Mysql半同步復制道理及成績排查。本站提示廣大學習愛好者:(Mysql半同步復制道理及成績排查)文章只能為提供參考,不一定能成為您想要的結果。以下是Mysql半同步復制道理及成績排查正文
mysql半同步復制和異步復制的差異如上述架構圖所示:在mysql異步復制的情形下,Mysql Master Server將本身的Binary Log經由過程復制線程傳輸入去今後,Mysql Master Sever就主動前往數據給客戶端,而不論slave上能否接收到了這個二進制日記。在半同步復制的架構下,當master在將本身binlog發給slave上的時刻,要確保slave曾經接收到了這個二進制日記今後,才會前往數據給客戶端。比較兩種架構:異步復制關於用戶來講,可以確保獲得疾速的呼應構造,然則不克不及確保二進制日記確切達到了slave上;半同步復制關於客戶的要求呼應略微慢點,然則他可以包管二進制日記的完全性。
1.成績配景
默許情形下,線上的mysql復制都是異步復制,是以在極端情形下,主備切換時,會有必定的幾率備庫比主庫數據少,是以切換後,我們會經由過程對象停止回滾回補,確保數據不喪失。半同步復制則請求主庫履行每個事務,都請求至多一個備庫勝利吸收後,才真正履行完成,是以可以堅持主備庫的強分歧性。為了確保主備庫數據強分歧,削減數據喪失,測驗考試在臨盆情況中開啟mysql的復制的半同步(semi-sync)特征。現實操作進程中,發明年夜部門實例半同步都可以正常運轉,但有少部門實例一直開不起來(只能以通俗復制方法運轉),更奇葩的是統一個主機的兩個實例,一個能開啟,一個不克不及。終究定位的成績也很簡略,但排查出來照樣花了一番工夫,下文將描寫全部成績的排查進程。
2.半同步復制道理
mysql的主備庫經由過程binlog日記堅持分歧,主庫當地履行完事務,binlog日記落盤後即前往給用戶;備庫經由過程拉取主庫binlog日記來同步主庫的操作。默許情形下,主庫與備庫並沒有嚴厲的同步,是以存在必定的幾率備庫與主庫的數據是纰謬等的。半同步特征的湧現,就是為了包管在任什麼時候刻主備數據分歧的成績。絕對於異步復制,半同步復制請求履行的每個事務,都請求至多有一個備庫勝利吸收後,才前往給用戶。完成道理也很簡略,主庫當地履行終了後,期待備庫的呼應新聞(包括最新備庫吸收到的binlog(file,pos)),吸收到備庫呼應新聞後,再前往給用戶,如許一個事務才算真正完成。在主庫實例上,有一個專門的線程(ack_receiver)吸收備庫的呼應新聞,並以告訴機制告訴主庫備庫曾經吸收的日記,可以持續履行。有關半同步的詳細完成,可以參考別的一篇文章,mysql半同步(semi-sync)源碼完成。
3.成績剖析
後面簡略引見了半同步復制的道理,如今來看看詳細成績。在主備庫翻開半同步開關後,成績實例的狀況變量"Rpl_semi_sync_master_status"一直是OFF,表現復制一向運轉在通俗復制的狀況。
(1).修正rpl_semi_sync_master_timeout參數。
半同步復制參數中有一個rpl_semi_sync_master_timeout參數,用以掌握主庫期待備庫呼應新聞的時光,假如跨越該值,則以為備庫一向沒有收到(備庫能夠掛了,也能夠備庫履行很慢,較主庫相差很遠),這個時刻復制會切換為通俗復制,防止主庫的履行事務長時光期待。線上這個值默許是50ms,簡略想是否是這個值太小了,遂將其改到10s,但成績仍然不解。
(2).打印日記
排盤問題最簡略最笨的辦法就是打日記,看看究竟是哪一個環節出了成績。主庫和備庫分離有rpl_semi_sync_master_trace_level和rpl_semi_sync_slave_trace_level參數來掌握半同步復制打印日記。將兩個參數值設置為80(64+16),記載具體日記信息,和進出的函數挪用。
master:
2016-01-04 18:00:30 13212 [Note] ReplSemiSyncMaster::updateSyncHeader: server(-1721062019), (mysql-bin.000006, 500717950) sync(1), repl(1)
2016-01-04 18:00:40 13212 [Warning] Timeout waiting for reply of binlog (file: mysql-bin.000006, pos: 500717950), semi-sync up to file , position 0.
2016-01-04 18:00:40 13212 [Note] Semi-sync replication switched OFF.
slave:
2016-01-04 18:00:30 38932 [Note] ---> ReplSemiSyncSlave::slaveReply enter
2016-01-04 18:00:30 38932 [Note] ReplSemiSyncSlave::slaveReply: reply (mysql-bin.000006, 500717950)
2016-01-04 18:00:30 38932 [Note] <--- ReplSemiSyncSlave::slaveReply exit (0)
從master日記可以看到在2016-01-04 18:00:30時,主庫設置了半同步標志,並開端期待備庫的呼應,期待10s後,依然沒有收到呼應,則以為超時,遂將半同步形式封閉,切換為通俗形式。但從slave日記來看,在2016-01-04 18:00:30曾經將(mysql-bin.000006, 500717950)發送給主庫,表現曾經收到該日記。這就解釋,master日記曾經打了semi-sync標,slave收到了日記,而且也回了包,master也確切等了10s,就是沒有收到包,所以就切換為通俗復制。如今成績就釀成了,為何master沒有收到?
(3)select函數
後面提到了,主庫實例上有一個專門吸收呼應包的線程(ack_receiver),它經由過程select函數監聽socket,發明有slave的呼應新聞後,讀撤消息,告訴任務線程可以持續履行。那末成績是否是湧現在select函數下面?由於select是一個體系挪用,一向沒有疑惑,但曾經跟到這裡來了,那就得看看。與select函數相干的有幾個主要的宏界說和解釋。重要完成在/usr/include/bits/typesizes.h,/usr/include/bits/select.h和/usr/include/sys/select.h這三個文件中。
FD_ZERO(fd_set *fdset):清空fdset與一切文件句柄的接洽。FD_SET(int fd, fd_set *fdset):樹立文件句柄fd與fdset的接洽。FD_CLR(int fd, fd_set *fdset):消除文件句柄fd與fdset的接洽。FD_ISSET(int fd, fd_set *fdset):檢討fdset接洽的文件句柄fd能否可讀寫,當>0表現可讀寫。
array { __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; 1024/64=16 (long int) }fd_set #define __FD_SET_SIZE 1024 typedef long int __fd_mask; //8個字節 #define __NFDBITS (8 * (int) sizeof (__fd_mask)) // 64位 #define __FDMASK(d) ((__fd_mask) 1 << ((d) % __NFDBITS)) //fd%64=N,則在第N位設置為1 #define __FDELT(d) ((d) / __NFDBITS) //表現在第幾個long int #define __FDS_BITS(set) ((set)->__fds_bits) #define __FD_SET(d, set) (__FDS_BITS (set)[__FDELT (d)] |= __FDMASK (d)) #define __FD_CLR(d, set) (__FDS_BITS (set)[__FDELT (d)] &= ~__FDMASK (d)) #define __FD_ISSET(d, set) \ ((__FDS_BITS (set)[__FDELT (d)] & __FDMASK (d)) != 0)
經由過程FD_SET可以設置我們想要監聽的句柄,句柄信息存儲在fd_set位數組中,數組元素的個數由__FD_SETSIZE/64決議,關於__FD_SETSIZE=1024而言,全部數組只要16個long int。每一個句柄占領一個位,就是1024個位,可以存儲1024個句柄。假定句柄值為138,那末138/64=2,138%64=10,那末這個句柄在數組的標示在第2個long int的第10地位1。那末假如句柄值超越1024呢,這裡不就溢出了?我細心撸了撸代碼,發明基本就沒有容錯斷定,假如句柄值跨越1024就必定會溢出。因為select函數是遍歷數組中的每一個位,然後去斷定該句柄能否可讀可寫,是以關於跨越1024的句柄,永久也不會去斷定,是以主庫永久不曉得備庫能否發送了呼應包。
(4)驗證
下面只是實際剖析,假如現實運轉的實例句柄確切是跨越了1024,那末成績就定位到了。
1.獲得mysql過程mysql-pid
ps –aux | grep mysqld | grep port
2.gdb attach到該過程
gdb –p mysql-pid
3.找到ack_receive線程,並切換
info thread
thread thread_id
4.打印socket的值,這裡fd值為2344。
(5)若何解
我們看到了因為__FD_SETSIZE的界說,普通是1024,招致select函數最多只能監聽1024個句柄,而且最年夜句柄值不跨越1024。第一個辦法是調年夜該參數,但這類辦法須要從新編譯linux內核。並且因為select機制,每次都須要遍歷 的每位來斷定句柄上能否有新聞到來,是以假如設置很年夜,將招致效力異常低。select是一種比擬老的IO復用機制,比擬先輩的poll,epoll都有相似的功效,而且更壯大,也沒有句柄總數和最年夜句柄的限制。有關select,poll,epoll等機制,年夜家可以去網上查材料,這裡不睜開評論辯論。
(6)官方版本
看了最新oracle官方版本git上5.7的源代碼,這塊也是用select來完成的,所以也存在相似的成績。固然,因為句柄號有復用機制,當實例上銜接數很少,或許長銜接不多時,不輕易湧現fd>1024的情形,所以這個bug不是很輕易湧現,但成績是廣泛存在的。
(7)成績延生
成績定位後,別的一個成績還困擾我了半天。由於mysql內核中有監聽的部門有3塊,1是監聽端口的select,2是線程池的監聽epoll,3是半同步的select監聽。slave binlog dump的線程就是通俗的任務線程,而任務線程的socket會受epoll的監聽,如許一來,binlog dump的socket會同時受半同步的select監聽和線程池的epoll監聽,這穩定了嗎?後來細心看了看代碼,才發明線程池的epoll監聽采取的是EPOLLONESHOT形式,每次吸收新聞後會解綁,須要從新注冊,是以不會湧現統一個句柄被兩種監聽機制同時監聽的情形。
到此,排盤問題進程就停止了,結論是比擬簡略的,但定位這個成績確切消費了一些工夫。因為select一種比擬通用的多路IO復用機制,是以有效到select函數的童鞋,能夠要留意下它的限制。