《MySQL技術內幕-InnoDB存儲引擎》整理3-表

一、索引組織表

在InnoDB存儲引擎中,表都是根據主鍵順序組織存放的,這種存儲方式稱爲索引組織表。如果創建表時沒有顯示地指定主鍵,InnoDB引擎會按照下述規則選擇或創建主鍵:判讀表中是否有非空的唯一索引,有即爲主鍵如果有多個會選擇創建表時第一個定義的非空唯一索引(Not Null+Unqiue Key);如果沒有InnoDB引擎會自動創建一個6字節大小的指針

使用_rowid字段可以查看錶的主鍵,但只適用於單個列爲主鍵的情況

二、InnoDB邏輯存儲結構

從InnoDB存儲引擎的邏輯存儲結構看,所有的數據都被存放在一個稱爲表空間的空間中,表空間又被稱爲段、頁。

1、表空間

表空間可以看作是InnoDB存儲引擎邏輯結構的最高層,所有的數據都存放在表空間中。上一章提到的innodb_file_per_table參數,開啓後每張表內的數據都可以單獨存放到一個表空間內,但是隻包含數據、索引和插入緩衝頁,其它類似回滾、插入緩衝索引頁、系統事務信息、二次寫緩衝等信息還是存放在共享表空間中。

2、段

表空間是由各個段組成的,常見的段由數據段、索引段、回滾段等。InnoDB存儲引擎表是索引組織的,因此數據即索引、索引即數據。數據段即爲B+樹上的葉子節點,索引段即爲B+樹上的非葉子節點。

3、區

區是連續頁組成的空間,在任何情況下每個區的大小都是1MB,爲了保證區中頁的連續性,InnoDB引擎一次從磁盤申請4~5個區,在默認情況下,InnoDB存儲引擎頁的大小爲16KB,即一個區中一共有64個連續的頁。當然也可以使用參數KEY_BLOCK_SIZE設置頁的大小爲2K、4K或8K,與之對應的頁的數量就是512、256和128

在每個段開始時,先用32個頁大小的破碎頁來存放數據,在使用完這些頁後會進行64個連續頁的申請,以節省磁盤容量的開銷

4、頁

頁(也稱塊)是InnoDB磁盤管理的最小單位,其默認大小爲16KB,可以通過參數innodb_page_size將頁的大小設置爲4K、8K、16K,但是設置完成後,所有表的大小都會變爲設置的值,不可以再次進行修改,除非產生新的庫。常見的頁有:數據頁、undo頁、系統頁、事務數據頁、插入緩衝位圖頁、插入緩衝空閒列表頁、未壓縮的二進制大對象頁、壓縮的二進制大對象頁

5、行

每個頁存放的行記錄也是有硬性規定的,最多允許存放16KB/2-200行記錄,約7992行記錄。

三、InnoDB行記錄格式

InnoDB存儲引擎提供了Compact和Redundant兩種格式來存放行記錄數

1、Compact行記錄格式

Commpact行記錄的設計目標是高效地存儲數據,簡單來說,一個頁中存放的行數據越多,其性能就越高。其存儲方式如下:

image-20210307195601886

其首部是一個非空變長字段長度列表,其是按照列的順序逆序放置的,若列的長度小於255字節,用1字節表示,若大於255字節,用2字節表示。變長字段的長度最大不可以超過2字節,這是因爲Mysql數據庫中VARCHAR類型的最大長度限制爲65535。

第二個部分是NULL標識位,指示該行數據中是否有NULL值,有則用1表示,所佔字節爲1字節。頭信息佔用5個字節,共40個bit,記錄如下信息:

image-20210307200426153

值得注意的是NULL除了佔有NULL標誌位後,不佔用任何空間。此外每行數據除了用戶定義的例外,還有兩個隱藏列,事務ID列和回滾指針列,分別佔用6字節和7字節大小。若InnoDB表沒有定義主鍵,每行還會增加一個6字節的rowid列

2、Redundant行記錄格式

Redudant格式是5.0版本之前InnoDB的行記錄方式,不同於Compant,Redundant行記錄格式的首部是一個字段長度偏移列表,同樣是按照列的順序逆序放置的,若列的長度小於255字節,用1字節表示,若大於255字節,用2字節表示。第二部分爲記錄頭信息,佔用6字節,如下:

image-20210307201324403

3、行溢出數據

InnoDB存儲引擎可以將一條記錄中的某些數據存儲在真的的數據頁之外。Mysql數據庫中的VARCHAR類型可以存放65535字節,但這裏指的是所有VARCHAR類型長度的總和。InnoDB存儲引擎頁的大小爲16kb,即16638字節,是如何存放下65535個字節的呢?

一般情況下,InnoDB存儲引擎的數據都是存放在頁類型爲B樹的節點中,當行溢出發生時,數據存放在頁類型爲Uncompress BLOB的二進制大對象頁中。

4、Compressed和Dynamic行記錄格式

InnoDB1.0.x版本開始引入了新的文件按格式,Compact和Redundant格式被稱爲Antelope文件格式,新的文件格式被稱爲Barracuda文件格式,該格式下有兩種新的行記錄格式:Compressed和Dynamic。這兩種新的格式對於存放在BLOB中的數據採用了完全的行溢出方式。

5、CHAR的行結構存儲

通常理解VARCHAR是存儲變長長度的字符類型,CHAR是存儲固定長度的字符類型。從Mysql4.1版本開始,CHAR(N)中的N指的是字符的長度,而不是字節的長度,因此對於不同的字符編碼,CHAR類型不再代表固定長度的字符串,因此CHAR在InnoDB中可以試爲變長字符類型。

四、InnoDB數據頁結構

InnoDB數據頁可以由以下7個部分組成:①文件頭②頁頭③Infimun和Supremum Records④用戶記錄即行記錄⑤空閒空間⑥頁目錄⑦文件結尾信息。其中文件頭、頁頭和文件結尾信息大小是固定的,分別爲38、56、8字節,用來標記該頁的一些信息;用戶記錄、空閒空間和頁目錄爲實際的行記錄存儲空間,它們的大小是動態的

1、File Header

用來記錄頁的一些頭信息,由8個部分組成,共佔38字節

image-20210307210215294

2、Page Header

用來記錄數據頁的狀態信息,由14個部分組成,共佔用56字節

3、Infimum和Supremum Record

InnoDB存儲引擎中,每個數據頁有兩個虛擬的行記錄,用來限定記錄的邊界。Infimum記錄是比該頁中任何主鍵值都要小的值,Supremum指任何可能達的值還要大的值,兩個值在也被創建時建立,並且在任何情況下都不會被刪除

4、User Recore和Free Space

User Record是實際存儲記錄的內容,Free Space指空閒空間,同樣也是整個鏈表數據結構,在一條記錄被刪除後該空間會被加入到空閒鏈表中。

5、Page Directory

頁目錄存放了記錄的相對位置,有時這些記錄指針稱爲槽或目錄槽,並不是每個記錄都有一個槽,InnoDB存儲引擎的槽是一個稀疏槽,即一個槽中可能包含多個記錄。槽中按照索引鍵值順序存放,可以利用二叉查找找到一個粗略的結果,再通過recorder header中的next_record來繼續查找相關記錄。

6、File Trailer

爲了檢測頁是否已經完整的寫入磁盤,InnoDB存儲引擎設置了File Trailer部分。默認配置下,InnoDB存儲引擎每次從磁盤讀取一個頁就檢測該頁的完整性。

五、約束

1、數據完整性

一般來說,數據完整性由以下三種形式:①實體完整性保證表中有一個主鍵;②用戶通過主鍵或唯一約束來保證實體的完整性;③用戶編寫一個觸發器來保證數據完整性

2、約束的創建和查找

約束的創建可以採用以下兩種方式:①表建立時就進行約束定義;②利用ALTER TABLE命令來進行創建約束

  • 對於唯一索引約束而言,可以通過Create Unique Index來創建,默認約束名和列名一樣,也可以認爲指定
  • 對於主鍵約束而言,其默認約束名爲Primary
  • 對於外鍵約束而言,其默認約束名會包含依賴的表以及列名信息

3、約束和索引的區別

約束是邏輯的概念,用來保證數據的完整性,索引是一個數據結構,即有邏輯上的概念,在數據庫中還代表着物理存儲的方式

4、對錯誤數據的約束

通過設置參數sql_mode的值爲Strict_trans_tables,數據庫會對輸入值的合法性進行約束,針對不同的錯誤,提示的錯誤內容也不同

5、Enum和Set約束

Mysql不支持傳統的Check約束,但是可以通過Enum和Set類型解決部分這樣的約束需求。但是對於傳統約束支持的連續值的範圍約束或更爲複雜的約束,Enum和Set約束無法進行有效的約束,只能通過觸發器來實現值域的約束

6、觸發器與約束

觸發器的作用是在執行Insert、Delete和Update之前或之後自動調用SQL或存儲過程,創建觸發器的命令是Create Trigger,只有具備Super權限的Mysql數據庫用戶纔可以執行這條命令。一個表最多可以建立6個觸發器,即新增、刪除、更新操作之前或之後,目前Mysql只支持For Each Row的觸發方式,即按每行記錄進行觸發

7、外鍵約束

InnoDB存儲引擎完整支持外鍵約束,一般來說被引用的爲父表,引用的表稱爲子表,外鍵定義時的On Delete和On Update表示在對父表進行Delete和Update操作時,對子表做的操作,可定義的子表操作如下:

  • Cascade:父表發生delete或update操作時,相應子表中的數據也進行delete或update操作
  • Set Null:父表發生delete或update操作時,相應子表中的數據被更新爲NULL值,但是該列需要被設定爲允許Null值
  • No Action:父表發生delete或update操作時,拋出錯誤,不允許這類操作發生
  • Restrict:父表發生delete或update操作時,拋出錯誤,不允許這類操作發生,如果定義外鍵時沒有指定On Delete或On Update,則使用該設定

No Action和Restrict的功能是相同的,這時因爲Mysql數據庫的外鍵約束都是即時檢查的,而非Oracle可以設定延時檢查。另外,InnoDB存儲引擎在外鍵建立時會自動地對該列加一個索引,可以很好的避免外鍵無索引導致的死鎖問題。

六、視圖

View是一個命名的虛表,視圖中的數據沒有實際的物理存儲

1、視圖的作用

視圖的主要用途就是作爲一個抽象層,程序本身只需要按照視圖定義來獲取或更新數據。一般稱可以進行更新操作的視圖爲可更新視圖,視圖定義中的With Check Option就是針對可更新視圖的,即可更新的值是否需要檢查,對於不滿足定義條件的插入數據,會拋出一個異常,不允許數據更新。

2、物化視圖

物化視圖表示根據基表實際存在的實表,其數據存儲在非易失的存儲設備上,它可以用於預先計算並保存多表的Join或聚集(Group By)等耗時較多的SQL操作結果,在MSSQL中,稱爲索引視圖。

在Oracle中物化視圖的創建方式有兩種:Build Immediate和Build Defreeed,默認爲前置,在創建物化視圖時生成數據,後者則根據需要生成數據。物化視圖的刷新是指在DML操作後,採用何種模式進行刷新:On Demand表示在用戶需要時進行刷新,On Commit表示在對基表進行DML操作提交時進行刷新。刷新的方式有四種:①FAST爲增量刷新,即只刷新上次刷新後的修改;②Complete指對整個物化視圖進行刷新;③Force指在刷新時判斷是否可以進行FAST刷新,不行則採用Complete刷新;④Nerver指不進行任何刷新

Mysql不支持物化視圖,但是可以通過觸發器實現類似的功能

七、分區表

1、分區概述

分區的過程是將一個表或索引分解爲更小、更可管理的部分,每個分區都是獨立的對象,可以獨立處理,也可以作爲一個更大的對象的一部分進行處理。Mysql數據庫支持水平分區,且分區是局部分區索引,即一個分區中即存放了數據又存放了索引。Mysql數據庫支持集中類型的分區:

  • Range分區:行數據基於屬於一個給定連續區間的列值被放入分區;
  • List分區:和Range分區類似,只是List分區面向的是離散的值;
  • Hash分區:根據用戶自定義的表達式的返回值來進行區分,返回值不能爲負數;
  • Key分區:根據Mysql數據庫提供的哈希函數進行分區

不論創建任何分區,如果表中存在主鍵或唯一索引時,分區列必須是唯一索引的一個組成部分;如果沒有指定主鍵或唯一索引,可以指定任何一個列爲分區列

2、分區類型

1、Range分區

Range分區主要用於日期列的分區,例如銷售記錄表可以按照年來分區存放對應年份的記錄。這樣做的好處是方便管理,並且可以加快部分查詢操作,啓用分區後,需要注意邊界值來編寫最優的SQL語句

2、List分區

List分區和Range分區非常相似,但是List分區的值是離散的,非連續的。插入多行數據遇到分區未定義的值時,MyISAM引擎會存儲之前的行數據,而InnoDB會將其視爲一個事務,不進行任何數據插入操作。

3、Hash分區

Hash分區的目的是將數據均勻地分佈到預先定義的各個分區中,保證各分區的數據數量大概一樣,Range和List分區需要明確給定一個列值或列值集合,而Hash分區中,Mysql自動完成這些工作,用戶只需要將要進行哈希分區的列值指定一個列值或表達式,以及指定被分區的表將要被分割成的分區數量

4、Key分區

Key分區和Hash分區相似,不同之處在於Hash分區使用用戶定義的函數進行分區,Key分區使用Mysql數據庫提供的函數進行分區

5、Columns分區

Range、List、Hash和Key分區的條件是數據必須是整型,如果不是整型需要通過函數將其轉換爲整型。從Mysql5.5版本開始支持Columns分區,它可以直接使用非整型的數據進行分區,分區根據類型直接比較而得,不需要轉換爲整型,它支持所有整型類型、日期類型、字符串類型。此外Range Column分區可以對多個列的值進行分區,對於之前的Range和List分區可以使用Range Columns和List Columns分區進行代替

3、子分區

子分區(subpartitioning)是在分區的基礎上在進行分區,有時也稱爲複合分區,Mysql允許在Range和List的分區上再進行Hash或Key子分區。子分區的建立需要注意:

  • 每個子分區的數量必須相同
  • 要在一個分區表的任何分區上使用SubPartition來明確定義任何子分區,就必須定義所有的子分區
  • 每個Subpartition子句必須包括子分區的一個名字
  • 子分區的名字必須是唯一的

4、分區中的Null值

Mysql數據庫允許對Null值進行分區,但是與其它數據庫不同。Mysql數據庫分區總是視NULL值小於任何一個非NULL值,因此對於不同的分區類型,其處理Null值的方法也不同

  • 對於Range分區,如果向分區列插入了Null值,Mysql數據庫會將該值放入最左邊的分區;
  • 對於List分區,使用Null值必須顯示地指出哪個分區中放入Null值,否則會報錯
  • 對於Hash和Key分區,任何分區函數都會將含有Null值的記錄返回爲0

5、分區的性能

數據庫應用分爲兩類,一類是OLTP(在線事務處理),如電子商務和網絡遊戲,一類是OLAP(在線分析處理),如數據倉庫和數據集市。對於OLAP應用,分區可以很好的提高查詢的性能,對於OLTP應用,分區需要非常小心,設計不好的表會帶來嚴重的性能問題。

6、在表和分區間交換數據

Mysql5.6開始支持Alter Table ... Exchange Partition語法,該語法允許分區或子分區中的數據與另一個非分區表中的數據進行交換。在使用時需要滿足以下條件:

  • 要交換的表需要和分區表有着相同的表結構,但是表不能含有分區;
  • 在非分區表中數據必須在交換的分區定義內;
  • 被交換的表中不能含有外鍵,或者其它表含有對該表的外鍵引用;
  • 用戶除了Alter、Insert和Create權限外還需要Drop權限;
  • 使用時不會觸發交換表和被交換表上的觸發器;
  • Auto_Increment列將被重置
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章