MySQL的詭異同步問題-重複執行一條relay-log

來源:互聯網
上載者:User

標籤:

MySQL的詭異同步問題

近期遇到一個詭異的MySQL同步問題,經過多方分析和定位後發現居然是由於備份引發的,非常的奇葩,特此記錄一下整個問題的分析和定位過程。

現象

同事擴容的一台slave死活追不上同步,具體的現象是SBM=0,但是Exec_Master_Log_Pos執行的位置和Read_Master_Log_Pos完全對不上,且伺服器本身CPU和IO都消耗的非常厲害。

 

——total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--usr sys idl wai hiq siq| read writ| recv send| in out | int csw1 0 99 0 0 0|8667B 862k| 0 0 | 0 0 | 536 8421 3 93 2 0 1| 0 102M| 112k 12k| 0 0 | 21k 50k1 3 94 2 0 1| 0 101M| 89k 5068B| 0 0 | 21k 49k1 3 93 2 0 1| 0 103M| 97k 5170B| 0 0 | 21k 50k1 4 93 2 0 1| 0 103M| 80k 15k| 0 0 | 21k 50k1 4 92 2 0 1| 0 102M| 112k 8408B| 0 0 | 21k 50k

 

 

分析

由於SBM並不能真正代表同步的狀態,所以最開始我們認為有一個比較大的event在執行過程中,所以導致SBM=0但是pos位置不對,但是在觀察一段之後發現,現象並沒有變化,並且Relay_Log_File始終在同一個檔案上。

為了找到這個現象的原因,我們使用strace來分析。由於伺服器的現象是有非常大的磁碟寫操作,故先使用iotop定位寫入量較大的thread id,然後通過strace -p來分析。

通過strace採集的資訊如下:

 

read(38, "376bin>303275T1721325322Bg000k0000040005.5.31-"..., 8192) = 150lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44read(38, "", 8042) = 0close(38) = 0lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44unlink("./relay-bin.rec") = -1 ENOENT (No such file or directory)open("./relay-bin.rec", ORDWR|OCREAT, 0660) = 38lseek(38, 0, SEEKCUR) = 0fsync(38) = 0lseek(35, 0, SEEKSET) = 0read(35, "./relay-bin.000914n./relay-bin.0"..., 8192) = 437lseek(35, 0, SEEKSET) = 0write(35, "./relay-bin.000914n./relay-bin.0"..., 437) = 437lseek(35, 437, SEEKSET) = 437read(35, "", 8192) = 0lseek(35, 0, SEEKEND) = 437fsync(35) = 0lseek(38, 0, SEEKSET) = 0close(38) = 0unlink("./relay-bin.rec") = 0lseek(35, 0, SEEKSET) = 0read(35, "./relay-bin.000914n./relay-bin.0"..., 437) = 437open("./relay-bin.000914", ORDONLY) = 38lseek(38, 0, SEEKCUR) = 0read(38, "376bin>303275T1721325322Bg000k0000040005.5.31-"..., 8192) = 150lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44read(38, "", 8042) = 0

 

 

從上面可以看出MySQLD在不斷的read和write353638這3個檔案。通過內容,我們大概可以看出這3個檔案應該是relay-bin.index、relay-bin.000914和relay-log.info這3個檔案。但是如何確定這3個檔案呢? 這時候就需要lsof了,通過lsof我們可以發現每個pid在proc下都會有一個fd的目錄,在這個目錄中就是各個檔案的連結,通過fd號就可以直接確定具體是那個檔案了

 

cd /proc/$pid/fdll -hlr-x------ 1 root root 64 Jan 31 12:44 0 -> /dev/nulll-wx------ 1 root root 64 Jan 31 12:44 1 -> /data1/mysql3361/error.loglrwx------ 1 root root 64 Jan 31 12:44 10 -> /data1/mysql3361/iblogfile1lrwx------ 1 root root 64 Jan 31 12:44 11 -> /data1/mysql3361/iblogfile2lr-x------ 1 root root 64 Jan 31 12:44 12 -> /data1/mysql3361/relay-bin.reclrwx------ 1 root root 64 Jan 31 12:44 13 -> /data1/mysql3361/ibF0ovt9 (deleted)lrwx------ 1 root root 64 Jan 31 12:44 14 -> socket:1042425539lrwx------ 1 root root 64 Jan 31 12:44 15 -> /data1/mysql3361/relay-bin.indexlrwx------ 1 root root 64 Jan 31 12:44 16 -> socket:1042425540lrwx------ 1 root root 64 Jan 31 12:44 17 -> /data1/mysql3361/sngdb/sngrankscore6.ibdlrwx------ 1 root root 64 Jan 31 12:44 18 -> /data1/mysql3361/mysql/host.MYIlrwx------ 1 root root 64 Jan 31 12:44 19 -> /data1/mysql3361/mysql/host.MYDl-wx------ 1 root root 64 Jan 31 12:44 2 -> /data1/mysql3361/error.loglrwx------ 1 root root 64 Jan 31 12:44 20 -> /data1/mysql3361/mysql/user.MYIlrwx------ 1 root root 64 Jan 31 12:44 21 -> /data1/mysql3361/mysql/user.MYD

 

 

定位

至此,我們已經非常確定是由於這3個檔案導致,那麼具體看一下這3個檔案有什麼異常?在挨個檢查後發現,relay-bin.index檔案非常異常,同樣的relay-bin在index中存在了兩行,這會不會就是問題的原因呢?

 

cat relay-bin.index./relay-bin.000914./relay-bin.000914./relay-bin.000915./relay-bin.000916cat relay-log.info./relay-bin.000914107mysql-bin.000724107

 

 

人肉去掉一行之後,發現一切變正常了,那麼看來問題的原因就在於relay-bin.index中存在著兩行同樣的記錄導致的了,但是為什麼會導致這種現象呢?

再分析

這時候純想已經沒有用了,只好從code中尋找答案,主要看slave.cc和log.cc。

具體來說就是MySQL在啟動slave的時候會從relay-log.info中讀取對應的filename和pos然後去execute relay log event,當執行完畢之後會進行刪除操作,mysql會使用reinit_io_cache重設relay-log.index檔案的檔案指標,之後再採用find_log_pos裡面的代碼mcmcmp從relay-log.index第一行確認所需位移,這時候就又回到了同樣的relay-log,於是死迴圈產生了。

 

int MYSQLBINLOGfindlogpos(LOGINFO linfo, const char logname,                bool needlock) (void) reinitiocache(&indexfile, READCACHE, (myofft) 0, 0, 0);  for (;;)     if (!logname    (lognamelen == fnamelen-1 && fullfnamelognamelen == ‘n‘ &&     !memcmp(fullfname, fulllogname, lognamelen)))         DBUGPRINT("info", ("Found log file entry"));      fullfnamefnamelen-1]= 0;           // remove last n      linfo->indexfilestartoffset= offset;      linfo->indexfileoffset = mybtell(&indexfile);      break;    }    }  }}

  

 

簡單來說:

  1. sql-thread讀取relay-log.info,開始從000914開始執行。
  2. 執行完畢之後,從index中讀取下一個relay-log,發現還是000914
  3. 於是將指標又定位到index檔案的第一行
  4. 然後死迴圈就產生了
  5. 至於之後的000915等檔案是由於io-thread產生的,和sql-thread無關,可以忽略

至此,出現SBM=0現象,並且產生很大的IO消耗的原因已經確定,就是由於relay-bin.index檔案中的重複行導致。但是為什麼會產生同樣的兩行資料呢?

分析產生原因

首先,這個絕對不可能是人閑的抽風手動vi的。

其次,手動執行flush logs的時候relay-log會自動增加一個。

再次,眾所周知,MySQl再啟動的時候會自動執行類似flush logs的操作,將relay-log自動向下建立一個。

結合上面的資訊,並在同事的不斷追查下,最終發現是由於我們的備份導致的。

第一、我們每天都會進行資料庫的冷備,使用rsync方式。

第二、我們每天會對日誌進行歸檔操作,定時執行flush logs。

當以上兩個操作遭遇到一起之後,就會發生在拷貝完relay-log和relay-bin.index之間執行了flush logs操作,這樣就導致relay-log和relay-bin.index中不等,也就導致了使用這個備份進行擴容都會產生index檔案中的重複行。

總結改進

終於,這麼奇葩,這麼小機率的一個問題終於找到根源,在此之前,真是想破頭也想不出來到底是怎麼回事。感謝 @zolker @大自在真人 @zhangyang8的 深入調研和追查,沒有鍥而不捨的精神,我們是不會追查到這種地步的。

然後就是改進了:

  1. 加強對備份的檔案一致性效驗,從根源上避免。
  2. 加強還原程式的相容性,由於index檔案對於擴容的slave並沒有實際意義,所以如果發現重複行可以直接置空index檔案。

MySQL的詭異同步問題-重複執行一條relay-log

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.