詳解MySQL分區表的局限和限制的代碼實例

本文對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)。

? 版權聲明
THE END
喜歡就支持一下吧
點贊5 分享