innodb學習筆記(一)  aio的使用

sanyu.lh發表於2020-10-04

innodb學習筆記(一)  aio的使用

      這裡先以mysql 5.7.26版本進行學習。

一. 四種IO執行緒及執行緒數量

      分為讀執行緒,寫執行緒,log io執行緒和buf io執行緒,重點在innobase/os/Osofile.cc這個檔案裡。

1.1 aio讀寫執行緒

      innodb可以配置aio執行緒的數量,在圖1-1某線上例項的my.cnf中可以看到讀執行緒和寫執行緒數量的配置,這裡一例讀寫執行緒數量各自為8。後文都以讀8寫8為例說明。

                                

                                            圖 1-1 某線上例項執行緒數量配置

      實際程式碼中srv0start.cc負責aio的初始化,這裡實際對應srv_n_read_io_threads和srv_n_write_io_threads兩個引數分別指定負責讀IO的執行緒數量和負責寫IO的執行緒數量;

      當然除了指定的讀執行緒和寫執行緒外,還有一個log io thread和ibuf io thread。所以實際的io執行緒總數srv_n_file_io_threads就是:

srv_n_file_io_threads = srv_n_read_io_threads + srv_n_write_io_threads + 2

      以上這些邏輯在innobase_start_or_create_for_mysql()初始化中可以看到。

      綜上,一個mysql例項中有:

  • 4種不同的IO執行緒;
  • n個read io執行緒,n個write io執行緒,一個log io執行緒,一個buf io執行緒;

二. aio管理結構

      核心是定義在os0file.cc中的class AIO結構,如下圖列出了其中所有的類成員和部分重要類方法。

                                    

                                                            圖 2-1 aio類

       其實全域性來看AIO結構是一個工廠,並不會有實體,該工廠可以根據引數分別建立read io,write io,log io和ibuf io這四種不同的AIO例項,這些例項被建立好後賦給相應的static AIO *指標,之後在db例項的整個執行過程中就可以通過諸如s_reads, s_writes這樣的static指標來呼叫相應的AIO例項。

2.1 AIO結構中重要的成員

  • m_mutex

對於wirte和read型別的AIO,會有多個執行緒來操作AIO例項,因此需要mutex進行互斥;

  • m_slots

一個AIO包含的所有的slot例項,儲存在該向量中,每個slot代表一個io請求,可能是讀,也可能是寫。slot的數量為相應的執行緒數量 n 乘以系統規定的該類執行緒允許的pending aio的數量,其中執行緒數量n在上一節已經給出,而該類執行緒允許pending aio的數量為硬編碼n * OS_AIO_N_PENDING_IOS_PER_THREAD為256;

對於read/write io型別的AIO,slot數量為執行緒數n * 256,這些slot需要均分成m_n_segments份,其實m_n_segments就是執行緒數量;

  • m_n_segments

所有的slot會被分隔成m_n_segments份,其實就是io執行緒數量,一個io執行緒可以以io_getevents等待在任一segment上,其實一個segment就對應一個io_ctx;可以推斷,針對某一種io執行緒,比如write io數量為8,那麼m_n_segments值就為8,slots被分隔成8個segment,每個segment包含的slot數量為256;從後續程式碼可以看到,每個io_ctx在被io_setup和io getevents時,引數nr和max_nr都為256;

                                                                            圖 2-2 slot的分割

實際上看起來分成多個segments的主要目的除了提前分配好m_n_segments個io_ctx外,可以把實體地址相近的io放入到同一segments中方便可能存在的io merge,如reserve_slot()中所示;

但是,reserve_slot()中選擇了一個segment後,io並不一定真的能放入這個segment中,只是從這個segment開始搜尋第一個未被佔用的slot而已;

  • m_not_full

條件變數,噹噹前AIO的slots從full變成not full時就釋放這個條件變數,表示有空餘的slot可以用來提交io請求了,如reserve_slot()中所示;

  • m_is_empty

條件變數,噹噹前AIO的被reserved的slot數量從0變成1時該條件會被hold,見reserve_slot();當reserved的slot數量從1變成0時會被釋放,見AIO::release();

從程式碼來看,只有s_write型別的AIO會存在對該條件變數的wait,主要是log file和db wirte file的flush的前置操作;

  • m_n_reserved

該AIO中被佔用的slot總數,維護這個數字主要是做為m_not_full和m_is_empty兩個條件變數操作的依據;

  • m_aio_ctx

n_segments個aio上下文組成的陣列,正好每個IO執行緒對應一個;

  • m_events

n_slots個aio_event組成的陣列,aio event完成後,io_getevents將io_event儲存的位置;

  • s_log/s_writes/s_reads/s_ibuf

四種不同型別io執行緒對應的AIO例項; 

2.2 Slot結構

      一個Slot結構代表一個io請求。Slot結構非常容易理解,該IO相關的一些資訊。

                                            

                                                                   圖 2-3 slot結構

三. AIO初始化

aio的初始化從os_aio_init,可以分為兩個範疇:

  1. AIO範疇:分別為各種io型別建立AIO例項,也就是呼叫AIO::create()函式,對於每個AIO例項,就是執行建構函式和init成員方法;
  2. linux aio範疇:對於每一個AIO例項內部,io_setup準備m_n_segment個aio_ctx,也就是每個io執行緒對應一個;每個aio_ctx的max_event值為該AIO的m_slots.size()除以m_n_segment,其實就是256;這些操作是在init_linux_native_aio()中完成;

以上兩個操作還包含了為AIO例項以及Slot例項的成員變數賦初始值。

                    

                                                                     圖 3-1 aio初始化函式呼叫

四. AIO執行緒邏輯

在程式碼中多處看到諸如# ifdef UNIV_PFS_THREAD這樣的巨集,這裡pfs是指performance schema instrumented,這是用於監控MySQL server在一個較低階別的執行過程中的資源消耗、資源等待等情況的一個模組,參考https://blog.csdn.net/qq_35068291/article/details/105921515

      AIO邏輯當然包括提交IO請求(submit)和IO收割(getevents)兩部分,實際上innodb裡的AIO邏輯主要是在收割上,前文所述的IO執行緒都是在做收割工作,所以我們先來看下收割邏輯。

4.1 收割

       AIO執行緒主函式是io_handler_thread,在innobase_start_or_create_for_mysql( )中可以看到create了srv_n_file_io_threads個AIO執行緒均是以io_handler_thread為執行緒主函式。                     

                                                                              圖 4-1 aio收割邏輯

      如圖4-1所示,其實主函式就是不斷迴圈呼叫fil_aio_wait函式,fil_aio_wait則負責實際的對segment中aio_ctx的io_getevents和善後工作。

      其中os_aio_handler是實際的收割動作,segment實際指定了aio_ctx,其他引數其實都是輸出型結果引數。

4.2 提交

linux_dispatch通過io_submit提交aio,層層回推,aio的submit入口是fil_io函式。

fil_io一個主要的使用場景是刷髒頁,邏輯比較複雜,準備後面專門分析,這裡畫了一個主要的邏輯圖。

https://www.processon.com/view/link/5f6602cee401fd2ad7eb852

   

相關文章