表就是關于特定實體的數據集合,這也是關系型數據庫模型的核心麻蹋。
在InnoDB存儲引擎表中跛溉,每張表都有個主鍵(Primary Key),如果在創(chuàng)建表時沒有顯式地定義主鍵,則InnoDB存儲引擎會按如下方式選擇或創(chuàng)建主鍵:
?首先判斷表中是否有非空的唯一索引(Unique NOT NULL)芳室,如果有专肪,則該列即為主鍵。
?如果不符合上述條件堪侯,InnoDB存儲引擎自動創(chuàng)建一個6字節(jié)大小的指針嚎尤。
當表中有多個非空唯一索引時,InnoDB存儲引擎將選擇建表時第一個定義的非空唯一索引為主鍵伍宦。這里需要非常注意的是芽死,主鍵的選擇根據的是定義索引的順序,而不是建表時列的順序
_rowid 可以顯示表的主鍵
從InnoDB存儲引擎的邏輯存儲結構看次洼,所有數據都被邏輯地存放在一個空間中关贵,稱之為表空間(tablespace)。
表空間又由段(segment)卖毁、區(qū)(extent)揖曾、頁(page)組成。頁在一些文檔中有時也稱為塊(block)
如果啟用了innodb_file_per_table的參數亥啦,需要注意的是每張表的表空間內存放的只是數據炭剪、索引和插入緩沖Bitmap頁
其他類的數據,如回滾(undo)信息翔脱,插入緩沖索引頁奴拦、系統(tǒng)事務信息,二次寫緩沖(Double write buffer)等還是存放在原來的共享表空間內碍侦。
插入緩沖Bitmap 插入緩沖頁粱坤?(什么區(qū)別呀)
共享表空間還包含有 undo信息,執(zhí)行rollback后并不會收縮表空間瓷产,會自動判斷這些undo信息是否還需要站玄,如果不需要,則會將這些空間標記為可用空間濒旦,供下次undo使用株旷。(每 10 秒的 full purge)
段 segment
表空間是由各個段組成的,常見的段有數據段尔邓、索引段晾剖、回滾段等。
因為前面已經介紹過了InnoDB存儲引擎表是索引組織的(index organized)梯嗽,因此數據即索引齿尽,索引即數據。
數據段即為B+樹的葉子節(jié)點(Leaf node segment)
索引段即為B+樹的非索引節(jié)點(Non-leaf node segment)
回滾段較為特殊灯节,將會在后面的章節(jié)進行單獨的介紹循头。數據行基于聚集索引鍵按順序存儲绵估。
非聚集索引不影響數據行的順序
索引組織表(Index Organizied Table)
- 索引組織表(index organized table, IOT)就是存儲在一個索引結構中的表。存儲在堆中的表是無組織的(也就是說卡骂,只要有可用的空間国裳,數據可以放在任何地方),IOT中的數據則按主鍵存儲和排序全跨。對你的應用來說缝左,IOT表和一個“常規(guī)”表并無二致。
- 索引組織表的數據按主鍵排序手段被存儲在B-樹索引中浓若,除了存儲主鍵列值外還存儲非鍵列的值渺杉。普通索引只存儲索引列,而索引組織表則存儲表的所有列的值七嫌。
- 索引組織表一般適應于靜態(tài)表少办,且查詢多以主鍵列苞慢。當表的大部分列當作主鍵列時诵原,且表相對靜態(tài),比較適合創(chuàng)建索引組織表M旆拧(8i以上)
既然它屬于表绍赛,那么它當然也有建立索引的需求。由于它的索引的結構辑畦,比如說由于索引葉節(jié)點的分裂吗蚌,行所在塊可能會發(fā)生改變,因而建立在IOT上的索引和一般的索引的最大區(qū)別是它存的是IOT的行的邏輯地址纯出,也就是UROWID蚯妇,oracle用這個邏輯rowid來猜這個行所在的塊,如果猜到了暂筝,那么這個urowid是正確的箩言,否則它從這個地址向下遍歷來找這條記錄。
IOT表的rowid是邏輯上的焕襟,因為IOT表中的行的位置是在不斷變化的(例如插入新的行陨收,有可能帶來其它行的位置移動)
IOT有什么意義呢?使用堆組織表時鸵赖,我們必須為表和表主鍵上的索引分別留出空間务漩。而IOT不存在主鍵的空間開銷,因為索引就是數據它褪,數據就是索引饵骨,二者已經合二為一。但是茫打,IOT帶來的好處并不止于節(jié)約了磁盤空間的占用居触,更重要的是大幅度降低了I/O,減少了訪問緩沖區(qū)緩存(盡管從緩沖區(qū)緩存獲取數據比從硬盤讀要快得多镐确,但緩沖區(qū)緩存并不免費,而且也絕對不是廉價的饼煞。每個緩沖區(qū)緩存獲取都需要緩沖區(qū)緩存的多個閂源葫,而閂是串行化設備,會限制應用的擴展能力)
IOT適用的場合有:
1砖瞧、完全由主鍵組成的表息堂。這樣的表如果采用堆組織表,則表本身完全是多余的開銷块促,因為所有的數據全部同樣也保存在索引里荣堰,此時,堆表是沒用的竭翠。
2振坚、代碼查找表。如果你只會通過一個主鍵來訪問一個表斋扰,這個表就非常適合實現為IOT.
3渡八、如果你想保證數據存儲在某個位置上,或者希望數據以某種特定的順序物理存儲传货,IOT就是一種合適的結構屎鳍。
IOT提供如下的好處:
提高緩沖區(qū)緩存效率,因為給定查詢在緩存中需要的塊更少问裕。
減少緩沖區(qū)緩存訪問逮壁,這會改善可擴縮性。
獲取數據的工作總量更少粮宛,因為獲取數據更快窥淆。
每個查詢完成的物理I/O更少,因為對于任何給定的查詢巍杈,需要的塊更少忧饭,而且對地址記錄的一個物理 I/O 很可能可以獲取所有地址(而不只是其中一個地址,但堆表實現就只是獲取一個地址)
如果經常在一個主鍵或惟一鍵上使用BETWEEN 查詢也是如此秉氧,因為相近的記錄存在一起眷昆,查詢時引入的邏輯IO和物理IO都會更少。
堆表 heap table
堆表(heap table)數據插入時時存儲位置是隨機的汁咏,主要是數據庫內部塊的空閑情況決定亚斋,獲取數據是按照命中率計算,全表掃表時不見得先插入的數據先查到攘滩。
堆表(heap table) 就是一般的表帅刊,獲取表中的數據是按命中率來得到的。沒有明確的先后之分漂问,在進行全表掃描的時候赖瞒,并不是先插入的數據就先獲取女揭。數據的存放也是隨機的,當然根據可用空閑的空間來決定栏饮。
區(qū) extent
區(qū)是由連續(xù)頁組成的空間吧兔,在任何情況下每個區(qū)的大小都為1MB。為了保證區(qū)中頁的連續(xù)性袍嬉,InnoDB存儲引擎一次從磁盤申請4~5個區(qū)境蔼。在默認情況下,InnoDB存儲引擎頁的大小為16KB伺通,即一個區(qū)中一共有64個連續(xù)的頁箍土。
mysql> INSERT INTO t SELECT NULL,REPEAT('a',7000);
新創(chuàng)建表的默認大小是 96KB
在用戶啟用了參數innodb_file_per_talbe后,創(chuàng)建的表默認大小是96KB罐监。區(qū)中是64個連續(xù)的頁吴藻,創(chuàng)建的表的大小至少是1MB才對啊弓柱?其實這是因為在每個段開始時沟堡,先用32個頁大小的碎片頁(fragment page)來存放數據,在使用完這些頁之后才是64個連續(xù)頁的申請吆你。這樣做的目的是弦叶,對于一些小表俊犯,或者是undo這類的段妇多,可以在開始時申請較少的空間,節(jié)省磁盤容量的開銷燕侠。
頁 page
在InnoDB存儲引擎中者祖,常見的頁類型有:
?數據頁(B-tree Node)
?undo頁(undo Log Page)
?系統(tǒng)頁(System Page)
?事務數據頁(Transaction system Page)
?插入緩沖位圖頁(Insert Buffer Bitmap)
?插入緩沖空閑列表頁(Insert Buffer Free List)
?未壓縮的二進制大對象頁(Uncompressed BLOB Page)
?壓縮的二進制大對象頁(compressed BLOB Page)
InnoDB存儲引擎是面向列的(row-oriented),也就說數據是按行進行存放的绢彤。
data page頁默認16kb,當有新索引記錄寫入時,會預留1/16(1kb)空閑空間用于以后的索引記錄寫入
InnoDB數據頁由以下7個部分組成七问。
?File Header(文件頭)
?Page Header(頁頭)
?Infimun和Supremum Records
?User Records(用戶記錄,即行記錄)
?Free Space(空閑空間)
?Page Directory(頁目錄)
?File Trailer(文件結尾信息)
File Header
記錄頁的一些頭信息
Page Header
記錄數據頁的狀態(tài)信息
Infimum和Supremum Record
每個數據頁中有兩個虛擬的行記錄茫舶,用來限定記錄的邊界械巡。Infimum記錄是比該頁中任何主鍵值都要小的值,Supremum指比任何可能大的值還要大的值饶氏。這兩個值在頁創(chuàng)建時被建立讥耗,并且在任何情況下不會被刪除。
User Record 和 Free Space
User Record就是之前討論過的部分疹启,即實際存儲行記錄的內容古程。再次強調,InnoDB存儲引擎表總是B+樹索引組織的喊崖。
Free Space很明顯指的就是空閑空間挣磨,同樣也是個鏈表數據結構雇逞。在一條記錄被刪除后,該空間會被加入到空閑鏈表中茁裙。
Page Directory
存放了記錄的相對位置(注意塘砸,這里存放的是頁相對位置,而不是偏移量)晤锥,有些時候這些記錄指針稱為Slots(槽)或目錄槽(Directory Slots)谣蠢。
由于在InnoDB存儲引擎中Page Direcotry是稀疏目錄,二叉查找的結果只是一個粗略的結果查近,因此InnoDB存儲引擎必須通過recorder header中的next_record來繼續(xù)查找相關記錄眉踱。同時,Page Directory很好地解釋了recorder header中的n_owned值的含義霜威,因為這些記錄并不包括在Page Directory中谈喳。
需要牢記的是,B+樹索引本身并不能找到具體的一條記錄戈泼,能找到只是該記錄所在的頁婿禽。數據庫把頁載入到內存,然后通過Page Directory再進行二叉查找大猛。只不過二叉查找的時間復雜度很低扭倾,同時在內存中的查找很快,因此通常忽略這部分查找所用的時間挽绩。
File Trailer
為了檢測頁是否已經完整地寫入磁盤(如可能發(fā)生的寫入過程中磁盤損壞膛壹、機器關機等),InnoDB存儲引擎的頁中設置了File Trailer部分唉堪。
File Trailer只有一個FIL_PAGE_END_LSN部分模聋,占用8字節(jié)。前4字節(jié)代表該頁的checksum值唠亚,最后4字節(jié)和File Header中的FIL_PAGE_LSN相同链方。將這兩個值與File Header中的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN值進行比較,看是否一致(checksum的比較需要通過InnoDB的checksum函數來進行比較灶搜,不是簡單的等值比較)祟蚀,以此來保證頁的完整性(not corrupted)。
約束
對于InnoDB存儲引擎本身而言割卖,提供了以下幾種約束:
?Primary Key
?Unique Key
?Foreign Key
?Default
?NOT NULL
約束更是一個邏輯的概念前酿,用來保證數據的完整性,
索引是一個數據結構究珊,既有邏輯上的概念薪者,在數據庫中還代表著物理存儲的方式。
外鍵約束
對于數據的導入操作時剿涮,外鍵往往導致在外鍵約束的檢查上花費大量時間言津。因為MySQL數據庫的外鍵是即時檢查的攻人,所以對導入的每一行都會進行外鍵檢查。但是用戶可以在導入過程中忽視外鍵的檢查悬槽。
分區(qū)表
就訪問數據庫的應用而言怀吻,從邏輯上講,只有一個表或一個索引初婆,但是在物理上這個表或索引可能由數十個物理分區(qū)組成蓬坡。每個分區(qū)都是獨立的對象,可以獨自處理磅叛,也可以作為一個更大對象的一部分進行處理屑咳。
MySQL數據庫的分區(qū)是局部分區(qū)索引,一個分區(qū)中既存放了數據又存放了索引弊琴。而全局分區(qū)是指兆龙,數據存放在各個分區(qū)中,但是所有數據的索引放在一個對象中敲董。目前紫皇,MySQL數據庫還不支持全局分區(qū)。
對表空間的概念好模糊啊腋寨,怎么看都看不明白
段segment聪铺、 區(qū)extent、 頁page(block)
然后數據頁的結構%>_<%
約束
視圖
分區(qū)
看得比較粗糙萄窜,感覺實用性不怎么高呀铃剔。
Jeremy Cole - InnoDB
jeremycole/innodb_ruby
innodb_ruby工具分析innob表空間文件Part-01
innodb_ruby工具分析innob表空間文件Part-02