天天看點

《MySQL技術内幕:InnoDB存儲引擎第2版》——3.6 InnoDB存儲引擎檔案

之前介紹的檔案都是mysql資料庫本身的檔案,和存儲引擎無關。除了這些檔案外,每個表存儲引擎還有其自己獨有的檔案。本節将具體介紹與innodb存儲引擎密切相關的檔案,這些檔案包括重做日志檔案、表空間檔案。

3.6.1 表空間檔案

innodb采用将存儲的資料按表空間(tablespace)進行存放的設計。在預設配置下會有一個初始大小為10mb,名為ibdata1的檔案。該檔案就是預設的表空間檔案(tablespace file),使用者可以通過參數innodb_data_file_path對其進行設定,格式如下:

使用者可以通過多個檔案組成一個表空間,同時制定檔案的屬性,如:

這裡将/db/ibdata1和/dr2/db/ibdata2兩個檔案用來組成表空間。若這兩個檔案位于不同的磁盤上,磁盤的負載可能被平均,是以可以提高資料庫的整體性能。同時,兩個檔案的檔案名後都跟了屬性,表示檔案idbdata1的大小為2000mb,檔案ibdata2的大小為2000mb,如果用完了這2000mb,該檔案可以自動增長(autoextend)。

設定innodb_data_file_path參數後,所有基于innodb存儲引擎的表的資料都會記錄到該共享表空間中。若設定了參數innodb_file_per_table,則使用者可以将每個基于innodb存儲引擎的表産生一個獨立表空間。獨立表空間的命名規則為:表名.ibd。通過這樣的方式,使用者不用将所有資料都存放于預設的表空間中。下面這台mysql資料庫伺服器設定了innodb_file_per_table,故可以觀察到:

表profile、t1和t2都是基于innodb存儲的表,由于設定參數innodb_file_per_table=on,是以産生了單獨的.ibd獨立表空間檔案。需要注意的是,這些單獨的表空間檔案僅存儲該表的資料、索引和插入緩沖bitmap等資訊,其餘資訊還是存放在預設的表空間中。圖3-1顯示了innodb存儲引擎對于檔案的存儲方式:

《MySQL技術内幕:InnoDB存儲引擎第2版》——3.6 InnoDB存儲引擎檔案

3.6.2 重做日志檔案

在預設情況下,在innodb存儲引擎的資料目錄下會有兩個名為ib_logfile0和ib_logfile1的檔案。在mysql官方手冊中将其稱為innodb存儲引擎的日志檔案,不過更準确的定義應該是重做日志檔案(redo log file)。為什麼強調是重做日志檔案呢?因為重做日志檔案對于innodb存儲引擎至關重要,它們記錄了對于innodb存儲引擎的事務日志。

當執行個體或媒體失敗(media failure)時,重做日志檔案就能派上用場。例如,資料庫由于所在主機掉電導緻執行個體失敗,innodb存儲引擎會使用重做日志恢複到掉電前的時刻,以此來保證資料的完整性。

每個innodb存儲引擎至少有1個重做日志檔案組(group),每個檔案組下至少有2個重做日志檔案,如預設的ib_logfile0和ib_logfile1。為了得到更高的可靠性,使用者可以設定多個的鏡像日志組(mirrored log groups),将不同的檔案組放在不同的磁盤上,以此提高重做日志的高可用性。在日志組中每個重做日志檔案的大小一緻,并以循環寫入的方式運作。innodb存儲引擎先寫重做日志檔案1,當達到檔案的最後時,會切換至重做日志檔案2,再當重做日志檔案2也被寫滿時,會再切換到重做日志檔案1中。圖3-2顯示了一個擁有3個重做日志檔案的重做日志檔案組。

《MySQL技術内幕:InnoDB存儲引擎第2版》——3.6 InnoDB存儲引擎檔案

下列參數影響着重做日志檔案的屬性:

?innodb_log_file_size

?innodb_log_files_in_group

?innodb_mirrored_log_groups

?innodb_log_group_home_dir

參數innodb_log_file_size指定每個重做日志檔案的大小。在innodb1.2.x版本之前,重做日志檔案總的大小不得大于等于4gb,而1.2.x版本将該限制擴大為了512gb。

參數innodb_log_files_in_group指定了日志檔案組中重做日志檔案的數量,預設為2。參數innodb_mirrored_log_groups指定了日志鏡像檔案組的數量,預設為1,表示隻有一個日志檔案組,沒有鏡像。若磁盤本身已經做了高可用的方案,如磁盤陣列,那麼可以不開啟重做日志鏡像的功能。最後,參數innodb_log_group_home_dir指定了日志檔案組所在路徑,預設為./,表示在mysql資料庫的資料目錄下。以下顯示了一個關于重做日志組的配置:

重做日志檔案的大小設定對于innodb存儲引擎的性能有着非常大的影響。一方面重做日志檔案不能設定得太大,如果設定得很大,在恢複時可能需要很長的時間;另一方面又不能設定得太小了,否則可能導緻一個事務的日志需要多次切換重做日志檔案。此外,重做日志檔案太小會導緻頻繁地發生async checkpoint,導緻性能的抖動。例如,使用者可能會在錯誤日志中看到如下警告資訊:

上面錯誤集中在innodb:error:the age of the last checkpoint is 9433645,innodb:which exceeds the log group capacity 9433498。這是因為重做日志有一個capacity變量,該值代表了最後的檢查點不能超過這個門檻值,如果超過則必須将緩沖池(innodb buffer pool)中髒頁清單(flush list)中的部分髒資料頁寫回磁盤,這時會導緻使用者線程的阻塞。

也許有人會問,既然同樣是記錄事務日志,和之前介紹的二進制日志有什麼差別?

首先,二進制日志會記錄所有與mysql資料庫有關的日志記錄,包括innodb、myisam、heap等其他存儲引擎的日志。而innodb存儲引擎的重做日志隻記錄有關該存儲引擎本身的事務日志。

其次,記錄的内容不同,無論使用者将二進制日志檔案記錄的格式設為statement還是row,又或者是mixed,其記錄的都是關于一個事務的具體操作内容,即該日志是邏輯日志。而innodb存儲引擎的重做日志檔案記錄的是關于每個頁(page)的更改的實體情況。

此外,寫入的時間也不同,二進制日志檔案僅在事務送出前進行送出,即隻寫磁盤一次,不論這時該事務多大。而在事務進行的過程中,卻不斷有重做日志條目(redo entry)被寫入到重做日志檔案中。

在innodb存儲引擎中,對于各種不同的操作有着不同的重做日志格式。到innodb 1.2.x版本為止,總共定義了51種重做日志類型。雖然各種重做日志的類型不同,但是它們有着基本的格式,表3-2顯示了重做日志條目的結構:

《MySQL技術内幕:InnoDB存儲引擎第2版》——3.6 InnoDB存儲引擎檔案

從表3-2可以看到重做日志條目是由4個部分組成:

?redo_log_type占用1位元組,表示重做日志的類型

?space表示表空間的id,但采用壓縮的方式,是以占用的空間可能小于4位元組

?page_no表示頁的偏移量,同樣采用壓縮的方式

?redo_log_body表示每個重做日志的資料部分,恢複時需要調用相應的函數進行解析

在第2章中已經提到,寫入重做日志檔案的操作不是直接寫,而是先寫入一個重做日志緩沖(redo log buffer)中,然後按照一定的條件順序地寫入日志檔案。圖3-3很好地诠釋了重做日志的寫入過程。

《MySQL技術内幕:InnoDB存儲引擎第2版》——3.6 InnoDB存儲引擎檔案

從重做日志緩沖往磁盤寫入時,是按512個位元組,也就是一個扇區的大小進行寫入。因為扇區是寫入的最小機關,是以可以保證寫入必定是成功的。是以在重做日志的寫入過程中不需要有doublewrite。

前面提到了從日志緩沖寫入磁盤上的重做日志檔案是按一定條件進行的,那這些條件有哪些呢?第2章分析了主線程(master thread),知道在主線程中每秒會将重做日志緩沖寫入磁盤的重做日志檔案中,不論事務是否已經送出。另一個觸發寫磁盤的過程是由參數innodb_flush_log_at_trx_commit控制,表示在送出(commit)操作時,處理重做日志的方式。

參數innodb_flush_log_at_trx_commit的有效值有0、1、2。0代表當送出事務時,并不将事務的重做日志寫入磁盤上的日志檔案,而是等待主線程每秒的重新整理。1和2不同的地方在于:1表示在執行commit時将重做日志緩沖同步寫到磁盤,即伴有fsync的調用。2表示将重做日志異步寫到磁盤,即寫到檔案系統的緩存中。是以不能完全保證在執行commit時肯定會寫入重做日志檔案,隻是有這個動作發生。

是以為了保證事務的acid中的持久性,必須将innodb_flush_log_at_trx_commit設定為1,也就是每當有事務送出時,就必須確定事務都已經寫入重做日志檔案。那麼當資料庫因為意外發生當機時,可以通過重做日志檔案恢複,并保證可以恢複已經送出的事務。而将重做日志檔案設定為0或2,都有可能發生恢複時部分事務的丢失。不同之處在于,設定為2時,當mysql資料庫發生當機而作業系統及伺服器并沒有發生當機時,由于此時未寫入磁盤的事務日志儲存在檔案系統緩存中,當恢複時同樣能保證資料不丢失。