MySQL技術內幕:InnoDB存儲引擎讀書筆記(中)

第四章、表

  4.1、innodb存儲引擎表類型

  innodb表類似oracle的IOT表(索引聚集表-indexorganized table),在innodb表中每張表都會有一個主鍵,如果在創建表時沒有顯示的定義主鍵則innodb如按照如下方式選擇或者創建主鍵。

  首先表中是否有唯一非空索引(unique not null),如果有則該列即爲主鍵。

  不符合上述條件,innodb存儲引擎會自動創建一個6字節大小的指針,rowid().

  4.2、innodb邏輯存儲結構

  innodb的邏輯存儲單元由大到小分別是 tablespace,segment,extent,page(block)組成。

  4.2.1、表空間(tablespace)

  大部分內容和3.6.1章節相同,可以通過產生undo操作來驗證共享表空間存儲undo的信息,也可以通過py_innodb_page_info.py來查看錶空間文件中各頁的類型和數量。

  4.2.2、段(segment)

  常見的segment有數據段、索引段、回滾段。

  由於我們剛纔說過,innodb是索引聚集表,所以數據就是索引,索引就是數據,那麼數據段即是B+樹的頁節點(leaf node segment),索引段即爲B+樹的非索引節點(non-leaf node segment).innodb的segment是innodb自動完成的,不許要人工參與。

  4.2.3、區(extend)

  區是由64個連續的頁主成,每個頁大小爲16K,即每個區的大小爲(64*16K)=1MB,對於大的數據段,mysql每次最多可以申請4個區,以此保證數據的順序性能。

  對於innodb_file_per_table參數的特殊情況,開啓這個參數後,默認創建的表空間大小爲96K。 區是64個連續的頁,應該創建1M纔對啊?原因就是每個段開始時有32個頁大小的碎片頁(fragment page)來存放數據,當這些頁使用完成以後,纔是32+64個連續頁的申請。具體參見P75的實驗。

  4.2.4、頁(page)

  頁是innodb磁盤管理最小的單位,innodb每個頁的大小是16K。常見的頁類型有:

  數據頁 B-tree Node

  undo頁 Undo Log Page

  系統頁 System Page

  事務數據頁 Transaction system Page

  插入緩衝位圖頁 Insert Buffer Bitmap

  插入緩衝空閒列表頁 Insert Buffer free Bitmap

  未壓縮的二進制大對象頁 Uncompressed BLOB Page

  壓縮的二進制大對象頁 Compressed BLOB Page

  4.2.5、行

  innodb存儲引擎是面向行的(row-oriented),也就是說數據的存放按行進行存放。每個頁最多可以存放16K/2~200行,也就是8192和81.92個行。

  也有面向列的數據庫(column-orientied), mysql infobright就是面向列的,對於數據倉庫下的分析類sql語句和數據壓縮很有好處。

  4.3、innodb物理存儲結構

  innodb引擎由共享表空間,日誌文件(redo log),表結構定義文件組成。如果開啓了innodb_file_per_table,那每個表將獨立的產生一個表空間文件,以ibd結尾,數據、索引、表的內部數據字典都將被保存在這個單獨的表空間中。

  4.4、innodb行記錄格式

  mysql從5.1開始,innodb提供了compact和redundant(爲了兼容以前版本)兩種格式來存放行記錄數據。可以通過show table status like t\G來查看格式。

  4.4.1、compact行記錄格式

  P85

  4.4.2、redundant行記錄格式

  P88

  4.4.3、行溢出數據

  varchar(N)最多可以存儲多少個字符跟表的字符集格式有關係,在latin1下,varchar可以存儲65532個字符,在GBK下可以存儲32767個字符,和UTF8下可以存儲21845個字符。使用?varchar 提示的0-65535指的是字節。

  一個頁是16K,如何存儲65535字節呢?這個時候就會出現行溢出,在B-tree節點頁存儲768字節的前綴,剩下的數據存入Uncompressed BLOG Page。爲什麼會在B-tree節點頁存儲768個字節,而不全部存進去呢?因爲innodb是索引組織表(B-tree),一個頁中至少應該有2條記錄,否則就成鏈表,失去了B+樹的意義。所以innodb會自我優化,一個頁中如果只能存放一條記錄,那麼innodb存儲引擎會自動將數據存儲到溢出頁。

  4.4.4、compressed與dynamic記錄格式

  P98

  4.4.5、char的行結構存儲

  從mysql4.1開始CHR(n),中N指定的是字符的長度,而不是之前版本的字節長度。也就是說在不同字符集下,CHAR的內部存儲不是定長的數據。可以通過select a,char_length(a),length(a) from t;查看字符和字節數。所以在多字符集下,char和varchar佔用a空間是一樣的。

  4.5、innodb數據頁結構

  P101

  4.6、named file formats

  innodb存儲引擎通過named file formats機制來解決不同版本下頁結構兼容性問題。

  4.7、約束

  4.7.1、數據完成行

  innodb提供了以下四種約束

  Primary key

  Unique Key

  Foreign Key

  Default

  Not NULL

  4.7.2、約束的創建和查找

  創建時候定義,或者使用alter table定義。

  4.7.3、約束和索引的區別

  primary key和unique key既是約束也是主鍵。約束是一個邏輯的概念,用來保證數據完整性,而索引是一個數據結構,有邏輯上的概念,在數據庫中更是一個物理存儲的方式。

  4.7.4、對於錯誤數據的約束

  可以通過修改sql_mode來保證約束的強制性。

  4.7.5、ENUM和SET約束

  由於mysql不支持check約束,所以可以通過ENUM和SET來實現部分需求,還可以通過觸發器來實現check約束,注意需要修改sql_mode=’strict_trans_tables’;

  4.7.6、觸發器

  P121

  4.7.7、外鍵

  P123

  4.8、視圖

  4.9、分區表

  4.9.1、分區表的概述

  分區表不是在存儲引擎曾完成的,所以不止innodb支持分區表功能。myisma,ndb等都支持,csv、federated、merge等不支持。

  mysql的分區表是水平分區,並不是垂直分區,mysql的分區表是局部分區索引,一個分區中既存儲數據又存放索引。

  可以通過 show variables like ‘%partition%’;查看mysql是否支持分區表功能。

  當前mysql數據庫支持以下幾種類型的分區:

  Range分區 行數據基於屬於一個給定連續區間的列值放入分區,這個值只能是整數。

  RANGE CLUMNS分區 5.5開始支持

  LIST分區 和range類似,只是list分區裏面是離散的值,這個值只能是整數

  LIST CLUMNS分區 5.5開始支持

  HASH分區 根據用戶自定義的表達式的返回值進行分區,返回值不能是負數。

  KEY分區 根據mysql數據庫提供的哈西函數進行分區

  不論什麼類型的分區,如果表中存在主鍵和唯一索引,那麼分區列必須是主鍵或者唯一索引的一個組成部分。否則回報錯。

  4.9.2、range分區

 

create table t_range( 
  id int(11), 
  money int(11) unsigned not null, 
  date datetime 
  )partition by range(year(date))( 
  partition p2007 values less than (2008), 
  partition p2008 values less than (2009), 
  partition p2009 values less than (2010) 
  partition p2010 values less than maxvalue 
  )

 

  可以使用 select * from INFORMATION_SCHEMA.partitions where table_schema=database() and table_name=’t_range’\G查看分區的相關信息。

  可以使用 explain partitions select * from t_range where date > ’2010-10-10′;來分析分區使用的索引和執行情況。

  對range分區的查詢,優化器只能對YEAR(),TO_DAYS(),TO_SECONDS(),UNIX_TIMESTAMP()這類函數進行優化選擇,所以在規劃partition by range時的分區函數一定要用上面的類型才能達到優化的目的。

  4.9.3、list分區

 

create table t_list( 
  a int(11), 
  b int(11) 
  )(partition by list (b) 
  partition p0 values in (1,3,5,7,9), 
  partition p1 values in (2,4,6,8,0) 
  );

 

  對於innodb和myisam引擎,一條語句插入多條記錄的時候,如果中間有值不能插入,innodb會全部回滾,myisam在錯誤值之前的數據可以插入到表中。

  4.9.5、hash分區

  hash分區的目的是將數據均勻的分佈到預先定義的各個分區中,保證各分區的數據量大致一致。

 

create table t_hash( 
  a int(11), 
  b datetime 
  )partition by hash (YEAR(b) 
  partitions 4;

 

  hash的分區函數頁需要返回一個整數值。partitions子句中的值是一個非負整數,不加的partitions子句的話,默認爲分區數爲1。

  4.9.6、key分區

  key分區和hash分區相似,不同在於hash分區是用戶自定義函數進行分區,key分區使用mysql數據庫提供的函數進行分區,NDB cluster使用MD5函數來分區,對於其他存儲引擎mysql使用內部的hash函數,這些函數基於password()一樣的算法。

 

create table t_key( 
  a int(11), 
  b datetime) 
  partition by key (b) 
  partitions 4;

 

  4.9.6、columns分區

  上面介紹的RANGE、LIST、HASH、KEY四種分區中,分區的條件必須是整形,如果不是整形需要通過函數將其轉換爲整形。

  mysql-5.5開始支持COLUMNS分區,可視爲RANGE和LIST分區的進化,COLUMNS分區可以直接使用非整形數據進行分區。COLUMNS分區支持以下數據類型:

  所有整形,如INT SMALLINT TINYINT BIGINT。FLOAT和DECIMAL則不支持。

  日期類型,如DATE和DATETIME。其餘日期類型不支持。

  字符串類型,如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT類型不支持。

  COLUMNS可以使用多個列進行分區。

  4.9.7、子分區

  mysql允許在RANGE和LIST分區上再進行HASH或者key的子分區。

  每個分區上的子分區數量必須相同。

  在每個分區內,子分區的名稱是唯一的,

  分區可以放到不同磁盤上。

  4.9.8、分區中的NULL值

  RANGE,HASH,KEY分區如果插入null值,mysql會把它放入最左邊的分區,如果刪除最左邊的分區,null值不會被刪除,他會記錄到新的最左邊的分區。

  LIST分區如果沒有指定NULL值的存放位置,那麼就會報錯。

  4.9.9、分區的性能

  OLTP系統不適合使用分區表,如果磁盤空間和磁盤IO沒出現瓶頸,也不建議使用分區表。

  第五章、索引與算法

  索引和開銷是需要找一個平衡點,過多或者過少都會影響性能,從而導致負載過高,浪費硬件資源。

  5.1、innodb存儲引擎概述

  innodb支持常見的兩種索引,B+樹索引和hash索引。hash索引是自適應的,不能認爲干預。

  B+樹是由平衡二叉樹演化而來,但是B+樹不是一個二叉樹。

  B+樹並不能直接找到具體的行,B+樹索引只能找到數據行所在的頁,然後數據庫通過把頁讀入內存,再在內存中進行查找。

  5.2、二分查找法

  頁中的具體行就是通過二分法查找的。1946年發明的二分查找法,直到1962年纔出現完整正確的二分查找法。

  5.3、平衡二叉樹

  平衡二叉樹首先的符合二叉樹定義,其次必須滿足任何節點的左右兩個子樹高度最大差1.平衡二叉樹的效率較高,但是維護平衡二次樹需要消耗比較多的資源。

  5.4、B+樹

  B+樹是從B樹和索引順序訪問方法演化而來。在B+樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉節點中,各頁節點指針進行鏈接。

  5.4.1、B+樹的插入操作

  B+樹的插入必須保證插入後頁節點中的記錄依然排序,並且需要考慮插入B+樹的三種情況。

  leaf page full index page full 操作

  NO NO 直接插入leaf page。

  YES NO 1、拆分leaf page 2、將中間的節點放入index page中 3、小於中間節點的記錄到左邊 4、大於等於中間節點的記錄到右邊

  YES YES 1、拆分left page 2、將中間的節點放入index page中 3、大於等於中間節點的記錄到右邊 4、拆分index page 5、小於中間節點的記錄到左邊 6、大於中間節點的放右邊 7、中間節點放上一層index page

  如果看不懂的請參照P166.

  B+樹總會保持平衡,但是對於新插入的值可能需要大量拆分,這樣會消耗大量磁盤資源,所以B+樹有了旋轉(rotation)功能,旋轉發生在leat page已經滿了,但是其左右節點沒有滿的情況下,這時B+樹並不會着急去拆分頁的操作,而且是將記錄轉移到所在頁的兄弟節點上,通常左兄弟先被檢查。

  5.4.2、B+樹的刪除操作

  B+樹使用填充因子(fill factor)來控制樹的刪除變化,50%是填充因子可設的最小值。B+樹的刪除操作同樣必須保證刪除後頁節點中的記錄依然排序,同插入一樣B+樹刪除操作也需要考慮三種情況。

  leaf page below fill factor index page below fill factor 操作

  NO NO 直接將記錄從頁節點刪除,如果該節點還是index page的節點,則用該節點右邊節點代替

  YES NO 合併頁節點及其兄弟節點,同時更新index page

  YES NO 1、合併頁節及其兄弟節點 2、更新index page 3、合併index page及其兄弟節點

  5.5、B+樹索引

  B+樹索引在數據庫中有一個特點是高扇出性(fan out),B+樹的高度一般是2-3層。B+樹索引可以分爲聚集索引(clustered index)和輔助聚集索引(secondary index)。

  聚集索引:即表中數據按照主鍵順序存放,而聚集索引就是按照每張表的主鍵構造一顆B+樹,並且葉節點中存放着整張表的行記錄數據。

  輔助索引:也稱爲非聚集索引,葉級別不包含行的全部數據,葉節點除了包行鍵值以外,每個葉級別中的索引行中還包含了一個書籤,該書籤就是對應行數據的聚集索引鍵。

  5.6、B+樹索引的使用 P183 (聯合索引可以只使用左邊那個,或者同時使用左邊+右邊,但是不能單獨使用右邊的索引)

  5.7、hash索引

  innodb存儲引擎中自適應hash索引使用的是散列表(hash table)的數據結構。但是散列表不只存在於自適應hash中,每個數據庫中都存在,用來加速內存中數據的查找。

  5.7.1哈西表(hash table)

  hash table又叫散列表,由直接尋址表改進而來。

發佈了125 篇原創文章 · 獲贊 0 · 訪問量 6282
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章