mysql 源代码16384_MySQL源码:Innobase文件系统管理
前言:很久沒有寫一些東西了,這次把一些以前寫好的貼上來,可能其中有些不對或者不準確的地方請朋友指正,這里先謝謝大家了。2012-5-13 by whuai QQ:329570985 歡迎指正!
1.表空間文件
Innodb存儲引擎在存儲設計上模仿了Oracle的存儲結構,其數據是按照表空間進行存儲的,默認情況下,在Innodb存儲引擎會初始化一個名為ibdata1的表空間文件,同時這個文件會存儲所有表的數據,包括系統表SYS_TABLES、SYS_COLUMNS、SYS_INDEXES、SYS_FIELDS等。
Innodb存儲引擎的設計很靈活,可以通過參數innodb_file_per_table來設置每一個表都對應一個自己的獨立表空間文件,而不是存儲到公共的ibdata1文件中,獨立的表空間文件中只存儲對應表的B樹數據、索引和插入緩沖等信息,其余信息還是存儲在默認表空間中的。
Innodb存儲引擎的表空間文件的組織結構也是通過段、簇、頁面構成的,下面分別做介紹。
1.段
段是表空間文件中的主要組織結構,它是物理上的管理物理空間的一個邏輯概念,它是構成索引、表、回滾段的基本元素,創建一個索引(B樹)時會同時創建兩個段,分別是內節點段和葉子段,內節點段用來管理(存儲)B樹中非葉子節點(頁面)的數據,葉子段用來管理(存儲)B樹中葉子節點的數據。
2.簇
簇是構成段的基本元素,一個段是由若干個簇構成的,一個簇是物理上連續分配的一段空間,每一個段至少會有一個簇,在創建一個段的時候會創建一個默認的簇,如果數據在存儲時一個簇已經不足以放得到更多的數據,此時需要從這個段中分配一個新的簇,來存放新的數據,段對多個簇的管理后面會做更詳細的介紹。
3.頁面
頁面是數據庫中文件管理的最小無素,也是文件中空間分配的最小單位,也是構成簇的基本元素,一個簇中可以包括多個頁面(默認為64個頁面),這個頁面數通常被叫做“簇的大小”,這些頁面都是歸這個簇管理的,這些頁面在邏輯上(頁面號都是從小到大連續的)及物理上都是連續的,在向表中插入數據的時如果一個頁面已經被寫完,則系統會從當前簇中分配一個新的空閑頁面出來使用,如果當前簇中的64個頁面都被分配完,系統會從當前頁面所在段中分配一個新的簇,然后再從這個簇中分配一個新的頁面都使用,依此類推。更簡單的說,表空間文件就是被劃分成相等長度的塊,每一個塊就是一個頁面,一個頁面默認為16KB。
下面是段、簇、頁面之間的一個簡單的關系圖:
圖1.1段、簇及頁面的關系
2.段、簇、頁組織結構
上面已經提到,段是由多個簇組成,簇是由默認64個頁面組成,但在具體實現上它們是如何組織,如果實現的呢?下面就主要從innodb的源代碼角度來討論它們的組織方式。
一個表空間可以有多個文件,每個文件都有各自的編號,那么創建一個表空間都至少有一個文件,這個文件被稱為“0號文件”,上面已經提到,一個文件是被切割為等長(默認16KB)的塊,這個塊通常被稱為頁面,那么在“0號文件”的第一個頁面(page_no為0)中存儲了這個表空間中所有的段簇頁管理的入口,從這個頁面偏移為FIL_PAGE_DATA(38)的位置開始存儲了表空間描述信息,描述信息中包括如下主要信息:
FSP_SPACE_ID:當前表空間ID號,每一個表空間都有一個唯一的ID號,在創建時分配。
FSP_SIZE:當前表空間總的頁面數,一個表空間可以有多個文件,這個值表示這個表空間中所有文件按照頁面(默認16KB)大小劃分的頁面數。
FSP_FREE:表空間中所有的段中的簇都是由表空間統一管理的,這個地址是一個鏈表頭指針,表空間中所有的空閑簇都以鏈表的形式存儲到這個鏈表中。
FSP_FREE_FRAG:一個簇可以管很多頁面(默認值64個頁),如果這個簇中已經有被使用(分配)的頁面,則這個簇就被稱為半滿簇,那么這個地址就是存儲所有半滿簇的鏈表頭指針。
FSP_FULL_FRAG:根據上面已經敘述過的,如果一個簇中所有的頁面都已經被分配(使用)了,則這個簇就被稱為滿簇,這個地址就是用來存儲所有滿簇的鏈表頭指針。
FSP_FRAG_N_USED:這個值表示上面FSP_FREE_FRAG鏈表中,所有已經被使用過的頁面數,在分配頁面時,每從FSP_FREE_FRAG鏈表中分配一個空閑頁,這個值都會加1并寫入到文件中。
FSP_SEG_ID:在表空間中,每一個段都有一個唯一分配的ID號,這個值表示的是下一個段的ID號,在每次使用之后這個值都會自加,這樣保證所有的ID號都是不相同的。
FSP_SEG_INODES_FULL:這里需要先介紹一下Inode,Inode是用來管理一個段的,簡單說,一個Inode就是代表一個數據段。Inode可以說是一個結構體,它也是像上面一樣,按順序存儲到Inode頁面中,一個Inode頁面可以存儲多個Inode節點。如果頁面中所有的空間都用來存放已經使用的Inode,則這個頁面就稱為滿Inode頁面,否則為半滿Inode頁面。FSP_SEG_INODES_FULL就是用來存儲所有的滿Inode頁面的鏈表頭指針。
FSP_SEG_INODES_FREE:用來存儲所有的半滿Inode頁面的鏈表頭指針。
Inode節點用來管理一個段,一個Inode中包括以下內容:
FSEG_ID:這個表示這個段的ID號,在創建時唯一分配。
FSEG_NOT_FULL:一個段管理很多簇,這些簇都是屬于這個段的,這個地址是用來存儲所有半滿簇的鏈表頭指針。
FSEG_NOT_FULL_N_USED:這個地址用來存儲上面半滿簇鏈表中所有已經使用的頁面總數。
FSEG_FREE:這個地址用來存儲所有空閑簇的鏈表頭指針。
FSEG_FULL:這個地址用來存儲所有滿簇的鏈表頭指針。
從上面敘述中知道,表空間控制信息中有滿簇鏈表,半滿簇鏈表,空閑簇鏈表,而段的Inode信息中也有這些信息,這兩個其實是不同的,表空間中的鏈表管理的是整個表空間中所有的簇,包括滿簇、半滿簇及空閑簇,而段的Inode信息中管理的是屬于自己段中的滿簇,半滿簇及空閑簇。當段新申請一個簇時,如果段上面沒有空閑的簇,此時它會從表空間的簇鏈表中找,找到后從相應鏈表中摘下來掛到段空閑簇鏈表中。
上面一直提到的簇,它是一個段的組成元素,段通過三個鏈表將不同狀態的簇管理起來,鏈表都是雙向鏈表,在段中,鏈表頭分別是FSEG_FREE、FSEG_FULL、FSEG_NOT_FULL,鏈表節點就是簇,每個簇上面都有向前指針和向后指針。每一個簇通過一個簇描述符來表示,簇是實際的物理存儲空間,簇描述符用來管理一個簇,簇描述符也是一個結構體,其內容如下:
XDES_ID:這個值表示這個簇所屬的段的ID號。
XDES_FLST_NODE:這個地址用來存儲簇的鏈表指針,包括向前指針和向后指針,每一個指針都是一個頁面地址,包括page、boffset,page表示這個簇描述符在文件中的哪一個頁面,它是一個頁面號,boffset表示簇描述符在page頁面中的偏移地址,表示從這個位置開始就是這個簇的描述符地址。
XDES_STATE:表示當前這個簇的狀態,狀態包括:XDES_FREE(空閑簇)、XDES_FREE_FRAG(半滿簇)、XDES_FULL_FRAG(滿簇)、XDES_FSEG(屬于一個段)。
XDES_BITMAP:這個地址存儲的是一個位圖,innodb通過這個來管理一個簇中所有頁面的使用情況,每一個頁面用兩個位來表示,簇大小FSP_EXTENT_SIZE表示的是一個簇的頁面數,默認值為64,所以XDES_BITMAP占用的總長度為64*2/8=16個字節,用兩個字節來表示一個頁面,第一個位表示這個頁面是否使用,第二個位現在還沒有使用,所以一個簇描述符的大小為XDES_ID(8)+ XDES_FLST_NODE(12)+ XDES_STATE(4)+16(XDES_BITMAP的大小)=40個字節。
簇描述符是存儲在簇描述頁面中的,在innodb中,一個簇描述頁面默認要管理16384(UNIV_PAGE_SIZE)個頁面,簇大小默認為(FSP_EXTENT_SIZE)64個,而一個簇描述符的大小為40B,所以一個簇描述頁面中可以描述的簇的個數為(UNIV_PAGE_SIZE-頁面頭長度)/ 40,但一般情況下,都不會將簇描述頁面存儲滿,因為在一個表空間中,簇描述頁面的存儲是每隔16384個頁面就有一個是簇描述頁面,所以簇描述頁面中只需要描述16384個頁面即可,每個簇大小為64個頁面,所以需要存儲的簇描述符個數為16384/64=256個,所以在頁面大小為16384中,其實只用了256*40=10240B的空間,其它的空間都是空閑的。
圖1.2表空間簇組織結構
從圖1.2中可以看出,一個表空間文件以簇為單位被分隔開,同時每16384(默認值)個頁面又用一個簇描述頁面來描述,在每16384個頁面的分隔中,第一個頁面都用來做簇描述頁面,用來描述后面的16384-64=16320個頁面,那么第一個簇(其實是第一個頁面)它只是用來做簇描述頁面的,它沒有被加入到表空間的簇鏈表中,也沒有被加入到段的簇鏈表中,那么第一個簇的后面63個頁面是被空出來的,是未被使用的,真正的空間使用是從第二個簇開始的,直到最后一個簇,所以簇描述頁面中實際上只有255個簇描述符。
從上面可以看出來,簇描述頁面中存儲的簇描述符上面的信息沒有體現它管理的頁面是哪些,它只有一個位圖用來表示它管理的64個頁面是否已經被使用,但是從圖1.2中發現,每16384個頁面就有一個簇描述頁,所以第一個描述頁的頁號為0,第二個為16384,依此類推;同時一個描述頁面中所有的描述符描述的頁面都是相隔64連續,所以只要知道一個描述符的位置,就可以計算出這個描述符所管理的頁面號的范圍,所以在描述符中是不需要存儲它的頁面信息的,通過描述符的地址即可得到。
計算方法概括如下:根據描述符的地址得到描述頁的頁面號describe_page_no,然后再根據其地址得到這個描述符在描述頁中的序號index,然后管理的范圍scope為:scope >= describe_page_no + index * FSP_EXTENT_SIZE同時scope < describe_page_no + (index + 1)* FSP_EXTENT_SIZE。
圖1.3段簇頁組織結構
圖1.3描述了表空間、INODE頁面、INODE、段、簇、頁面之間的關系,也是innodb文件系統管理架構圖。
下面簡單敘述一下創建一個段的過程,用來說明文件管理的實現過程。
1.根據表空間ID號得到表空間頭信息;
2.從得到的表空間頭中分配一個Inode,首先判斷FSP_SEG_INODES_FREE鏈表中是否還有空閑INODE的INODE頁面,如果有則從頁面的數據存儲位置開始掃描,因為每一個INODE的大小是固定的,所以掃描的步長是固定的,找到每一個INODE后判斷INODE描述符中的FSEG_ID是否為0,如果是則沒有使用,否則已經使用過了,找到第一個為0的則返回,說明已經找到了合適的INODE,如果找到后發現這個INODE是這個頁最后一個INODE,則將這個頁面從FSP_SEG_INODES_FREE鏈表中摘下來,同時將這個頁面插入到FSP_SEG_INODES_FULL鏈表中;如果FSP_SEG_INODES_FREE鏈表中沒有空閑的INODE頁面,則需要重新分配一個INODE頁面,分配后將所有的INODE描述符中的FSEG_ID置為0,表示未使用,然后將這個頁面鏈接到FSP_SEG_INODES_FREE鏈表中,然后直接從這個頁面中分配一個空閑的INODE,過程如上所述;
3.給新分配的INODE指定SEG_ID號,這個ID號要從表空間頭的FSP_SEG_ID取出來,這個ID號就是新段的ID號,然后將這個ID號寫入到INODE的FSEG_ID中,同時要更新FSP_SEG_ID中的值,更新為ID+1,表示下一個段的ID號;
4.初始化這個INODE信息,將偏移FSEG_NOT_FULL_N_USED處的值置為0;初始化鏈表FSEG_FREE、FSEG_NOT_FULL、FSEG_FULL;
5.從這個段中分配一個頁面,分配頁面時首先找到表空間頭上的半滿簇鏈表FSP_FREE_FRAG,然后從鏈表中找一個簇描述符,找到簇描述符之后從它的XDES_BITMAP中找一個狀態為XDES_FREE_BIT的頁面,返回一個0~63的下標index,再根據簇描述符計算得到這個簇管理的64個頁面的首頁號page,然后申請到的真正頁面號就是page+index,計算方法是首先得到這個描述頁的頁面號descr_page,然后再得到這個簇描述符在簇描述頁面中的序號seq_no,那么簇描述的首頁為page=descr_page + seq_no * 64。
6.分配好頁面之后,通過系統緩存得到頁面號為page+index的頁面,這個頁面就是這個段的首頁面,在一個段的首頁上,需要記錄這個段對應的INODE的位置,INODE的位置存儲在頁面頭中,分別是FSEG_HDR_OFFSET(INODE在INODE頁面中的偏移)、FSEG_HDR_PAGE_NO(INODE所在的INODE頁面號)、FSEG_HDR_SPACE(INODE所在的表空間號)。
7.到此為止,一個段就分配完成。以后如果需要在這個段中分配空間,只要找到其首頁,然后找到對應的INODE即可分配空間。
總結
以上是生活随笔為你收集整理的mysql 源代码16384_MySQL源码:Innobase文件系统管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php mysql 白屏_apache+
- 下一篇: mysql 8 centos_CentO