<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                在innodb的引擎實現中,為了實現事務的持久性,構建了重做日志系統。重做日志由兩部分組成:內存日志緩沖區(redo log buffer)和重做日志文件。這樣設計的目的顯而易見,日志緩沖區是為了加快寫日志的速度,而重做日志文件為日志數據提供持久化的作用。在innodb的重做日志系統中,為了更好實現日志的易恢復性、安全性和持久化性,引入了以下幾個概念:LSN、log block、日志文件組、checkpoint和歸檔日志。以下我們分別一一來進行分析。 ## 1.LSN 在innodb中的重做日志系統中,定義一個LSN序號,其代表的意思是日志序號。LSN在引擎中定義的是一個dulint_t類型值,相當于uint64_t,關于dulint_t的定義如下: ~~~ typedef struct dulint_struct { ulint high; /* most significant 32 bits */ ulint low; /* least significant 32 bits */ }dulint_t; ~~~ LSN真正的含義是儲存引擎向重做日志系統寫入的日志量(字節數),這個日志量包括寫入的日志字節 + block_header_size + block_tailer_size。LSN的初始化值是:LOG_START_LSN(相當于8192),在調用日志寫入函數LSN就一直隨著寫入的日志長度增加,具體看: ~~~ void log_write_low(byte* str, ulint str_len) { log_t* log = log_sys; . . . part_loop: /*計算part length*/ data_len = log->buf_free % OS_FILE_LOG_BLOCK_SIZE + str_len; . . . /*將日志內容拷貝到log buffer*/ ut_memcpy(log->buf + log->buf_free, str, len); str_len -= len; str = str + len; . . . if(data_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE){ /*完成一個block的寫入*/ . . . len += LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE; log->lsn = ut_dulint_add(log->lsn, len); . . . } else /*更改lsn*/ log->lsn = ut_dulint_add(log->lsn, len); . . . } ~~~ LSN是不會減小的,它是日志位置的唯一標記。在重做日志寫入、checkpoint構建和PAGE頭里面都有LSN。 **關于日志寫入:** 例如當前重做日志的LSN = 2048,這時候innodb調用log_write_low寫入一個長度為700的日志,2048剛好是4個block長度,那么需要存儲700長度的日志,需要量個BLOCK(單個block只能存496個字節)。那么很容易得出新的LSN = 2048 + 700 + 2 *?LOG_BLOCK_HDR_SIZE(12)?+?LOG_BLOCK_TRL_SIZE(4) = 2776。 **關于checkpoint和日志恢復:** 在page的fil_header中的LSN是表示最后刷新是的LSN, 假如數據庫中存在PAGE1 LSN ?= 1024,PAGE2 LSN = 2048, 系統重啟時,檢測到最后的checkpoint LSN = 1024,那么系統在檢測到PAGE1不會對PAGE1進行恢復重做,當系統檢測到PAGE2的時候,會將PAGE2進行重做。一次類推,小于checkpoint LSN的頁不用重做,大于LSN checkpoint的PAGE就要進行重做。 ## 2.Log Block innodb在日志系統里面定義了log block的概念,其實log block就是一個512字節的數據塊,這個數據塊包括塊頭、日志信息和塊的checksum.其結構如下: ![](https://box.kancloud.cn/2016-08-17_57b42162e82d0.jpg) Block no的最高位是描述block是否flush磁盤的標識位.通過lsn可以blockno,具體的計算過程是lsn是多少個512的整數倍,也就是no = lsn / 512 + 1;為什么要加1呢,因為所處no的塊算成clac_lsn一定會小于傳入的lsn.所以要+1。其實就是block的數組索引值。checksum是通過從塊頭開始到塊的末尾前4個字節為止,做了一次數字疊加,代碼如下: ~~~ sum = 1; sh = 0; for(i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE, i ++){ sum = sum & 0x7FFFFFFF; sum += (((ulint)(*(block + i))) << sh) + (ulint)(*(block + i)); sh ++; if(sh > 24) sh = 0; } ~~~ 在日志恢復的時候,innodb會對加載的block進行checksum校驗,以免在恢復過程中數據產生錯誤。事務的日志寫入是基于塊的,如果事務的日志大小小于496字節,那么會合其他的事務日志合并在一個塊中,如果事務日志的大小大于496字節,那么會以496為長度進行分離存儲。例如:T1 = 700字節大小,T2 = 100字節大小存儲結構如下: ![](https://box.kancloud.cn/2016-08-17_57b4216308db8.jpg) ## 3.重做日志結構和關系圖 innodb在重做日志實現當中,設計了3個層模塊,即redo log buffer、group files和archive files。這三個層模塊的描述如下: redo log buffer ?? ? ?重做日志的日志內存緩沖區,新寫入的日志都是先寫入到這個地方.redo log buffer中數據同步到磁盤上,必須進行刷盤操作。 group files ? ? ?重做日志文件組,一般由3個同樣大小的文件組成。3個文件的寫入是依次循環的,每個日志文件寫滿后,即寫下一個,日志文件如果都寫滿時,會覆蓋第一次重新寫。重做日志組在innodb的設計上支持多個。 archive files ? ? ? ? 歸檔日志文件,是對重做日志文件做增量備份,它是不會覆蓋以前的日志信息。 **以下是它們關系示意圖:** ![](https://box.kancloud.cn/2016-08-17_57b4216321efd.jpg) ### 3.1重做日志組 重做日志組可以支持多個,這樣做的目的應該是為了防止一個日志組損壞后,可以從其他并行的日志組里面進行數據恢復。在MySQL-5.6的將日志組的個數設置為1,不允許多個group存在。網易姜承堯的解釋是innodb的作者認為通過外層存儲硬件來保證日志組的完整性比較好,例如raid磁盤。重做日志組的主要功能是實現對組內文件的寫入管理、組內的checkpoint建立和checkpiont信息的保存、歸檔日志狀態管理(只有第一個group才做archive操作).以下是對日志組的定義: ~~~ typedef struct log_group_struct { ulint id; /*log group id*/ ulint n_files; /*group包含的日志文件個數*/ ulint file_size; /*日志文件大小,包括文件頭*/ ulint space_id; /*group對應的fil_space的id*/ ulint state; /*log group狀態,LOG_GROUP_OK、LOG_GROUP_CORRUPTED*/ dulint lsn; /*log group的lsn*/ dulint lsn_offset; /*當前lsn相對組內文件起始位置的偏移量 */ ulint n_pending_writes; /*本group 正在執行fil_flush的個數*/ byte**file_header_bufs; /*文件頭緩沖區*/ byte**archive_file_header_bufs;/*歸檔文件頭信息的緩沖區*/ ulint archive_space_id; /*歸檔重做日志ID*/ ulint archived_file_no; /*歸檔的日志文件編號*/ ulint archived_offset; /*已經完成歸檔的偏移量*/ ulint next_archived_file_no; /*下一個歸檔的文件編號*/ ulint next_archived_offset; /*下一個歸檔的偏移量*/ dulint scanned_lsn; byte* checkpoint_buf; /*本log group保存checkpoint信息的緩沖區*/ UT_LIST_NODE_T(log_group_t) log_groups; }log_group_t; ~~~ 上面結構定義中的spaceid是對應fil0fil中的fil_space_t結構,一個fil_space_t結構可以管理多個文件fil_node_t,關于fil_node_t參見[這里](http://blog.csdn.net/yuanrxdu/article/details/41418421)。 ### 3.1.1LSN與組內偏移 ?在log_goup_t組內日志模塊當中,其中比較重要的是關于LSN與組內偏移之間的換算關系。在組創建時,會對lsn和對應lsn_offset做設置,假如 初始化為 group lsn = 1024, ?group lsn_offset = 2048,group由3個10240大小的文件組成,LOG_FILE_HDR_SIZE = 2048, 我們需要知道buf lsn = 11240對應的組內offset的偏移是多少,根據log_group_calc_lsn_offset函數可以得出如下公式: ? group_size = 3 * 11240; ?相對組起始位置的LSN偏移 = (buf_ls - group_ls) ?+ log_group_calc_size_offset(lsn_offset ) = (11240 - 1024) - 0 = 10216; ?lsn_offset = log_group_calc_lsn_offset(相對組起始位置的LSN偏移 % group_size) = 10216 + 2 * LOG_FILE_HDR_SIZE = 14312; 這個偏移一定是加上了文件頭長度的。 ### 3.1.2 file_header_bufs file_header_bufs是一個buffer緩沖區數組,數組長度和組內文件數是一致的,每個buf長度是2048。其信息結構如下: ![](https://box.kancloud.cn/2016-08-17_57b421633c7e8.jpg) log_group_id ? ? ?對應log_group_t結構中的id file_start_lsn ? ?當前文件其實位置數據對應的LSN值 File_no ? ? ? ? ? 當前的文件編號,一般在archive file頭中體現 Hot backup str ? ?一個空字符串,如果是hot_backup,會填上文件后綴ibackup。 File_end_ls ? ? ? 文件結尾數據對應的LSN值,一般在archive file文件中體現。 ### 3.2 checkpoint checkpoint是日志的檢查點,其作用就是在數據庫異常后,redo log是從這個點的信息獲取到LSN,并對檢查點以后的日志和PAGE做重做恢復。那么檢查點是怎么生成的呢?當日志緩沖區寫入的日志LSN距離上一次生成檢查點的LSN達到一定差距的時候,就會開始創建檢查點,創建檢查點首先會將內存中的表的臟數據寫入到硬盤,讓后再將redo log buffer中小于本次檢查點的LSN的日志也寫入硬盤。在log_group_t中的checkpoint_buf,以下是它對應字段的解釋: LOG_CHECKPOINT_NO ? ? ? ? ? ?checkpoint序號, LOG_CHECKPOINT_LSN ? ? ? ? ? 本次checkpoint起始的LSN LOG_CHECKPOINT_OFFSET ? ? ? ?本次checkpoint相對group file的起始偏移量 LOG_CHECKPOINT_LOG_BUF_SIZE? redo log buffer的大小,默認2M LOG_CHECKPOINT_ARCHIVED_LSN? 當前日志歸檔的LSN LOG_CHECKPOINT_GROUP_ARRAY?? 每個log group歸檔時的文件序號和偏移量,是一個數組 ### 3.3 log_t 重做日志的寫入、數據刷盤、建立checkpoint和歸檔操作都是通過全局唯一的,log_sys進行控制的,這是個非常龐大而又復雜的結構,定義如下: ~~~ typedef struct log_struct { byte pad[64]; /*使得log_struct對象可以放在通用的cache line中的數據,這個和CPU L1 Cache和數據競爭有和 直接關系*/ dulint lsn; /*log的序列號,實際上是一個日志文件偏移量*/ ulint buf_free; /*buf可以寫的位置*/ mutex_t mutex; /*log保護的mutex*/ byte* buf; /*log緩沖區*/ ulint buf_size; /*log緩沖區長度*/ ulint max_buf_free; /*在log buffer刷盤后,推薦buf_free的最大值,超過這個值會被強制刷盤*/ ulint old_buf_free; /*上次寫時buf_free的值,用于調試*/ dulint old_lsn; /*上次寫時的lsn,用于調試*/ ibool check_flush_or_checkpoint; /*需要日志寫盤或者是需要刷新一個log checkpoint的標識*/ ulint buf_next_to_write; /*下一次開始寫入磁盤的buf偏移位置*/ dulint written_to_some_lsn; /*第一個group刷完成是的lsn*/ dulint written_to_all_lsn; /*已經記錄在日志文件中的lsn*/ dulint flush_lsn; /*flush的lsn*/ ulint flush_end_offset; /*最后一次log file刷盤時的buf_free,也就是最后一次flush的末尾偏移量*/ ulint n_pending_writes; /*正在調用fil_flush的個數*/ os_event_t no_flush_event; /*所有fil_flush完成后才會觸發這個信號,等待所有的goups刷盤完成*/ ibool one_flushed; /*一個log group被刷盤后這個值會設置成TRUE*/ os_event_t one_flushed_event; /*只要有一個group flush完成就會觸發這個信號*/ ulint n_log_ios; /*log系統的io操作次數*/ ulint n_log_ios_old; /*上一次統計時的io操作次數*/ time_t last_printout_time; ulint max_modified_age_async; /*異步日志文件刷盤的閾值*/ ulint max_modified_age_sync; /*同步日志文件刷盤的閾值*/ ulint adm_checkpoint_interval; ulint max_checkpoint_age_async; /*異步建立checkpoint的閾值*/ ulint max_checkpoint_age; /*強制建立checkpoint的閾值*/ dulint next_checkpoint_no; dulint last_checkpoint_lsn; dulint next_checkpoint_lsn; ulint n_pending_checkpoint_writes; rw_lock_t checkpoint_lock; /*checkpoint的rw_lock_t,在checkpoint的時候,是獨占這個latch*/ byte* checkpoint_buf; /*checkpoint信息存儲的buf*/ ulint archiving_state; dulint archived_lsn; dulint max_archived_lsn_age_async; dulint max_archived_lsn_age; dulint next_archived_lsn; ulint archiving_phase; ulint n_pending_archive_ios; rw_lock_t archive_lock; ulint archive_buf_size; byte* archive_buf; os_event_t archiving_on; ibool online_backup_state; /*是否在backup*/ dulint online_backup_lsn; /*backup時的lsn*/ UT_LIST_BASE_NODE_T(log_group_t) log_groups; }log_t; ~~~ ### 3.3.1各種LSN之間的關系和分析 從上面的結構定義可以看出有很多LSN相關的定義,那么這些LSN直接的關系是怎么樣的呢?理解這些LSN之間的關系對理解整個重做日志系統的運作機理會有極大的信心。以下各種LSN的解釋: lsn ? ? ? ? ? ? ? ? ? ? ? ?當前log系統最后寫入日志時的LSN flush_lsn ? ? ? ? ? ? ? ? ?redolog buffer最后一次數據刷盤數據末尾的LSN,作為下次刷盤的起始LSN written_to_some_lsn ? ? ? 單個日志組最后一次日志刷盤時的起始LSN written_to_all_lsn ? ? ? ? 所有日志組最后一次日志刷盤是的起始LSN last_checkpoint_lsn ? ? ? ?最后一次建立checkpoint日志數據起始的LSN next_checkpoint_lsn ? ? ? ?下一次建立checkpoint的日志 ? ?數據起始的LSN,用log_buf_pool_get_oldest_modification獲得的 archived_lsn ? ? ? ? ? ? ? 最后一次歸檔日志數據起始的LSN next_archived_lsn ? ? ? ? ?下一次歸檔日志數據的其實LSN ? **關系圖如下:** ![](https://box.kancloud.cn/2016-08-17_57b4216356ac4.jpg) ### 3.3.2偏移量的分析 log_t有各種偏移量,例如:max_buf_free、buf_free、flush_end_offset、buf_next_to_write等。偏移和LSN不一樣,偏移量是相對redo log buf其實位置的絕對偏移量,LSN是整個日志系統的序號。 max_buf_free ? ? ? ?寫入日志是不能超過的偏移位置,如果超過,將強制redo log buf寫入磁盤 buf_free ? ? ?? ? ? 當前日志可以寫的偏移位置 ?buf_next_to_write ? 下一次redo log buf數據寫盤的數據起始偏移,在所有刷盤IO完成后,其值和?flush_end_offset是一致的。 flush_end_offset ? ?本次刷盤的數據末尾的偏移量,相當于刷盤時的buf_free,當flush_end_offset 超過max_buf_free的一半時會將未寫入的數據移到 ? ? ? ? ? ? ? ? ? ? ? redobuffer的最前面,這時buf_free和buf_next_to_write都將做調整 大小關系圖如下: ![](https://box.kancloud.cn/2016-08-17_57b4216388ade.jpg) ### 3.4內存結構關系圖 ![](https://box.kancloud.cn/2016-08-17_57b421639c5b1.jpg) ## 4.日志寫入和日志保護機制 innodb有四種日志刷盤行為,分別是異步redo log buffer刷盤、同步redo log buffer刷盤、異步建立checkpoint刷盤和同步建立checkpoint刷盤。在innodb中,刷盤行為是非常耗磁盤IO的,innodb對刷盤做了一套非常完善的策略。 ### 4.1重做日志刷盤選項 在innodb引擎中有個全局變量srv_flush_log_at_trx_commit,這個全局變量是控制flushdisk的策略,也就是確定調不調用fsync這個函數,什么時候掉這個函數。這個變量有3個值。這三個值的解釋如下: 0 ? ? 每隔1秒由MasterThread控制重做日志模塊調用log_flush_to_disk來刷盤,好處是提高了效率,壞處是1秒內如果數據庫崩潰,日志和數據會丟失。 1 ? ? 每次寫入重做日志后,都調用fsync來進行日志寫入磁盤。好處是每次日志都寫入了磁盤,數據可靠性大大提高,壞處是每次調用fsync會產生大量的磁盤IO,影響數據庫性能。 2 ? ? 每次寫入重做日志后,都將日志寫入日志文件的page cache。這種情況如果物理機崩潰后,所有的日志都將丟失。 ### 4.2日志刷盤保護 由于重做日志是一個組內多文件重復寫的一個過程,那么意味日志如果不及時寫盤和創建checkpoint,就有可能會產生日志覆蓋,這是一個我們不愿意看到的。在innodb定義了一個日志保護機制,在存儲引擎會定時調用log_check_margins日志函數來檢查保護機制。簡單介紹如下: 引入三個變量 buf_age、checkpoint_age和日志空間大小. ? ?? ? ? ? ? ? ?buf_age = lsn -oldest_lsn; ? ? ? ? ?checkpoint_age =lsn -?last_checkpoint_lsn; ? ? ? ? 日志空間大小 = 重做日志組能存儲日志的字節數(通過log_group_get_capacity獲得); 當buf_age >=日志空間大小的7/8時,重做日志系統會將red log buffer進行異步數據刷盤,這個時候因為是異步的,不會造成數據操作阻塞。 當buf_age >=日志空間大小的15/16時,重做日志系統會將redlog buffer進行同步數據刷盤,這個時候會調用fsync函數,數據庫的操作會進行阻塞。 ? ??? 當 checkpoint_age >=日志空間大小的31/32時,日志系統將進行異步創建checkpoint,數據庫的操作不會阻塞。 當?checkpoint_age == 日志空間大小時,日志系統將進行同步創建checkpoint,大量的表空間臟頁和log文件臟頁同步刷入磁盤,會產生大量的磁盤IO操作。數據庫操作會堵塞。整個數據庫事務會掛起。 ## 5.總結 ????? Innodb的重做日志系統是相當完備的,它為數據的持久化做了很多細微的考慮,它效率直接影響MySQL的寫效率,所以我們深入理解了它便以我們去優化它,尤其是在大量數據刷盤的時候。假設數據庫的受理的事務速度大于磁盤IO的刷入速度,一定會出現同步建立checkpoint操作,這樣數據庫是堵塞的,整個數據庫都在都在進行臟頁刷盤。避免這樣的問題發生就是增加IO能力,用多磁盤分散IO壓力。也可以考慮SSD這讀寫速度很高的存儲介質來做優化。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看