程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> MySQL系列:innodb引擎分析之文件IO

MySQL系列:innodb引擎分析之文件IO

編輯:MySQL綜合教程

MySQL系列:innodb引擎分析之文件IO


innodb作為數據庫引擎,自然少不了對文件的操作,在innodb中所有需要持久化的信息都需要文件操作,例如:表文件、重做日志文件、事務日志文件、備份歸檔文件等。innodb對文件IO操作可以是煞費苦心,其主要包括兩方面,一個是對異步io的實現,一個是對文件操作管理和io調度的實現。在MySQL-5.6版本的innodb還加入了DIRECT IO實現。做了這麼多無非是優化io操作的性能。在innodb的文件IO部分中,主要實現集中在os_file.*和fil0fil.*兩個系列的文件當中,其中os_file*是實現基本的文件操作、異步IO和模擬異步IO。fil0fil.*是對文件io做系統的管理和space結構化。下面依次來介紹這兩個方面的內容.

1.系統文件IO

在innodb中,文件的操作是比較關鍵的,innodb封裝了基本的文件操作,例如:文件打開與關閉、文件讀寫以及文件屬性訪問等。這些是基本的文件操作函數封裝。在linux文件的讀寫方面,默認是采用pread/pwrite函數進行讀寫操作,如果系統部支持這兩個函數,innodb用lseek和read、write函數聯合使用來達到效果. 以下是innodb文件操作函數: os_file_create_simple 創建或者打開一個文件 os_file_create 創建或者打開一個文件,如果操作失敗會重試,直到成功 os_file_close 關閉打開的文件 os_file_get_size 獲得文件的大小 os_file_set_size 設置文件的大小並以0填充文件內容 os_file_flush 將寫的內容fsync到磁盤 os_file_read 從文件中讀取數據 os_file_write 將數據寫入文件 innodb除了實現以上基本的操作以外,還實現了文件的異步IO模型,在Windows下采用的IOCP模型來進行處理(具 體可以見網上的資料),在linux下是采用aio來實現的,有種情況,一種是通過系統本身的aio機制來實現,還有一種是 通過多線程信號模擬來實現aio.這裡我們重點來介紹,為了實現aio,innodb定義了slot和slot array,具體數據結構如下:
typedef struct os_aio_slot_struct
{
     ibool	 is_read;                             /*是否是讀操作*/
     ulint	 pos;                                    /*slot array的索引位置*/
     ibool	 reserved;                           /*這個slot是否被占用了*/
     ulint	 len;                                     /*讀寫的塊長度*/
     byte*	 buf;                                   /*需要操作的數據緩沖區*/
     ulint	 type;                                   /*操作類型:OS_FILE_READ OS_FILE_WRITE*/
     ulint	 offset;                                 /*當前操作文件偏移位置,低32位*/
     ulint	 offset_high;                        /*當前操作文件偏移位置,高32位*/
     os_file_t	 file;                               /*文件句柄*/
     char*	 name;                               /*文件名*/
     ibool	 io_already_done;             /*在模擬aio的模式下使用,TODO*/
     void*	 message1;
     void*	 message2;
#ifdef POSIX_ASYNC_IO
     struct aiocb	control;                 /*posix 控制塊*/
#endif
}os_aio_slot_t;

typedef struct os_aio_array_struct
{
 os_mutex_t	 mutex;          /*slots array的互斥鎖*/
 os_event_t	 not_full;         /*可以插入數據的信號,一般在slot數據被aio操作後array_slot有空閒可利用的slot時發送*/
 os_event_t	 is_empty;       /*array 被清空的信號,一般在slot數據被aio操作後array_slot裡面沒有slot時發送這個信號*/

 ulint	 n_slots;                     /*slots總體單元個數*/
 ulint	 n_segments;             /*segment個數,一般一個對應n個slot,n = n_slots/n_segments,一個segment作為aio一次的操作范圍*/
 ulint	 n_reserved;              /*有效的slots個數*/
 os_aio_slot_t*	slots;         /*slots數組*/

 os_event_t*	 events;         /*slots event array,暫時沒弄明白做啥用的*/
}os_aio_array_t;
內存結構關系圖:
\

2.文件管理的內存結構

在innodb中定義三種文件類型:表空間文件(ibdata*)、重做日志文件(ib_logfile*)和歸檔文件(ib_arch_log*)。一般innodb在運行的過程中,會同時打開很多個文件,這就要求對文件進行系統的管理和控制。在innodb中定義了一套基於fil_system_t、fil_space_t和fil_node_t的內存管理結構。每個文件對應的是一個fil_node_t,fil_node是存儲的最小單元,多個同一模塊的fil_node組成一個fil_space_t,所有的space組成一個fil_system_t,在innodb引擎裡,只有一個fil_system_t對象。

fil_system_t管理著全局的文件操作資源,例如:文件打開的數量、打開文件的信號控制、fil_space_t的管理和索引等。以下是fil_system_t的結構定義:

 

typedef struct fil_system_struct
{
     mutex_t	 mutex;              /*file system的保護鎖*/
     hash_table_t*	spaces;     /*space的哈希表,用於快速檢索space,一般是通過space id查找*/
     ulint	 n_open_pending;  /*當前有讀寫IO操作的fil_node個數*/
     ulint	 max_n_open;         /*最大允許打開的文件個數*/
     os_event_t	 can_open;    /*可以打開新的文件的信號*/
 
    UT_LIST_BASE_NODE_T(fil_node_t) LRU;       /*最近被打開操作過的文件,用於快速定位關閉的fil_node*/
    UT_LIST_BASE_NODE_T(fil_node_t) space_list;	 /*file space的對象列表*/
}fil_system_t;
值得注意的是space的哈希表和LRU,這裡為什麼會出現用hash table來索引space呢?因為在實際的數據庫系統中,fil_space_t是會非常多的,用哈希表能快速定位到需要操作的fil_space_t。LRU是用於保存最近被打開和被操作過的fil_node,為了避免頻發的關閉和打開文件,LRU保存一定數量(500)的最近打開過的文件,這樣可以提高系統的效率。

 

fil_space_t是用於管理同一模塊的file_node,上層模塊操作文件不是以文件名來做操作關聯的,而是用space_id,

也就是說,所有的文件操作是通過space為單位進行操作的。fil_space支持三種類型,分別是:
FIL_TABLESPACE 表空間space
FIL_LOG 重做日志space
FIL_ARCHI_LOG 歸檔日志space

fil_space_t的定義如下:

 

struct fil_space_struct
{
     char*	 name;                     /*space名稱*/
     ulint	 id;                            /*space id*/
     ulint	 purpose;                 /*space的類型,主要有space table, log file和arch file*/
     ulint	 size;                         /*space包含的頁個數*/
     ulint	 n_reserved_extents; /*預留的頁個數*/
     hash_node_t	 hash;          /*chain node的HASH表*/
     rw_lock_t	 latch;               /*space操作保護鎖,用於多線程並發*/
     ibuf_data_t*	ibuf_data;   /*space 對應的insert buffer*/
     ulint	 magic_n;                 /*魔法校驗字*/

     UT_LIST_BASE_NODE_T(fil_node_t) chain;
     UT_LIST_NODE_T(fil_space_t)	 space_list;
};
fil_space通常是由一組文件組成,例如重做日志,一般是有3個文件組成一個group space用於重做日志記錄。space通過成員latch可以支持多線程並發的。在innodb文件操作中,主要是通過space來做控制,以下是它的控制函數:
fil_space_create 創建一個fil_space
fil_space_free 銷毀一個fil_space
fil_space_truncate_start 從space中刪除fil_node,刪除的總數據長度為trunc_len
fil_node_create 創建一個fil_node並加入到對應的space當中
fil_space_get_size 獲得space的空間大小,以page為單位記
fil_io 指定space的io操作
fil_aio_wait aio異步方式的io操作等待,並根據完成狀態更新space狀態
fil_flush 指定space進行數據刷盤
fil_node_t是對單個文件進行管理,主要是管理文件的打開狀態、文件句柄信息、文件的page數量和更新狀態等。

 

其結構定義如下:

 

struct fil_node_struct
{
     char*	 name;                         /*文件路徑名*/
     ibool	 open;                         /*文件是否被打開*/
     os_file_t	handle;                  /*文件句柄*/
     ulint	 size;                             /*文件包含的頁個數,一個頁是16K*/
     ulint	 n_pending;                 /*等待讀寫IO操作的個數*/
     ibool	 is_modified;               /*是否有髒也存在,flush是根據這個標志進行刷盤的*/
     ulint	 magic_n;                     /*魔法校驗字*/
     UT_LIST_NODE_T(fil_node_t) chain;
     UT_LIST_NODE_T(fil_node_t) LRU;
};

值得注意的是當外部調用了fil_flush時,判斷一個fil_node是否需要刷盤的必要條件是:
文件必須是打開的 open = TRUE
文件存在內存和硬盤數據不一致 is_modified = TRUE

 

 

了解了他們三者的基本定義後,那他們之間的關系是怎麼的?不用文字敘述,看下面的內存結構關系圖:

 

\

 

在了解了他們之間的基本關系後,那麼一個io操作是怎麼進行的?在這個模型裡,一個io操作提交和被運行是比較復雜的。具體流程如下: 1.外部模塊提交一個fil_io, 先會進行基本的io操作類型的判斷和文件打開方式的判斷。 2.然後進行對正在進行io操作的計數做判斷,如果正在進行的io數量 > 最大文件打開數量的四分之三,喚醒所有aio的操作線程進行io處理,並進行sleep等待。 3.如果正在進行的io數量 = 最大文件打開數量,喚醒所有的aio操作線程進行io處理,並等待fil_system_t的can_open信號。 4.如果不滿足2和3,找到需要受理io操作的space和node,並打開node對應的文件,打開文件時會對打開文件數量限制做判斷,如果當前打開文件操作io的數量 + LRU裡已經打開文件的數量>= 最大文件打開數量時,會取出LRU中最後一個fil_node進行文件關閉。然後在對新的io操作的fil_node文件進行打開。 5.fil_node文件打開後,調用os_aio進行io操作提交,然後等待io操作完成 6. io操作完成後,將完成io操作的fil_node放入LRU的第一個位置,並更改對應的fil_system/fil_space/fil_node的狀態,最後觸發一個fil_system的can open信號。 7.監聽can_open的線程收到這個信號後,會跳到第4步進行自己的io操作提交。 流程圖如下: \

 

3總結

總體來說,innodb的文件IO涉及到知識面很多,可以能短時間無法完全理解透徹,一般在閱讀源碼的時候可以做一些基本的單元測試,這樣有助於理解。弄清楚innodb的文件IO操作是非常有必要的,因為文件IO操作模塊直接影響對innodb的日志系統的理解、表空間系統的理解。而且Innodb在文件IO模塊的改進還是比較大的,尤其是引入Direct IO後。Direct IO很多數據庫都在用這個技術,除了innodb,oracle和淘寶的oceanbase都使用了這個技術, 關於Direct IO網絡上資料很多,可以自行結合MySQL-5.6的innodb來做研究。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved