Java面試題沖刺第二十六天--實戰(zhàn)編程2
面試題1:當你發(fā)現(xiàn)一條SQL很慢,你的處理思路是什么?
- 發(fā)現(xiàn)Bug
- 確定Bug不是自己造成的,如果無法確定,不要理會步驟1
- 向組內(nèi)宣傳“程序里有一個未知Bug,錯不在我”
- 誰響應(yīng),誰對Bug負責
- 沒人響應(yīng),就要求特定人員配合調(diào)試
- 如果不配合,就是特定人員對Bug負責
- 如果特定人員配合,就相當于特定人員發(fā)現(xiàn)了一個Bug
- 讓特定人員看步驟1
- 完美,無懈可擊
我們是如何發(fā)現(xiàn)慢SQL的?除了慢查詢?nèi)罩竞腿藶榘l(fā)現(xiàn)之外,一般出現(xiàn)慢查詢時會有如下三個特征:
- 數(shù)據(jù)庫CPU負載高。一般是查詢語句中有很多計算邏輯,或并發(fā)處理線程較多,導致數(shù)據(jù)庫cpu負載。
- IO過高導致服務(wù)器卡住,這個一般和全表查詢沒索引有關(guān)系,問題出在處理的數(shù)據(jù)量太大。
- 查詢語句正常,索引正常但是還是慢。如果表面上索引都配置了,但是查詢慢,那得看看索引是否生效。
有些SQL雖然出現(xiàn)在慢查詢?nèi)罩局校幢厥瞧浔旧淼男阅軉栴},可能是因為鎖等待,服務(wù)器壓力高等等。
需要分析SQL語句真實的執(zhí)行計劃,而不是看重新執(zhí)行一遍SQL時,看是不是變快了(查詢緩存都不帶考慮的?)。。而是使用Explain工具來逐步調(diào)優(yōu),了解 MySQL 在執(zhí)行這條數(shù)據(jù)時的一些細節(jié),比如是否進行了優(yōu)化、是否使用了索引、索引選擇器是否正確選擇等等?;?Explain 的返回結(jié)果我們就可以根據(jù) MySQL 的執(zhí)行細節(jié)進一步優(yōu)化語法,使索引能被正確使用,實現(xiàn)性能需求。
關(guān)于索引的創(chuàng)建及優(yōu)化原則,美團點評技術(shù)團隊的幾點總結(jié),引用一下:
1.最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調(diào)整;
2.=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優(yōu)化器會幫你優(yōu)化成索引可以識別的形式,當然,好習慣要靠自己保持;
3.盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1,而一些狀態(tài)、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0,那可能有人會問,這個比例有什么經(jīng)驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄;
4.索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = '2014-05-29'就不能使用到索引,原因很簡單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進行檢索時,需要把所有元素都應(yīng)用函數(shù)才能比較,顯然成本太大。所以語句應(yīng)該寫成create_time = unix_timestamp('2014-05-29');
5.盡量的擴展索引,不要新建索引。比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可。
當然,我們知道還有單表數(shù)據(jù)量過大、大字段檢索、模糊匹配效率、時間維度統(tǒng)計效果差等MySQL硬性問題,俗稱硬傷,那我們就得基于實際情況來進行分庫分表、切換檢索引擎(如ES、FastDFS)等方式來處理了,術(shù)業(yè)有專攻,總不能在一棵樹上吊死對吧。
面試題2:怎么理解負載均衡的?你處理負載均衡都有哪些途徑?
負載均衡(Load Balance)是集群技術(shù)(Cluster)的一種應(yīng)用。負載均衡可以將工作任務(wù)分攤到多個處理單元,從而提高并發(fā)處理能力。下面是一組普通的web架構(gòu),我理解做負載均衡的切入點一般在web層和數(shù)據(jù)層。
目前最常見的負載均衡應(yīng)用是Web層負載均衡。根據(jù)實現(xiàn)的原理不同,常見的web負載均衡技術(shù)包括:DNS輪詢、IP負載均衡、CDN、反向代理以及http重定向等等。其中IP負載均衡可以使用硬件設(shè)備或軟件方式來實現(xiàn)。
對于數(shù)據(jù)層負載均衡,我們常用的分布式架構(gòu)、多節(jié)點集群、消息中間件(如Mycat)等來控制集群負載均衡狀況,同樣是橫向擴展,但側(cè)重點在于對集群的管理和災(zāi)備(高可用)方面。因此,實際大多負載均衡的工作還是在Web層。
高性能集群:將單個重負載的請求分散到多個節(jié)點進行處理,最后再將處理結(jié)果進行匯總;
高可用集群:提高冗余單元,避免單點故障;
負載均衡集群:將大量的并發(fā)請求分擔到多個處理節(jié)點。由于單個處理節(jié)點的故障不影響整個服務(wù),負載均衡集群同時也實現(xiàn)了高可用性。
Web層負載均衡
任何的負載均衡技術(shù)都要想辦法建立某種一對多的映射機制:一個請求的入口映射到多個處理請求的節(jié)點,從而實現(xiàn)分而治之(Divide and Conquer)。
1、【協(xié)議層】http重定向
當http代理(比如瀏覽器)向web服務(wù)器請求某個URL后,web服務(wù)器可以通過http響應(yīng)頭信息中的Location標記來返回一個新的URL。這意味著HTTP代理需要繼續(xù)請求這個新的URL,完成自動跳轉(zhuǎn)。
這種負載均衡方案的有點是比較簡單,缺點是瀏覽器需要兩次請求服務(wù)器才能完成一次訪問,性能較差;同時,重定向服務(wù)器本身的處理能力有可能成為瓶頸,整個集群的伸縮性規(guī)模有限;使用HTTP返回碼302重定向,有可能使搜索引擎判斷為SEO作弊,降低搜索排名。因此實踐中很少使用這種負載均衡方案來部署。
2、【協(xié)議層】DNS輪詢
DNS負責提供域名解析服務(wù),當訪問某個站點時,實際上首先需要通過該站點域名的DNS服務(wù)器來獲取域名指向的IP地址,在這一過程中,DNS服務(wù)器完成了域名到IP地址的映射,同樣,這樣映射也可以是一對多的,這時候,DNS服務(wù)器便充當了負載均衡調(diào)度器,它就像http重定向轉(zhuǎn)換策略一樣,將用戶的請求分散到多臺服務(wù)器上,但是它倆的實現(xiàn)機制完全不同。
相比http重定向,基于DNS的負載均衡完全節(jié)省了所謂的主站點,或者說DNS服務(wù)器已經(jīng)充當了主站點的職能。但不同的是,作為調(diào)度器,DNS服務(wù)器本身的性能幾乎不用擔心。因為DNS記錄可以被用戶瀏覽器或者互聯(lián)網(wǎng)接入服務(wù)商的各級DNS服務(wù)器緩存,只有當緩存過期后才會重新向域名的DNS服務(wù)器請求解析。也說是DNS不存在http的吞吐率限制,理論上可以無限增加實際服務(wù)器的數(shù)量。
優(yōu)勢:
1.可以根據(jù)用戶IP來進行智能解析。DNS服務(wù)器可以在所有可用的A記錄中尋找離用記最近的一臺服務(wù)器。
2.動態(tài)DNS:在每次IP地址變更時,及時更新DNS服務(wù)器。當然,因為緩存,一定的延遲不可避免。
不足:
1.沒有用戶能直接看到DNS解析到了哪一臺實際服務(wù)器,對運維人員的調(diào)試帶來了不便。
2.策略的局限性。例如你無法將HTTP請求的上下文引入到調(diào)度策略中,而在前面介紹的基于HTTP重定向的負載均衡系統(tǒng)中,調(diào)度器工作在HTTP層面,它可以充分理解HTTP請求后根據(jù)站點的應(yīng)用邏輯來設(shè)計調(diào)度策略,比如根據(jù)請求不同的URL來進行合理的過濾和轉(zhuǎn)移。
3、【協(xié)議層】CDN
CDN(Content Delivery Network,內(nèi)容分發(fā)網(wǎng)絡(luò))。通過發(fā)布機制將內(nèi)容同步到大量的緩存節(jié)點,并在DNS服務(wù)器上進行擴展,找到里用戶最近的緩存節(jié)點作為服務(wù)提供節(jié)點。
因為很難自建大量的緩存節(jié)點,所以通常使用CDN運營商的服務(wù)。目前國內(nèi)的服務(wù)商很少,而且按流量計費,價格昂貴。
4、【協(xié)議層】反向代理負載均衡
在客戶端明確的前提下,大量訪問請求(QPS)涌入。我們后臺通過Nginx代理了20個服務(wù)器,高QPS打進來后先打到Nginx中,通過Nginx的負載均衡來把請求分發(fā)給這20臺服務(wù)器,減輕了單臺服務(wù)器負擔。這種客戶端 → Nginx → 服務(wù)器 的模式稱為反向代理,如下圖:
N個客戶端給服務(wù)器發(fā)送的請求,Nginx服務(wù)器接收到之后,按照一定的規(guī)則均衡分發(fā)給了后端的業(yè)務(wù)處理服務(wù)器進行處理了。此時,請求的客戶端是明確的,但是請求具體由哪臺服務(wù)器處理的并不明確了,Nginx扮演的就是一個反向代理角色。
1.客戶端是無感知代理的存在的,反向代理對外都是透明的,訪問者并不知道自己訪問的是一個代理。因為客戶端不需要任何配置就可以訪問。
2.反向代理,它代理的是服務(wù)端,代服務(wù)端接收請求,主要用于服務(wù)器集群分布式部署的情況下,反向代理隱藏了服務(wù)器的信息。
反向代理的作用:
(1)保證內(nèi)網(wǎng)的安全,通常將反向代理服務(wù)器配置為公網(wǎng)訪問地址,代理的Web服務(wù)器是內(nèi)網(wǎng)IP。
(2)負載均衡,通過反向代理服務(wù)器來優(yōu)化每個單機服務(wù)實例的負載。
5、【網(wǎng)絡(luò)層】IP負載均衡
1.客戶端會向一個ip地址發(fā)出請求,這個ip地址是一個VIP(虛擬IP),這也是調(diào)度器向外公布的一個地址。
2.請求達到調(diào)度器,調(diào)度器會根據(jù)負載均衡算法從RealServer列表中選取一個負載不高的服務(wù)器,然后把請求報文的目標地址,也就是VIP和端口通過iptables進行NAT轉(zhuǎn)換成選中的服務(wù)器的真實ip地址。最后,調(diào)度器會把其連接保存在一個hash表中,只要這個連接下次再發(fā)請求報文過來就會把其分發(fā)到上次選定的服務(wù)器中。
3.RealServer收到報文之后,會把響應(yīng)返回給調(diào)度器。
4.調(diào)度器收到報文之后,會把源地址和源端口改為虛擬ip和端口,最后再返回給客戶端。
特點
- RealServer和調(diào)度器必須位于一個ip網(wǎng)絡(luò)之中。
- 調(diào)度器位于RealServer和客戶端之間,處理進出的通信。
- RIP通常是內(nèi)部地址,僅用于集群之間通信。
- RealServer的網(wǎng)關(guān)必須指向調(diào)度器。
- 支持端口映射,RealServer沒必要跟調(diào)度器一個端口。
限制
響應(yīng)報文一般比較大,每一次都需要NAT轉(zhuǎn)換的話,大流量的時候,會導致調(diào)度器成為一個瓶頸。
面試題3:你平時是怎樣定位線上問題的?
本題回答摘自朱曄的《Java業(yè)務(wù)開發(fā)常見錯誤100例》
定位問題,首先要定位問題出在哪個層次上。比如,是 Java 應(yīng)用程序自身的問題還是外部因素導致的問題。我們可以先查看程序是否有異常,異常信息一般比較具體,可以馬上定位到大概的問題方向;如果是一些資源消耗型的問題可能不會有異常,我們可以通過指標監(jiān)控配合顯性問題點來定位。
一般情況下,程序的問題來自以下三個方面。
第一,程序發(fā)布后的 Bug,回滾后可以立即解決。這類問題的排查,可以回滾后再慢慢分析版本差異。
第二,外部因素,比如主機、中間件或數(shù)據(jù)庫的問題。這類問題的排查方式,按照主機層面的問題、中間件或存儲(統(tǒng)稱組件)的問題分為兩類。
主機層面的問題,可以使用工具排查:
CPU 相關(guān)問題
:可以使用 top、vmstat、pidstat、ps 等工具排查;內(nèi)存相關(guān)問題
:可以使用 free、top、ps、vmstat、cachestat、sar 等工具排查;IO 相關(guān)問題
:可以使用 lsof、iostat、pidstat、sar、iotop、df、du 等工具排查;網(wǎng)絡(luò)相關(guān)問題
:可以使用 ifconfig、ip、nslookup、dig、ping、tcpdump、iptables 等工具排查。
組件的問題,可以從以下幾個方面排查:
- 排查組件所在主機是否有問題;
- 排查組件進程基本情況,觀察各種監(jiān)控指標;
- 查看組件的日志輸出,特別是錯誤日志;
- 進入組件控制臺,使用一些命令查看其運作情況。
第三,因為系統(tǒng)資源不夠造成系統(tǒng)假死的問題,通常需要先通過重啟和擴容解決問題,之后再進行分析,不過最好能留一個節(jié)點作為現(xiàn)場。系統(tǒng)資源不夠,一般體現(xiàn)在 CPU 使用高、內(nèi)存泄漏或 OOM 的問題、IO 問題、網(wǎng)絡(luò)相關(guān)問題這四個方面。
對于 CPU 使用高的問題,如果現(xiàn)場還在,具體的分析流程是:
- 首先,在 Linux 服務(wù)器上運行top -Hp pid命令,來查看進程中哪個線程 CPU 使用高;
- 然后,輸入大寫的 P 將線程按照 CPU 使用率排序,并把明顯占用 CPU 的線程 ID 轉(zhuǎn)換為 16 進制;
- 最后,在 jstack 命令輸出的線程棧中搜索這個線程 ID,定位出問題的線程當時的調(diào)用棧。
如果沒有條件直接在服務(wù)器上運行 top 命令的話,我們可以用采樣的方式定位問題:間隔固定秒數(shù)(比如 10 秒)運行一次 jstack 命令,采樣幾次后,對比采樣得出哪些線程始終處于運行狀態(tài),分析出問題的線程。
如果現(xiàn)場沒有了,我們可以通過排除法來分析。CPU 使用高,一般是由下面的因素引起的:
- 突發(fā)壓力。這類問題,我們可以通過應(yīng)用之前的負載均衡的流量或日志量來確認,諸如 Nginx 等反向代理都會記錄 URL,可以依靠代理的 Access Log 進行細化定位,也可以通過監(jiān)控觀察 JVM 線程數(shù)的情況。壓力問題導致 CPU 使用高的情況下,如果程序的各資源使用沒有明顯不正常,之后可以通過壓測 +Profiler(jvisualvm 就有這個功能)進一步定位熱點方法;如果資源使用不正常,比如產(chǎn)生了幾千個線程,就需要考慮調(diào)參。
- GC。這種情況,我們可以通過 JVM 監(jiān)控 GC 相關(guān)指標、GC Log 進行確認。如果確認是 GC 的壓力,那么內(nèi)存使用也很可能會不正常,需要按照內(nèi)存問題分析流程做進一步分析。
- 程序中死循環(huán)邏輯或不正常的處理流程。這類問題,我們可以結(jié)合應(yīng)用日志分析。一般情況下,應(yīng)用執(zhí)行過程中都會產(chǎn)生一些日志,可以重點關(guān)注日志量異常部分。
對于內(nèi)存泄露或 OOM 的問題,最簡單的分析方式,就是堆轉(zhuǎn)儲后使用MAT分析。堆轉(zhuǎn)儲,包含了堆現(xiàn)場全貌和線程棧信息,一般觀察支配樹圖、直方圖就可以馬上看到占用大量內(nèi)存的對象,可以快速定位到內(nèi)存相關(guān)問題。
需要注意的是,Java 進程對內(nèi)存的使用不僅僅是堆區(qū),還包括線程使用的內(nèi)存(線程個數(shù) * 每一個線程的線程棧)和元數(shù)據(jù)區(qū)。每一個內(nèi)存區(qū)都可能產(chǎn)生 OOM,可以結(jié)合監(jiān)控觀察線程數(shù)、已加載類數(shù)量等指標分析。另外,我們需要注意看一下,JVM 參數(shù)的設(shè)置是否有明顯不合理的地方,限制了資源使用。
問題來了,JVM 哪塊內(nèi)存區(qū)域不會發(fā)生內(nèi)存溢出?
程序計數(shù)器:程序計數(shù)器是一塊內(nèi)存較小的區(qū)域,它用于存儲線程的每個執(zhí)行指令,每個線程都有自己的程序計數(shù)器,此區(qū)域不會有內(nèi)存溢出的情況。
IO 相關(guān)的問題,除非是代碼問題引起的資源不釋放等問題,否則通常都不是由 Java 進程內(nèi)部因素引發(fā)的。網(wǎng)絡(luò)相關(guān)的問題,一般也是由外部因素引起的。
對于連通性問題,結(jié)合異常信息通常比較容易定位;對于性能或瞬斷問題,可以先嘗試使用 ping 等工具簡單判斷,如果不行再使用 tcpdump 或 Wireshark 來分析。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
多數(shù)據(jù)源模式JPA整合sharding-jdbc實現(xiàn)數(shù)據(jù)脫敏
這篇文章主要為大家介紹了JPA項目中多數(shù)據(jù)源模式整合sharding-jdbc來實現(xiàn)數(shù)據(jù)脫敏,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02

使用@Transactional 設(shè)置嵌套事務(wù)不回滾