深入理解MySQL innodb日誌管理
innodb日誌管理機制:
1、innodb儲存引擎是支援事務ACID特性的,這個理論基本就是一個關係型資料庫相關的資料恢復原形設計,包括日誌、回滾、redo、併發控制、buffer pool等管理方面,內容非常全面;
2、innodb的buffer pool主要用來儲存訪問過的資料頁面,他就是一塊連續的記憶體,通過一定的演算法可以使這塊記憶體得到有效的管理,它是資料庫系統中擁有最大塊記憶體的系統模組。
innodb儲存引擎中資料的訪問是按照頁(也可以叫塊,預設為16KB)的方式從資料庫檔案讀取到buffer pool中的,然後在記憶體中用同樣大小的記憶體空間來做一個對映;未來提高資料訪問效率,資料庫系統預先就分配了很多這樣的空間,用來與檔案中的資料進行交換;buffer pool的大小可以在配置檔案中配置,有引數innodb_buffer_pool_size的大小來決定,預設大小為128MB。在SQL/">MySQL5.7.4以前,一旦MySQL啟動這個值便不能再做修改,如果要修改只能退出MySQL程序,然後修改對應的配置檔案來設定新的buffer pool大小,重啟才能生效。
注意:在MySQL5.7.5之後,可以在MySQL程序執行的情況下,動態調整innodb_buffer_pool_size,需要強調的是,如果buffer pool的大小超過了1GB,應該通過調整innodb_buffer_pool_instances=N,把它分成若干個instance的做法,來提示MySQL處理請求的併發能力,因為buffer pool是通過連結串列的方式來管理頁面的,同時為了保護頁面,需要在存取的時候對連結串列加鎖,在多執行緒的情況下,併發去讀寫buffer pool裡面快取的頁面需要鎖的競爭和等待。所以修改為多個instance,每個instance各自管理自己的記憶體和連結串列,可以提升效率。
3、buffer pool實現原理:
buffer pool可以有多個例項,可以通過配置檔案中的引數innodb_buffer_pool_instance來設定,預設值為1,實現多個例項的buffer pool主要是為了提高資料頁訪問時的併發度。每個例項的空間大小都是相同的,也就是說系統會將整個配置的buffer pool大小按例項個數平分,然後每個例項各自進行初始化操作;
--注意:在運維過程中,看到狀態引數innodb_buffer_pool_bytes_data總是比innodb_buffer_pool_size小,就是因為控制頭資訊佔用了部分空間。實際的分配方式是,buffer pool頁面從整個例項池中從後向前分配,每次分配一個頁面,而控制結構使從前向後分配,每次分配一個buf_block_t結構的大小,知道相遇為止,這樣就將一個例項初始化好了。
第一、redo log日誌檔案管理:
redo log是用來做資料庫crash recovery的,這是資料庫保障資料安全的重要功能之一。在資料庫操作中,它儲存了對innodb表中資料的修改記錄,所以也叫日誌檔案。在innodb儲存引擎中,一般預設包括2個日誌檔案,新建資料庫之後會有名為ib_logfile0 和ib_logfile1的兩個檔案,如果在啟動資料庫時,這兩個檔案不存在,則innodb會根據配置引數或預設值,重新建立日誌檔案;
1.1、LSN 全名叫:log sequence number:
在innodb內部的日誌管理中,一個很重要的概念是LSN,全名叫log sequence number,它用來精確記錄日誌位置資訊,且是連續增長的。在innodb中,大小為8個位元組值,它的增長量是根據一個MTR寫入的日誌量來計算的,寫多少日誌,LSN就增長多少。(LSN是一個完全邏輯的概念,每提交一個物理事務,LSN就加1)
1.2、在innodb中,通過日誌組來管理日誌檔案,是一個邏輯定義,包含若干個日誌檔案,一個組中的日誌檔案大小相等,大小通過引數來設定,現在innodb只持有一個日誌組。(在MySQL5.5以前日誌組最大4G;MySQL5.6.3以後可以設定的更大到512G)
1.3、redo日誌的寫入,都是位元組連續的,雖然看上去是多個日誌檔案,但理解的時候,完全可以把它想象成一個檔案。(日誌組中的每個日誌檔案,都有自己的格式,內部也是按照大小相等的頁面切割,每個頁面大小是512位元組)
---注意:如果每次寫入是磁碟塊大小的倍數,效率才是最高的,並且日誌將邏輯事務對資料庫的分散隨機寫入轉化成了順序的512位元組整數倍資料的寫入,這樣就大大提高了資料庫的效率。
1.4、redo日誌檔案的格式:
每個日誌檔案,都有檔案頭(普通頁面中,都會有12個位元組用來儲存頁面頭資訊,這些資訊主要用於管理這個頁面本身的資料儲存方式;---注意只有2KB是日誌頭,後面是一個個連續的,用來儲存MTR產生的日誌頁面)
1.5、MTRinnodb物理事務:
它是innodb儲存引擎中一個很重要的用來保證物理頁面寫入操作完整性及永續性的機制。之所以被稱為MTR,是因為它的意義相當於一個mini-transaction,用MTR來表示,這裡吧它稱作“物理事務”,這樣叫是相對邏輯事務而言的。
物理事務既然被稱為事務,那它同樣有事務的開始和提交,物理事務的開始其實就是對物理事務結構體mtr_struct的初始化,物理事務的提交主要是將所有這個物理事務產生的日誌寫入到innodb日誌系統的日誌緩衝區中,然後等待srv_master_thread執行緒定時將日誌系統的日誌緩衝區的日誌資料刷到日誌檔案中;
---注意:日誌緩衝區的儲存只是一個暫時的中間狀態,日誌緩衝區的大小可以通過引數innodb_log_buffer_size來設定,一般都比較小,儲存不了多少日誌。
--日誌是在邏輯事務對資料庫做DML操作時,其所包含的物理事務MTR所記錄的,針對所有涉及的buffer pool頁面的修改記錄;
1.6、日誌提高效能的關鍵原因:
①:因為日誌是用來記錄buffer pool中page的修改記錄的,所以把page的寫入轉化為對日誌的寫入,那此時page就不需要每次都刷盤,寫page頁面只需要在記憶體中寫入即可,效能會非常好;
②:通常,一個頁面是16KB,如果不寫入職,每次寫入的單位還是16KB,即使修改很少的資料,也是如此,這樣會導致無效IO非常嚴重。
1.7、redo日誌大小設定的問題:
①:如果設定的非常大,固然效能可能會很好,但是如果資料庫出現異常停機,此時可能有很多日誌都沒有刷盤,也就是log flushed up to 與 last checkpointat 兩個值之間相差太多,恢復需要比較長的時間。(redo日誌的恢復是順序的,都是根據頁面號的大小排序恢復的;)
②:日誌容量大小的設定,最好與buffer pool的總大小匹配。如果日誌容量太小,buffer pool太大,這就會導致buffer pool頻繁做檢查點,大的buffer pool不能被好好利用,如果日誌容量過大,而buffer pool很小,此時buffer page經常會被淘汰出去,增加IO頻次,同時如果資料庫意外宕機,buffer pool太小,恢復起來也會比較慢;
1.8、redo日誌記錄格式:
innodb的日誌是具有邏輯意義的物理日誌,所以,日誌記錄的格式就不完全是物理資訊,而是有一定邏輯意義,基本的格式如下:
type(日誌型別),space(表空間ID值),offset(前面space所指定的檔案中的頁面號,以頁面大小為單位),data(表示這條日誌記錄對應的資料,這個資料是不確定的,根據不同的type值而不同)
---type型別有很多,比較常用的有:
①:mlog_ibyte、mlog_2bytes、mlog_4bytes、mlog_8bytes:這四個型別,表示要在某個位置,寫入一個(兩個、四個、八個)位元組的內容;
②:mlog_write_string:這種型別的日誌,其實和mlog_ibyte是類似的,只是mlog_ibyte是要寫一個固定長度的資料,而mlog_write_string是要寫一段變長的資料。
③:mlog_undo_insert:這個型別的日誌,是在將一條記錄設定為頁面中的最小記錄時產生的,因為只是打個標記,儲存的內容比較簡單;
④:mlog_init_file_page:這個型別的日誌比較簡單,只有前面的基本頭資訊,沒有data部分;
⑤:mlog_comp_page_create:這個型別只需要村一個型別及要建立的頁面的位置即可;
⑥:mlog_multi_rec_end:這個型別的記錄是非常特殊的,它只起一個標記的作用,其儲存的內容只有佔一個位元組的型別值。
⑦:mlog_comp_rec_clust_delete_mark:這個型別的日誌是表示,需要將聚集索引中的某個記錄打上刪除標誌;
⑧:mlog_comp_rec_update_in_place:這個型別的日誌記錄更新後的記錄資訊,包括所有被更新的列的資訊。
⑨:mlog_comp_page_reorganize:這個型別的日誌表示的是要重組指定的頁面,其記錄的內容也很簡單,只需要儲存要重組哪一個頁面即可;
1.9、日誌刷盤時機:共有5種時機:
①:log buffer空間用完了,這就會將已經產生的log buffer中的日誌刷到磁碟中,這是最普遍的一種方式;
②:master執行緒在後臺每秒鐘刷一次,將當前log buffer中的日誌刷到磁碟中;
③:每次執行DML操作時,都會主動檢查日誌空間是否足夠,如果使用空間的量已經超過了一個預設的經驗值,就會主動刷日誌,以保證在後面真正執行時,不會再執行過程中被動的刷盤,但這裡只會是寫檔案(寫入OS緩衝中)不會刷盤
④:在做檢查點的時候,要保證所有要刷的頁面中LSN值最小的日誌已經刷入到磁碟,不然,如果此時資料庫宕機,日誌不存在,但資料頁面已經被修改,從而導致資料不一致,就違背了寫日誌的原則;
⑤:提交邏輯事務時,會因為引數innodb_flush_log_at_trx_commit值的不同,產生不同的行為。如果設定0,則在事務提交時,根本不會去刷日誌緩衝區,這種設定是最危險的;如果設定2,則在事務提交時會將日誌寫入到檔案中,但不會去刷盤,只要作業系統不掛,即使資料庫掛了,資料還是不會丟失,一般都是設定為2;
1.10、redo log刷盤機制:
當提交事務(邏輯)時,可以通過引數innodb_flush_log_at_trx_commit來控制redo log寫入的機制,引數值不同,產生的行為不同,主要引數值如下:
①:innodb_flush_log_at_trx_commit=0
事務提交時,MySQL不會去處理日誌快取區的內容,也不會去處理日誌檔案的刷盤操作,由MySQL的後臺master執行緒每隔1s將快取區的檔案重新整理到日誌檔案中;(主機正常,資料庫宕機後:一般只會丟失最近1s的事務)
②:innodb_flush_log_at_trx_commit=1
事務提交時,會將日誌緩衝區的日誌寫入到檔案中,同時會重新整理到磁碟中,保證資料庫事務完全不會丟失。這種設定影響資料庫效能;(主機正常,資料庫宕機後:資料不會丟失)
③:innodb_flush_log_at_trx_commit=2
事務提交時,會將日誌快取區日誌寫入到檔案中,但是不會重新整理到磁碟中。由MySQL的後臺master執行緒每隔1s將系統快取的日誌檔案重新整理到磁碟中;(主機正常,資料庫宕機後:資料不會丟失)
---注意:如果資料庫所在主機宕機後:引數0 會丟失最近1s的事務;引數1 不會有任何資料丟失; 引數2 會丟失最近1s的事務;
第二、資料庫undo段管理:
在innodb中支援的回滾段總共有:128X1024=131072個,在每一個事務開始的時候,都會分配一個rseg,就是從長度為128的陣列中,根據最近使用的情況,找到一個臨近位置的rseg;
在事務執行的過程中,會產生兩種回滾日誌,一種是insert的undo記錄,一種是update的undo記錄;(因為innodb把undo分為兩類,一類就是新增,也就是insert,一類是修改,就是update,分類的依據就是事務提交後要不要做purge操作,因為insert是不需要purge的,只要事務提交了,那這個回滾記錄就可以丟掉了,而對於更新和刪除操作而言,如果事務提交了,還需要為MVCC服務,那就需要將這些日誌放到history list中去,等待去做purge已經MVCC的多版本查詢等,所以分為兩類)
2.1、資料庫undo日誌記錄格式:
undo有4種類型:
①:trx_undo_insert_rec:記錄插入的undo日誌型別,插入記錄用於回滾時,只需要通過其主鍵就可以實現回滾操作,所以在undo日誌中,只記錄了表ID及主鍵資訊;
②:trx_undo_upd_exist_rec:更新一條存在記錄的undo日誌型別;
③:trx_undo_upd_del_rec:更新一條已經打了刪除標誌記錄的undo日誌型別;
④:trx_undo_del_mark_rec:刪除記錄時對記錄打刪除標誌的undo日誌型別;
---注意:與redo日誌記錄儲存不同,undo日誌的儲存,是不會垮頁面的;
---注意:使用引數innodb_force_recovery來決定要不要做回滾操作,如果設定3或3以上,那麼在啟動innodb的時候就不回滾了,這樣可能導致資料庫邏輯上的不一致;