MySQL技術內幕 InnoDB儲存引擎:分割槽表
一、MySQL分割槽表介紹
分割槽是一種表的設計模式,正確的分割槽可以極大地提升資料庫的查詢效率,完成更高質量的SQL程式設計。但是如果錯誤地使用分割槽,那麼分割槽可能帶來毀滅性的的結果。
分割槽功能並不是在儲存引擎層完成的,因此不只有InnoDB儲存引擎支援分割槽,常見的儲存引擎MyISAM、NDB等都支援分割槽。但是並不是所有的儲存引擎都支援,如CSV、FEDORATED、MERGE等就不支援分割槽。在使用此分割槽功能前,應該對選擇的儲存引擎對分割槽的支援有所瞭解。
MySQL資料庫在5.1版本時添加了對分割槽的支援,分割槽的過程是將一個表或索引分解為多個更小、更可管理的部分。就訪問資料庫的應用而言,從邏輯上講,只有一個表或一個索引,但是在物理上這個表或索引可能由數十個物理分割槽組成。每個分割槽都是獨立的物件,可以獨自處理,也可以作為一個更大物件的一部分進行處理。
MySQL資料庫支援的分割槽型別為水平分割槽(指將同一個表中不同行的記錄分配到不同的物理檔案中),並不支援垂直分割槽(指將同一表中不同列的記錄分配到不同的物理檔案中)。此外,MySQL資料庫的分割槽是區域性分割槽索引,一個分割槽中既存放了資料又存放了索引。而全域性分割槽是指,資料存放在各個分割槽中,但是所有資料的索引放在一個物件中。目前,MySQL資料庫還不支援全域性分割槽。
可以通過以下命令來檢視當前資料庫是否啟用了分割槽功能:
mysql> show global variables like '%partition%'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | have_partitioning | YES | +-------------------+-------+ 1 row in set (0.04 sec) mysql> show plugins *************************** 43. row *************************** Name: partition Status: ACTIVE Type: STORAGE ENGINE Library: NULL License: GPL
有時候可能會有這麼一種誤區,只要啟用了分割槽,資料庫就會執行的更快。這個結論結論是存在很多問題的,就經驗來看,分割槽可能會給某些SQL語句效能帶來提高,但是分割槽主要用於資料庫高可用性的管理。在OLTP應用中,對於分割槽的使用應該非常小心,總之,如果只是一味地使用分割槽,而不理解分割槽是如何工作的,也不清楚你的應用如何使用分割槽,那麼分割槽極有可能會對效能產生負面的影響。
二、MySQL分割槽型別
- RANGE分割槽
RANGE分割槽,是最常用的一種分割槽型別,基於屬於一個給定連續區間的列值,把多行分配給分割槽。
這些區間要連續且不能相互重疊,使用VALUES LESS THAN操作符來進行定義。
- LIST分割槽
LIST分割槽和RANGE分割槽類似,區別在於LIST分割槽是基於列值匹配一個離散值集合中的某個值來進行選擇,而非連續的。
LIST分割槽通過使用“PARTITION BY LIST(expr)”來實現,其中“expr” 是某列值或一個基於某個列值、並返回一個整數值的表示式,然後通過“VALUES IN (value_list)”的方式來定義每個分割槽,其中“value_list”是一個通過逗號分隔的整數列表。
- HASH分割槽
HASH分割槽的目的是將資料均勻地分佈到預先定義的各個分割槽中,保證各分割槽的資料量大致都是一樣的。在RANGE和LIST分割槽中,必須明確指定一個給定的列值或列值集合應該儲存在哪個分割槽中;而在HASH分割槽中,MySQL自動完成這些工作,使用者所要做的只是基於將要進行雜湊分割槽的列值指定一個列值或表示式,以及指定被分割槽的表將要被分隔成的分割槽數量。
要使用HASH分割槽來分割一個表,要在CREATE TABLE 語句上新增一個“PARTITION BY HASH (expr)”子句,其中“expr”是一個返回一個整數的表示式。它可以僅僅是欄位型別為MySQL 整型的一列的名字。此外,你很可能需要在後面再新增一個“PARTITIONS num”子句,其中num是一個非負的整數,它表示表將要被分割成分割槽的數量,如果沒有包括一個PARTITIONS子句,那麼分割槽的數量將預設為1。
- LINER HASH
MySQL還支援線性雜湊功能,它與常規雜湊的區別在於,線性雜湊功能使用的一個線性的2的冪(powers-of-two)運演算法則,而常規雜湊使用的是求雜湊函式值的模數。
線性雜湊分割槽和常規雜湊分割槽在語法上的唯一區別在於,在“PARTITION BY” 子句中新增“LINEAR”關鍵字。
- KEY分割槽
KEY分割槽和HASH分割槽相似,不同之處在於HASH分割槽使用使用者定義的函式進行分割槽,支援字串HASH分割槽,KEY分割槽使用MySQL資料庫提供的函式進行分割槽,這些函式基於與PASSWORD()一樣的運演算法則。
- COLUMNS
在前面說了RANGE、LIST、HASH和KEY這四種分割槽中,分割槽的條件是:資料必須為整形(interger),如果不是整形,那應該需要通過函式將其轉化為整形,如YEAR(),TO_DAYS(),MONTH()等函式。MySQL5.5版本開始支援COLUMNS分割槽,可視為RANGE分割槽和LIST分割槽的一種進化。COLUMNS分割槽可以直接使用非整形的資料進行分割槽,分割槽根據型別直接比較而得,不需要轉化為整形。此外,RANGE COLUMNS分割槽可以對多個列的值進行分割槽。
COLUMNS分割槽支援以下的資料型別:
- 所有的整形型別,如INT、SMALLINT、TINYINT和BIGINT。而FLOAT和DECIMAL則不予支援。
- 日期型別,如DATE何DATETIME。其餘的日期型別不予支援。
- 字串型別,如CHAR、VARCHAR、BINARY和VARBINARY。而BLOB和TEXT型別不予支援。
三、分割槽中的NULL值
MySQL資料庫允許對NULL值做分割槽,但是處理的方法與其他資料庫可能完全不同。MySQL資料庫的分割槽總是視NULL值小於任何的一個非NULL值,這和MySQL資料庫中處理NULL值的ORDER BY操作是一樣的。因此對於不同的分割槽型別,MySQL資料庫對於NULL值的處理也是各不相同。
- 對於RANGE分割槽,如果向分割槽列插入了NULL值,則MySQL資料庫會將該值放入最左邊的分割槽。
- 對於LIST分割槽,如果向分割槽列插入了NULL值,則必須顯示地指出哪個分割槽放入NULL值,否則會報錯。對於LIST分割槽,如果向分割槽列插入了NULL值,則必須顯示地指出哪個分割槽放入NULL值,否則會報錯。
- 對於HASH和KEY分割槽,對於NULL值的處理方法和RANGE分割槽、LIST分割槽不一樣。任何分割槽函式都會將含有NULL值的記錄返回為0。對於HASH和KEY分割槽,對於NULL值的處理方法和RANGE分割槽、LIST分割槽不一樣。任何分割槽函式都會將含有NULL值的記錄返回為0。
四、分割槽和效能
分割槽真的會加快資料庫的查詢嗎?實際上可能根本感覺不到查詢速度的提升,甚至會發現查詢速度急劇下降,因此在合理使用分割槽之前,必須瞭解分割槽的使用環境。
資料庫的應用分為兩類:一類是OLTP(線上事務處理),如Blog、電子商務、網路遊戲等;另一類是OLAP(線上分析處理),如資料倉庫、資料集市。對於OLAP的應用,分割槽的確是可以很好地提高查詢的效能,因為OLAP應用大多數查詢需要頻繁地掃描一張很大的表。假設有一張1億行的表,其中有一個時間戳屬性列。使用者的查詢需要從這張表中獲取一年的資料。如果按時間戳進行分割槽,則只需要掃描相應的分割槽即可。這就是前面介紹的分割槽修剪技術。
對於OLTP的應用,分割槽應該非常小心。在這種應用下,通常不可能會獲取一張大表10%的資料,大部分都是通過索引返回幾條記錄即可。而根據B+樹索引的原理可知,對於一張大表,一般的B+樹需要2~3次的磁碟IO。因此B+樹可以很好地完成操作,不需要分割槽的幫助,並且設計不好的分割槽會帶來嚴重的效能問題。
如很多開發團隊會認為含有1000w行的表是一張非常巨大的表,所以他們往往會選擇採用分割槽,如對主鍵做10個HASH的分割槽,這樣每個分割槽就只有100w的資料了,因此查詢應該變得更快了。如select * from table where [email protected]。但是有沒有考慮過這樣一種情況:100w和1000w行的資料本身構成的B+樹的層次都是一樣的,可能都是2~3層。那麼上述走主鍵分割槽的索引並不會帶來效能的提高。好的,如果1000w的B+樹高度是3,100w的B+樹高度是2,那麼上述按主鍵分割槽的索引可以避免1次IO,從而提高查詢的效率。這沒問題,但是這張表只有主鍵索引,沒有任何其他的列需要查詢的。如果還有類似如下的SQL:select * from table where [email protected],這時對於key的查詢需要掃描所有的10個分割槽,即使每個分割槽的查詢開銷為2次IO,則一共需要20次IO。而對於原來單表的設計,對於KEY的查詢只需要2~3次IO。
由以上結論可以看出,對於在OLTP場景中使用分割槽一定要特別小心了。
五、MySQL 5.7對分割槽的改進
在MySQL 5.6裡面,分割槽的資訊是在MySQL Server層維護的(在.par檔案裡面),InnoDB引擎層是不知道有分割槽這個概念的,InnoDB引擎層把每一個分割槽都當成一張普通的InnoDB表。在開啟一個分割槽表時,會開啟很多個分割槽,開啟這些分割槽表就相當於打開了同等數量的InnoDB表,這需要更多記憶體存放InnoDB表的元資料和各種與ibd檔案開啟相關的各種cache與handler的資訊。在MySQL 5.7裡面,InnoDB引入了Native Partitioning,它把分割槽的資訊從Server層移到了InnoDB層,開啟一個分割槽表和開啟一個InnoDB表的記憶體開銷基本是一樣的。
本文整理自:《MySQL技術內幕 InnoDB儲存引擎》
個人微信公眾號:
作者:jiankunking 出處:http://blog.csdn.net/jiankunking