這篇文章主要介紹“為什么主從切換不成功”,在日常操作中,相信很多人在為什么主從切換不成功問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”為什么主從切換不成功”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
成都創(chuàng)新互聯(lián)公司是專(zhuān)業(yè)的富蘊(yùn)網(wǎng)站建設(shè)公司,富蘊(yùn)接單;提供網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專(zhuān)業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行富蘊(yùn)網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專(zhuān)業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專(zhuān)業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
最近線(xiàn)上進(jìn)行主從切換,大部分應(yīng)用都切過(guò)去了,但是某些應(yīng)用的連接確還在老的主(新的從)上面。
這讓對(duì)應(yīng)應(yīng)用的開(kāi)發(fā)百思不得其解,于是求助了筆者一探究竟。
應(yīng)用開(kāi)發(fā)收到Cat監(jiān)控告警,發(fā)現(xiàn)這個(gè)應(yīng)用(A)中的請(qǐng)求在好幾臺(tái)機(jī)器中一直穩(wěn)定失敗。聯(lián)想到昨晚剛做過(guò)數(shù)據(jù)庫(kù)主從切換演練,于是上機(jī)器netstat -anp下,發(fā)現(xiàn)機(jī)器一直連的是舊的從庫(kù)!
netstat -anp | grep 1521 tcp 0 0 1.2.3.4:54100 1.1.1.1:1521 ESTABLISHED
開(kāi)發(fā)感覺(jué)肯定是主從沒(méi)有切換過(guò)去導(dǎo)致請(qǐng)求失敗。乍一看,好像非常有道理的樣子。
神馬情況?距離切換成功已經(jīng)8個(gè)小時(shí)了,為什么連接還連在上面呢?于是筆者ping了下對(duì)應(yīng)數(shù)據(jù)庫(kù)的域名:
ping db.prd 64byres from db.prd (2.2.2.2): icmp_seq=1 ttl=64 time=0.02ms
好奇怪,DNS已經(jīng)切換過(guò)去了。應(yīng)用怎么還連到老庫(kù)呢?
最先想到的是主從切換到DNS反應(yīng)過(guò)來(lái)有延遲。例如主從切換完,DNS在2min后才能生效,所以在此期間新建的連接還是到從庫(kù)。
這種情況很正常,對(duì)于這種情況需要DBA將舊主的連接全都?xì)⒌艏纯?。咨?xún)了下DBA,他們反饋他們已經(jīng)把連接全部殺掉了。而且當(dāng)場(chǎng)給我看了下數(shù)據(jù)庫(kù)的統(tǒng)計(jì)連接SQL,確實(shí)沒(méi)有對(duì)應(yīng)機(jī)器的連接。這就奇怪了,應(yīng)用機(jī)器上的連接是ESTABLISHED狀態(tài)??!
這時(shí)候,開(kāi)發(fā)向筆者反應(yīng),這個(gè)應(yīng)用對(duì)應(yīng)的大部分機(jī)器都是連的老庫(kù)!如果是DNS延遲,不可能這么巧吧,40多臺(tái)呢!
而且這些機(jī)器的DNS都是指向新庫(kù)的。
難道是DBA漏了kill連接的步驟?但是和他和我展示的DB統(tǒng)計(jì)信息矛盾啊。于是筆者讓DBA在對(duì)應(yīng)老庫(kù)的機(jī)器上netstat了一把。發(fā)現(xiàn),連接還真的存在!
netstat -anp | grep 1.2.3.4 tcp 0 0 1.1.1.1:1521 1.2.3.4:54100 ESTABLISHED
難道統(tǒng)計(jì)信息真的有問(wèn)題?
為了驗(yàn)證筆者對(duì)于DNS延遲的猜想,就通過(guò)一些技巧來(lái)獲取這個(gè)連接的創(chuàng)建時(shí)間。首先 netstat -anp | grep 1.2.3.4找出來(lái)這個(gè)連接。由于明顯是屬于應(yīng)用java進(jìn)程的,所以 直接找到進(jìn)程pid:8299
netstat -anp | grep 1521 tcp 0 0 1.2.3.4:54100 1.1.1.1:1521 ESTABLISHED netstat -anp | grep java abc 8299 java
既然有了進(jìn)程pid,我們直接cat /proc/8299/net/tcp,直接獲取到其所有的連接信息,然后在其中g(shù)rep 1521的16進(jìn)制05F1(當(dāng)前機(jī)器上1521的連接只有一個(gè))
...... local_address rem_address inode ...... ...... xxx:D345 xxx:05F1 23456789 ......
找到這個(gè)socket(1.2.3.4:54100<->1.1.1.1:1521)對(duì)應(yīng)的inode號(hào)。 有了這個(gè)inode號(hào)就簡(jiǎn)單了,我們直接
ls -all -h /proc/8299/fd | grep 23456789 (inode號(hào)) ...... Jan 29 17:43 222 -> socket:[23456789]
這么一看,這個(gè)連接是1月29日創(chuàng)建的。但是主從切換的時(shí)間點(diǎn)確是3月19日, 這個(gè)連接已經(jīng)建了2個(gè)月了!那么就不可能是筆者所說(shuō)的DNS失效問(wèn)題了。因?yàn)檫B接就沒(méi)有重連過(guò)。
看到這個(gè)連接創(chuàng)建時(shí)間,筆者第一反應(yīng),DBA確定殺連接了嗎?問(wèn)了下DBA有沒(méi)有可能是統(tǒng)計(jì)問(wèn)題。DBA聽(tīng)了后,告訴筆者,他們都重啟過(guò)數(shù)據(jù)庫(kù)了,怎么可能還有連接存在呢?看了下DB進(jìn)程的創(chuàng)建時(shí)間。
ps -eo lstart,cmd | grep db進(jìn)程名 Mar 19 17:52:32 2021 db進(jìn)程名
從進(jìn)程啟動(dòng)時(shí)間來(lái)看,真的是在3月19日啟動(dòng)的。而這個(gè)詭異的連接還確實(shí)屬于這個(gè)3月19日啟動(dòng)的進(jìn)程。這個(gè)怎么看邏輯上都不通啊。
但是,既然linux的統(tǒng)計(jì)信息在這(還是要先暫時(shí)認(rèn)為是靠譜的),那肯定是又有什么其它的詭異邏輯在里面了。
稍微思考了一會(huì),筆者就找到了一種可能。父進(jìn)程先新建了連接進(jìn)行處理,在創(chuàng)建子進(jìn)程fork的時(shí)候,子進(jìn)程會(huì)繼承父進(jìn)程的連接,這時(shí)候父進(jìn)程退出,只保留子進(jìn)程的話(huà)。就會(huì)出現(xiàn)連接在進(jìn)程啟動(dòng)之前就已經(jīng)存在的詭異現(xiàn)象。
為了驗(yàn)證這個(gè)問(wèn)題,筆者自己寫(xiě)了段簡(jiǎn)單的C程序,執(zhí)行了一下確實(shí)如此。代碼例子為:
main.c ...... int main(int argc,char* argv[]){ ...... if((client_fd = accept(sockfd,(struct sockaddr*)&remote_addr,&sin_size)) == -1){ printf("accept error!\n"); } printf("Received a connection \n"); // 制造兩分鐘延遲,以造成上面的現(xiàn)象 sleep(2 * 60); if(!fork()){ // 子進(jìn)程保持 while(1){ sleep(100000); } }else{ // 父進(jìn)程關(guān)閉連接 close(client_fd); } return 0; }
問(wèn)了下DBA,他們不會(huì)kill -9所有進(jìn)程,都是按照標(biāo)準(zhǔn)的數(shù)據(jù)庫(kù)重啟流程來(lái)操作的(kill -9所有進(jìn)程的同時(shí)會(huì)關(guān)閉這些進(jìn)程所擁有的連接,但這么暴力的操作明顯不敢用在DB上)。
如果我們使用的商業(yè)數(shù)據(jù)庫(kù)用了上圖的機(jī)制,那就會(huì)造成前面的現(xiàn)象。但是由于DB本身保持的session都已經(jīng)沒(méi)了,那么這個(gè)連接在數(shù)據(jù)庫(kù)維度肯定是已經(jīng)gg了(這也是數(shù)據(jù)庫(kù)統(tǒng)計(jì)不出來(lái)的原因)。既然還保留在上面,這個(gè)連接肯定再也沒(méi)有處理過(guò)請(qǐng)求!不然肯定出錯(cuò)了。
如果按照上面的論斷的話(huà),那么沒(méi)有執(zhí)行過(guò)請(qǐng)求,也就不會(huì)有報(bào)錯(cuò)嘍?如果按照這個(gè)邏輯的話(huà),那豈不是只有出現(xiàn)業(yè)務(wù)報(bào)錯(cuò)的才會(huì)有新的正常連接。筆者去報(bào)錯(cuò)的機(jī)器看了下,既然報(bào)錯(cuò)了,那肯定是執(zhí)行過(guò)SQL了,然后觸發(fā)Druid丟棄連接再新建連接。
果然,一直報(bào)錯(cuò)的機(jī)器上連接都連到新庫(kù)了(但應(yīng)用開(kāi)發(fā)發(fā)現(xiàn)其它機(jī)器還是連到老庫(kù),所以找到了我求助),而且創(chuàng)建時(shí)間是3月29日,而不報(bào)錯(cuò)的應(yīng)用的連接掛在老庫(kù)上面,挑了幾臺(tái)看一下,這些掛在老庫(kù)的連接依舊是1月29日創(chuàng)建的。
既然連接都正常了(到新庫(kù)了),為何還在報(bào)錯(cuò)呢?難道說(shuō)業(yè)務(wù)代碼寫(xiě)的有問(wèn)題,一旦報(bào)錯(cuò),就永遠(yuǎn)錯(cuò)下去?于是筆者直接翻起了應(yīng)用的源碼。其使用這個(gè)數(shù)據(jù)庫(kù)的連接用來(lái)獲取(sequence)序列號(hào)。然后細(xì)細(xì)分析了源碼后發(fā)現(xiàn)。其在數(shù)據(jù)庫(kù)報(bào)錯(cuò)之后沒(méi)有處理好,走了一個(gè)有問(wèn)題的代碼分支,導(dǎo)致永遠(yuǎn)不會(huì)再?gòu)臄?shù)據(jù)庫(kù)獲取sequence(業(yè)務(wù)代碼就不放上來(lái)了)。
因?yàn)檫@個(gè)序列號(hào)是取一段很大的范圍到機(jī)器的內(nèi)存中使用的,不耗盡之前不會(huì)執(zhí)行SQL。所以只有一些內(nèi)存中序列耗盡的機(jī)器才會(huì)運(yùn)行到那一段有問(wèn)題的代碼分支。
到這里大家可能會(huì)疑問(wèn)?沒(méi)有心跳檢測(cè)么?確實(shí)沒(méi)有,應(yīng)用采用的是Druid數(shù)據(jù)源,而他們使用的那個(gè)版本的Druid是沒(méi)有定時(shí)心跳檢測(cè)的。
主從切換當(dāng)然是成功的。這從其它的應(yīng)用切過(guò)去之后運(yùn)行良好可以判斷出來(lái)。主從切換當(dāng)中的數(shù)據(jù)庫(kù)流量損失是我們可預(yù)期的正?,F(xiàn)象。但是,數(shù)據(jù)庫(kù)切換完之后,應(yīng)用確恢復(fù)不回來(lái),那就要仔細(xì)看看應(yīng)用代碼本身有什么問(wèn)題了。
到此,關(guān)于“為什么主從切換不成功”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!