之前寫過一篇博文,《不好的MySQL過程編寫習慣》(http://www.cnblogs.com/wingsless/p/5041838.html)。這篇博文裡強調了不要循環的提交事務,盡量將可以放在一起的SQL同一個事務提交,會快很多很多。博文中提到了redo的問題,因此,結合最近編寫新員工培訓材料的感悟,簡單的介紹一些InnoDB和Redo的事情。
InnoDB的內存中有redo log buffer,磁盤上還有redo log file,redo用於在宕機之後恢復數據,保證數據的持久性。
一般來講,最符合ACID的redo工作方式應該是這樣的:事務提交時,內存中的redo buffer內容寫入文件中,並刷回磁盤(flush,官方文檔中解釋該動作是將磁盤緩存的數據flush回文件中)。此時buffer pool中的數據塊被修改成為髒頁,但是並不寫回磁盤中,而是在master thread的循環中慢慢寫回去,這樣實際上日志的寫入和數據文件的寫入不是同步進行的,兩者之間會有一定的時間差,這種方式就叫做預寫日志(WAL)。一旦系統宕機,內存中的數據立刻丟失,下次啟動數據庫時,沒有來得及寫回數據文件的數據可以從redo log中恢復。
因此在上篇博文中的過程,會在每一個事務提交時寫一次日志,這樣會帶來很多的磁盤IO,因此效率非常低下,而單個事務提交只會造成一次IO,所以效率提升非常顯著。但是,InnoDB還支持另外的一種模式,這個模式由innodb_flush_log_at_trx_commit參數控制,默認情況下是1,代表上面說的那種方式,每次事務提交都會寫redo日志。
在一次試驗中,我仍然保持存儲過程循環提交事務,但是我將參數調整到了2,這樣的效率提升也很明顯,稍微慢於單事務提交。參數為2的意義是,每次提交事務的時候,也會寫日志文件,但是並不調用fsync函數(刷盤的函數)將日志刷回磁盤,而是一秒一次的調度fsync函數。因此也會帶來不少的效率提升。這樣做的問題很明顯,如果遇到宕機,會丟失一秒左右的數據。
當然這個參數還能調整成0,代表事務提交時不作任何操作,每隔一秒才會將redo buffer的數據寫入日志並刷回磁盤中。這種方式看起來就明顯快的多了,但是卻是最不符合ACID原則的做法。
當然了,刷磁盤這個事情還會牽扯一些別的參數,那就不在本文的討論范圍之內了,未來如果有學習心得我會寫下來的。
實際上,如果不在乎一秒的數據丟失(一秒的數據有時候真的很多很多),可以將該參數選擇為2,但是最好還是選擇為1。程序在寫數據庫的時候,可以采用批量提交的方式,速度非常快。這是程序設計的問題了,也不在討論范圍內了。