案例描述:

  一個普通的事務(wù)提交,在應(yīng)用里面會提示commit超時,失敗。

一、理論知識

1、關(guān)于commit原理,事務(wù)提交過程

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團訓(xùn)

  1、尋找修改的數(shù)據(jù)頁:

    1、如果該數(shù)據(jù)頁在內(nèi)存中,則直接是內(nèi)存讀;

    2、如果該數(shù)據(jù)頁內(nèi)存中沒有,物理讀,就從磁盤調(diào)入內(nèi)存;

  2、磁盤中的undo頁調(diào)入內(nèi)存;

  3、先將原來的數(shù)據(jù)存入undo,然后修改數(shù)據(jù)(數(shù)據(jù)頁成臟頁);

  4、修改數(shù)據(jù)的信息生成redo數(shù)據(jù)存入log_buffer(內(nèi)存buffer_pool的一個空間,默認16M)中;

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團訓(xùn)

mysql> show variables like '%log_buffer%';+------------------------+----------+| Variable_name          | Value    |+------------------------+----------+| innodb_log_buffer_size | 16777216 |+------------------------+----------+1 row in set (0.01 sec)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團訓(xùn)

  5、log_buffer通過log線程(后臺線程,非常勤快),持續(xù)不斷的將redo信息寫入disk的innodb_log_file中;

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團訓(xùn)

mysql> show variables like 'innodb_log_file%';+---------------------------+----------+| Variable_name             | Value    |+---------------------------+----------+| innodb_log_file_size      | 50331648 || innodb_log_files_in_group | 2        |+---------------------------+----------+2 rows in set (0.01 sec)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團訓(xùn)

  6、事務(wù)提交,刻意觸發(fā)log線程,將剩余的log_buffer中的redo數(shù)據(jù)信息寫入磁盤中,數(shù)據(jù)量已剩不多,寫完提交成功。

注意:

  1、修改記錄前,一定要先寫日志;

    “日志先行”,這是數(shù)據(jù)庫最基本的原則。

  2、事務(wù)提交過程中,一定要保證日志先落盤,才能算事務(wù)提交完成。

  3、意外掉電,內(nèi)存臟頁丟失,但是磁盤的innodb_log_file中存放了redo日志信息,待重啟服務(wù)器,MySQL通過讀取磁盤的log_files數(shù)據(jù),自動將數(shù)據(jù)的修改重新跑一邊。

Q:為什么mysql commit速度總是很快,盡管事務(wù)修改的數(shù)據(jù)量可能很大?

A:

  因為事務(wù)提交,并不是對磁盤數(shù)據(jù)進行修改,而是將修改數(shù)據(jù)的redo信息通過后臺log線程寫入磁盤的redo logfile中,完成mysql commit,無論事務(wù)修改的數(shù)據(jù)量有多大,這個過程速度是很快的。

  而內(nèi)存中的臟塊,也就是修改后的數(shù)據(jù)頁,正常情況下是由后臺相關(guān)write線程周期性的將臟頁數(shù)據(jù)刷入磁盤中,保證innodb buffer pool有足夠的干凈塊、可用塊。

2、關(guān)于rollback原理,回滾過程

  1、MySQL讀取內(nèi)存中undo頁信息

  2、通過undo信息找到臟頁,反著對數(shù)據(jù)進行修改

  3、do、undo的時間相同,且都會產(chǎn)成redo信息

  4、事務(wù)提交

MySQL回滾處理機制:

  如果線程中斷,事務(wù)沒有提交,undo會將記錄此信息,待另一會話進程連上,查看該塊數(shù)據(jù)信息,MySQL自動回滾進行數(shù)據(jù)頁修改,然后被讀取。也就是說為了避免系統(tǒng)因為rollback被hang住,通過直接殺死進程的方式,中斷事務(wù),等待后來者要讀取該數(shù)據(jù)信息時進行回滾,再返回結(jié)果。

Q:rollback為什么有時候很慢,rollback的風(fēng)險和風(fēng)險避免方式?

A:

  rollback的時間取決于回滾前事務(wù)修改數(shù)據(jù)的時間,處理量大回滾時間長,處理量小回滾時間短。

  1、rollback風(fēng)險:容易導(dǎo)致系統(tǒng)被hang??;

  2、風(fēng)險避免方式:直接殺死會話進程或是mysql進程。

3、存儲寫入性能分析

Q:mysql commit,存儲為什么寫速度能夠保持在0ms,極少出現(xiàn)1ms情況?

A:

  對于存儲來說,寫性能相當(dāng)高:假設(shè)存儲cache總有空閑空間的情況下,事務(wù)提交,將log buffer中剩余的很少的redo數(shù)據(jù)寫入存儲cache,即為完成mysql commit,這個過程是相當(dāng)快的(能夠保持在0ms,極少出現(xiàn)1ms情況),后續(xù)redo數(shù)據(jù)由cache寫入磁盤的過程是后臺進行。

4、存儲級別的災(zāi)備(同城災(zāi)備)

  1、災(zāi)備同步過程:commit

    1、redo、binlog寫入本地存儲cache;

    2、通過網(wǎng)絡(luò)同步binlog寫入遠端同步的服務(wù)器的存儲cache中;

    3、響應(yīng)本地數(shù)據(jù)庫;

    4、事務(wù)提交成功;

  2、風(fēng)險:

    網(wǎng)絡(luò)出現(xiàn)問題(信號斷續(xù),纜線斷了),導(dǎo)致寫hang住,commit超時失敗。

  3、解決:

    通過超時設(shè)置,網(wǎng)絡(luò)中斷超過限制,自動將同步改為災(zāi)備異步,盡可能少的影響業(yè)務(wù)commit超時失敗。

 

二、分析與處理

  存儲寫性能比較差,很多時段會達到5ms,甚至于10ms以上

    備注:災(zāi)備同步已經(jīng)停止的情況下。

1、存儲中BBU問題,出現(xiàn)監(jiān)控BBU的bug;

  解決:重啟BBU,不行就更新BBU。

2、cache被占滿

  1、海量數(shù)據(jù)寫入,commit數(shù)據(jù)占滿cache;

  2、硬盤I/O異常,異常SQL導(dǎo)致的海量物理讀;

  解決:索引優(yōu)化。

3、存儲性能差

  解決:找老板掏錢,更換優(yōu)質(zhì)設(shè)備。

@author:http://www.cnblogs.com/geaozhang/

http://www.cnblogs.com/geaozhang/p/7172034.html