本文對mysql分區表的局限性做了一些總結,因為個人能力以及測試環境的 原因,有可能有錯誤的地方,還請大家看到能及時指出,當然有興趣的朋友可以去官方網站查閱。
禁止構建
分區表達式不支持以下幾種構建:
mysql,存儲函數,UDFS或者插件
聲明變量或者用戶變量
可以參考分區不支持的SQL函數
算術和mysql
分區表達式支持+,-,*算術運算,但是不支持p和/運算(還存在,可以查看Bug #30188, Bug #33182)。但是,結果必須是整形或者mysql(線性分區鍵除外,想了解更多信息,可以查看分區類型)。
分區表達式不支持mysql:|,&,^,>,~ .
HANDLER語句
在MySQL 5.7.1之前的分區表不支持HANDLER語句,以后的版本取消了這一限制。
服務器SQL模式
如果要用用戶自定義分區的表的話,需要注意的是,在創建分區表時的SQL模式是不保留的。在服務器SQL模式一章中已經討論過,大多數MySQL函數和運算符的結果可能會根據服務器SQL模式而改變。所以,一旦SQL模式在創建分區表后改變,可能導致這些表的行為發生重大變化,很容易導致數據丟失或者損壞。基于以上原因,強烈建議你在創建分區表后千萬不要修改服務器的SQL模式。
舉個例子來說明下上述情況:
1.mysql
mysql>?CREATE?TABLE?tn?(c1?INT) ??->????PARTITION?BY?LIST(1?p?c1)?( ??->????PARTITION?p0?VALUES?IN?(NULL), ??->????PARTITION?p1?VALUES?IN?(1) ??->?); ??Query?OK,?0?rows?affected?(0.05?sec)
MySQL默認除以0的結果是NULL,而不是報錯:
mysql>?SELECT?@@sql_mode; +------------+ |?@@sql_mode?| +------------+ |??????| +------------+ 1?row?in?set?(0.00?sec) ? mysql>?INSERT?INTO?tn?VALUES?(NULL),?(0),?(1); Query?OK,?3?rows?affected?(0.00?sec) Records:?3?Duplicates:?0?Warnings:?0
然而如果我們修改SQL模式的話,就會報錯:
mysql>?SET?sql_mode='STRICT_ALL_TABLES,ERROR_FOR_pISION_BY_ZERO'; Query?OK,?0?rows?affected?(0.00?sec) ? mysql>?INSERT?INTO?tn?VALUES?(NULL),?(0),?(1); ERROR?1365?(22012):?pision?by?0
2.表輔助功能
有時候修改SQL模式可能會導致分區表不可用。比如有些表只有在SQL模式為NO_UNSIGNED_SUBTRACTION才發揮作用,比如:
mysql>?SELECT?@@sql_mode; +------------+ |?@@sql_mode?| +------------+ |??????| +------------+ 1?row?in?set?(0.00?sec) ? mysql>?CREATE?TABLE?tu?(c1?BIGINT?UNSIGNED) ??->???PARTITION?BY?RANGE(c1?-?10)?( ??->???PARTITION?p0?VALUES?LESS?THAN?(-5), ??->???PARTITION?p1?VALUES?LESS?THAN?(0), ??->???PARTITION?p2?VALUES?LESS?THAN?(5), ??->???PARTITION?p3?VALUES?LESS?THAN?(10), ??->???PARTITION?p4?VALUES?LESS?THAN?(MAXVALUE) ??->?); ERROR?1563?(HY000):?Partition?constant?is?out?of?partition?function?domain ??? mysql>?SET?sql_mode='NO_UNSIGNED_SUBTRACTION'; Query?OK,?0?rows?affected?(0.00?sec) ? mysql>?SELECT?@@sql_mode; +-------------------------+ |?@@sql_mode???????| +-------------------------+ |?NO_UNSIGNED_SUBTRACTION?| +-------------------------+ 1?row?in?set?(0.00?sec) ? mysql>?CREATE?TABLE?tu?(c1?BIGINT?UNSIGNED) ??->???PARTITION?BY?RANGE(c1?-?10)?( ??->???PARTITION?p0?VALUES?LESS?THAN?(-5), ??->???PARTITION?p1?VALUES?LESS?THAN?(0), ??->???PARTITION?p2?VALUES?LESS?THAN?(5), ??->???PARTITION?p3?VALUES?LESS?THAN?(10), ??->???PARTITION?p4?VALUES?LESS?THAN?(MAXVALUE) ??->?); ??? Query?OK,?0?rows?affected?(0.05?sec)
如果你在創建tu后,修改SQL模式,就可能再也不能訪問這個表了:
mysql>?SET?sql_mode='';Query?OK,?0?rows?affected?(0.00?sec) ? mysql>?SELECT?*?FROM?tu; ERROR?1563?(HY000):?Partition?constant?is?out?of?partition?function?domain ? mysql>?INSERT?INTO?tu?VALUES?(20); ERROR?1563?(HY000):?Partition?constant?is?out?of?partition?function?domain
服務器端的SQL模式也會影響分區表的復制。在主備間使用不同的SQL模式可能會導致分區表達式主備上執行是不同的結果(而在阿里主備切換是很正常的操作);這也會導致在主備復制過程中,不同分區間的數據分布不同;也有可能導致在主庫上的分區表insert成功,而備庫上失敗。基于上述情況,最好的解決辦法是保證主備間的SQL模式要保持一致(這個是DBA在運維過程中需要注意的)。
性能mysql
下面是一些會影響分區操作性能的因素:
mysql操作
分區或者重新分區(比如ALTER TABLE …PARTITION BY …, REORGANIZE PARTITION, 或者REMOVE PARTITIONING )操作取決于文件系統的實現。意思是說上述操作會受操作系統上,比如:文件系統的類型和特性,磁盤速度,swap空間,操作系統上的mysql效率,以及MySQL服務器上的和文件句柄相關的選項,變量等因素影響。需要特別說明的是,你需要保證large_files_support是enabled的,open_files_limit設置是合理的。對于MyISAM引擎的分區表來說,需要增加myisam_max_sort_file_size以提高性能;對于InnoDB表來說,分區或者重新分區操作通過enabled innodb_file_per_table效率會更快。
也可以參考分區的最大數量。
MyISAM和分區文件描述符
對于MyISAM分區表來說,MySQL為每個打開的表,每個分區使用兩個文件描述符。這也就意味著,在MyISAM分區表上想執行操作(特別是ALTER TABLE操作)比相同的表沒有分區,需要更多的文件描述符。
假設我們要創建有100個分區的MyISAM表,語句如下:
CREATE?TABLE?t?(c1?VARCHAR(50)) PARTITION?BY?KEY?(c1)?PARTITIONS?100 ENGINE=MYISAM;
簡單來講,在這個例子中,雖然我們用的KEY分區,但是文件描述符的問題,在所有使用mysql是MyISAM的分區里都會遇到,不管是分區類型是哪種。但是使用其他存儲引擎(比如InnoDB)的分區表沒有這個問題。
假設你想對t重新分區,想讓它有101個分區的話,使用下面的語句:
ALTER TABLE t PARTITION BY KEY (c1) PARTITIONS 101;
如果要處理ALTER TABLE語句需要402個文件描述符,原來100個分區*2個+101個新分區*2。這是因為在重新組織表數據時,必須打開所有的(新舊)分區。所以建議在執行這些操作時,要確保–open-files-limit要設置的大些。
表鎖
對表執行分區操作的進程會占用表的寫鎖,不影響讀,例如在這些分區上的INSERT和UPDATE操作只有在分區操作完成后才能執行。
存儲引擎
分區操作,比如查詢,和更新操作通常情況下用MyISAM引擎要比InnoDB和NDB快。
mysql;分區修剪
分區表和非分區表一樣,合理的利用索引可以顯著地提升查詢速度。另外,設計分區表以及在這些表上的查詢,可以利用分區修剪來顯著提升性能。
在MySQL 5.7.3版本之前,分區表不支持索引條件下推,之后的版本可以支持了。
load data性能
在MySQL 5.7,load data 使用buffer提高性能。你需要知道的是buffer會占用每個分區的130KB來達到這個目的。
分區的最大個數
如果不是用NDB作為存儲引擎的分區表,支持分區(這里子分區也包含在內)最大個數是8192。
如果使用NDB作為存儲引擎的用戶自定義分區的最大分區個數,取決于MySQL Cluster的版本, 數據節點和其他因素。
如果你創建一個非常多(比最大分區數要少)的分區時,遇到諸如Got error … from storage engine: Out of resources when opening file類的錯誤,你可能需要增加open_files_limit。但是open_files_limit其實也依賴操作系統,可能不是所有的平臺都可以建議調整。還有一些其他情況,不建議使用巨大或者成百上千個分區,所以使用越來越多的分區并不見得能帶來好結果。
不支持Query cache
分區表不支持query cache,在分區表的查詢中自動避開了query cache。也就是說在分區表的查詢語句中query cache是不起作用的。
每個分區一個key caches
在MySQL 5.7版本中,可以通過CACHE INDEX和LOAD INDEX INTO CACHE來使用MyISAM分區表的key cache。可以為一個,幾個或者所有分區都定義key cache,這樣可以把一個,幾個或者所有分區的索引預加載到key cache中。
不支持InnoDB分區表的外鍵
使用InnoDB引擎的分區表不支持外鍵。下面的兩種具體情況來闡述:
在InnoDB表不能使用包含有外鍵的自定義分區;如果已經使用了外鍵的InnoDB表,則不能被分區。
InnoDB表不能包含一個和用戶自定義分區表相關的外鍵;使用了用戶自定義分區的InnoDB表,不能包含和外鍵相關的列。
剛剛列出的限制的范圍包括使用InnoDB存儲引擎的所有表。違反這些限制的CREATE TABLE和ALTER TABLE語句是不被允許的。
ALTER TABLE … ORDER BY
如果在分區表上執行ALTER TABLE … ORDER BY的話,會導致每個分區的行排序。
REPLACE語句在修改primary key上的效率
在某些情況下是需要修改表的primary key的,如果你的應用程序使用了REPLACE語句,這些語句的結果可能會被大幅度修改。
全文索引
分區表不支持全文索引或者搜索,即使分區表的存儲引擎是InnoDB或者MyISAM也不行。
空間列
分區表不支持空間列,比如點或者幾何。
臨時表
不能對臨時表進行分區(Bug #17497)。
日志表
不能對日志表進行分區,如果強制執行ALTER TABLE … PARTITION BY … 語句會報錯。
分區鍵的mysql
分區鍵必須是整形或者結果是整形的表達式。不能用結果為ENUM類型的表達式。因為這種類型的表達式可能是NULL。
下面兩種情況是例外的:
當用LINER分區時,可以使用除TEXT或者BLOBS以外的數據類型作為分區鍵,因為MySQL內部的 hash函數會從這些列中產生正確的數據類型。例如,下面的創建語句是合法的:
CREATE?TABLE?tkc?(c1?CHAR) PARTITION?BY?KEY(c1) PARTITIONS?4; CREATE?TABLE?tke ??(?c1?ENUM('red',?'orange',?'yellow',?'green',?'blue',?'indigo',?'violet')?) PARTITION?BY?LINEAR?KEY(c1) PARTITIONS?6;
當用RANGE,LIST,DATE或者DATETIME列分區的話,可能會用string。例如,下面的創建語句是合法的:
CREATE?TABLE?rc?(c1?INT,?c2?DATE) PARTITION?BY?RANGE?COLUMNS(c2)?( ??PARTITION?p0?VALUES?LESS?THAN('1990-01-01'), ??PARTITION?p1?VALUES?LESS?THAN('1995-01-01'), ??PARTITION?p2?VALUES?LESS?THAN('2000-01-01'), ??PARTITION?p3?VALUES?LESS?THAN('2005-01-01'), ??PARTITION?p4?VALUES?LESS?THAN(MAXVALUE) ); CREATE?TABLE?lc?(c1?INT,?c2?CHAR(1)) PARTITION?BY?LIST?COLUMNS(c2)?( ??PARTITION?p0?VALUES?IN('a',?'d',?'g',?'j',?'m',?'p',?'s',?'v',?'y'), ??PARTITION?p1?VALUES?IN('b',?'e',?'h',?'k',?'n',?'q',?'t',?'w',?'z'), ??PARTITION?p2?VALUES?IN('c',?'f',?'i',?'l',?'o',?'r',?'u',?'x',?NULL) );
上述異常都不適用于BLOB或TEXT列類型。
子查詢
即使子查詢避開整形值或者NULL值,分區鍵不能子查詢。
子分區的問題
子分區必須使用HASH或者KEY分區。只有RANGE和LIST分區支持被子分區;HASH和KEY不支持被子分區。
SUBPARTITION BY KEY要求顯示指定子分區列,不像PARTITION BY KEY可以省略(這種情況下會默認使用表的primary key)。例如,如果是這樣創建表:
CREATE?TABLE?ts?( ??id?INT?NOT?NULL?AUTO_INCREMENT?PRIMARY?KEY, ??name?VARCHAR(30) );
你也可以使用相同的列的創建分區表(以KEY分區),使用下面語句:
CREATE?TABLE?ts?( ??id?INT?NOT?NULL?AUTO_INCREMENT?PRIMARY?KEY, ??name?VARCHAR(30) ) PARTITION?BY?KEY() PARTITIONS?4;
前面的語句其實和下面的語句是一樣的:
CREATE?TABLE?ts?( ??id?INT?NOT?NULL?AUTO_INCREMENT?PRIMARY?KEY, ??name?VARCHAR(30) ) PARTITION?BY?KEY(id) PARTITIONS?4;
但是,如果嘗試使用缺省列作為子分區列,創建子分區表的話,以下語句將失敗,必須指定該語句才能執行成功,如下所示:(bug已知 Bug #51470)。
mysql>?CREATE?TABLE?ts?( ??->???id?INT?NOT?NULL?AUTO_INCREMENT?PRIMARY?KEY, ??->???name?VARCHAR(30) ??->?) ??->?PARTITION?BY?RANGE(id) ??->?SUBPARTITION?BY?KEY() ??->?SUBPARTITIONS?4 ??->?( ??->???PARTITION?p0?VALUES?LESS?THAN?(100), ??->???PARTITION?p1?VALUES?LESS?THAN?(MAXVALUE) ??->?); ??? ERROR?1064?(42000):?You?have?an?error?in?your?SQL?syntax;?check?the?manual?that corresponds?to?your?MySQL?server?version?for?the?right?syntax?to?use?near?') ? mysql>?CREATE?TABLE?ts?( ??->???id?INT?NOT?NULL?AUTO_INCREMENT?PRIMARY?KEY, ??->???name?VARCHAR(30) ??->?) ??->?PARTITION?BY?RANGE(id) ??->?SUBPARTITION?BY?KEY(id) ??->?SUBPARTITIONS?4 ??->?( ??->???PARTITION?p0?VALUES?LESS?THAN?(100), ??->???PARTITION?p1?VALUES?LESS?THAN?(MAXVALUE) ??->?); ??? Query?OK,?0?rows?affected?(0.07?sec)
數據字典和索引字典選項
分區表的數據字典和索引字典受以下因素制約:
表級的數據字典和索引字典被忽略(Bug #32091)
在Windows系統上,MyISAM分區表不支持獨立分區或子分區的數據字典和索引字典選項。但是支持InnoDB分區表的獨立分區或者子分區的數據字典。
修復和重建分區表
分區表支持CHECK TABLE, OPTIMIZE TABLE, ANALYZE TABLE, 和 REPAIR TABLE語句。
另外,你也可以用ALTER TABLE … REBUILD PARTITION在一個分區表上重建一個或多個分區;用ALTER TABLE … REORGANIZE PARTITION同樣可以重建分區。
從MySQL 5.7.2開始,子分區支持ANALYZE, CHECK, OPTIMIZE, REPAIR, 和 TRUNCATE操作。而在MySQL5.7.5之前的版本就已經引入REBUILD語法,只是不起作用(可以參考Bug #19075411, Bug #73130)。
分區表不支持mysqlcheck, myisamchk, 和 myisampack操作。
導出選項
在MySQL 5.7.4以前的版本,不支持InnoDB分區表的FLUSH TABLES語句的導出選項(Bug #16943907)。