本节讨论 MySQL 分区支持的当前限制和限制。
禁止的构造。 以下构造在分区表达式中不允许:
-
存储过程、存储函数、可加载函数或插件。
-
声明的变量或用户变量。
有关在分区表达式中允许的 SQL 函数的列表,请参阅 第 26.6.3 节,“与函数相关的分区限制”。
算术和逻辑运算符。 在分区表达式中允许使用算术运算符 +
、-
和 *
。然而,结果必须是一个整数值或 NULL
(除非在 [LINEAR] KEY
分区中,如本章其他地方所讨论的那样;请参阅 第 26.2 节,“分区类型”,以获取更多信息)。
位运算符 |
、&
、^
、<<
、>>
和 ~
在分区表达式中不允许。
服务器 SQL 模式。 使用用户定义的分区的表不会保留创建时的 SQL 模式。如本手册其他地方所讨论的那样(请参阅 第 7.1.11 节,“服务器 SQL 模式”),许多 MySQL 函数和运算符的结果可能根据服务器 SQL 模式而变化。因此,在创建分区表后更改服务器 SQL 模式可能会导致这些表的行为发生重大变化,并可能导致数据损坏或丢失。因此,强烈建议您在创建分区表后不要更改服务器 SQL 模式。
例如,考虑以下 CREATE TABLE
语句,该语句只能在 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
后删除 NO_UNSIGNED_SUBTRACTION
服务器 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
另请参阅 第 7.1.11 节,“服务器 SQL 模式”。
服务器SQL模式也会影响分区表的复制。源和副本之间的不同SQL模式可能会导致分区表达式的评估结果不同,这可能会导致源和副本之间的数据分布不同,甚至可能导致插入分区表时在源上成功但在副本上失败。为了获得最佳结果,您应该始终在源和副本上使用相同的服务器SQL模式。
性能考虑因素。 下面列出了分区操作对性能的影响:
-
文件系统操作。 分区和重新分区操作(例如
ALTER TABLE
withPARTITION BY ...
,REORGANIZE PARTITION
, orREMOVE PARTITIONING
) 依赖于文件系统操作来实现。这意味着这些操作的速度受到文件系统类型和特征、磁盘速度、交换空间、操作系统的文件处理效率和 MySQL 服务器选项和变量的影响。特别是,您应该确保large_files_support
已启用,并且open_files_limit
设置正确。使用InnoDB
表的分区和重新分区操作可以通过启用innodb_file_per_table
来提高效率。另见 最大分区数。
-
表锁。 一般来说,执行分区操作的进程会对表加写锁。读取这些表的操作相对不受影响;挂起的
INSERT
和UPDATE
操作将在分区操作完成后执行。对于InnoDB
特殊情况的限制,请参阅 分区操作。 -
索引;分区修剪。 与非分区表一样,正确使用索引可以大大加速查询分区表的速度。此外,设计分区表和查询来利用 分区修剪 可以大幅提高性能。请参阅 第 26.4 节,“分区修剪”,以获取更多信息。
索引条件下推现在支持分区表。请参阅 第 10.2.1.6 节,“索引条件下推优化”。
-
LOAD DATA 的性能。 在 MySQL 8.3 中,
LOAD DATA
使用缓冲来提高性能。您应该注意缓冲区使用每个分区 130 KB 内存来实现此目的。
最大分区数。 不使用 NDB
存储引擎的表的最大可能分区数为 8192。这包括子分区。
使用 NDB
存储引擎的表的最大可能用户定义分区数取决于 NDB Cluster 软件版本、数据节点数量和其他因素。请参阅 NDB 和用户定义分区,以获取更多信息。
如果在创建具有大量分区(但少于最大值)的表时遇到错误消息,如Got error ... from storage engine: Out of resources when opening file,您可能可以通过增加open_files_limit
系统变量的值来解决问题。但是,这取决于操作系统,并且在所有平台上可能不可行或不可取;请参阅第 B.3.2.16 节,“文件未找到和类似错误”,以获取更多信息。在某些情况下,使用大量分区(数百个)也可能不可取,因为其他原因,因此使用更多分区并不一定会导致更好的结果。
另请参阅文件系统操作。
外键不支持分区 InnoDB 表。 使用 InnoDB
存储引擎的分区表不支持外键。更具体地说,这意味着以下两个语句是正确的:
-
没有使用用户定义的分区的
InnoDB
表定义可以包含外键引用;没有InnoDB
表定义包含外键引用的用户分区表。 -
没有
InnoDB
表定义可以包含对用户分区表的外键引用;没有InnoDB
表使用用户定义的分区可以包含外键引用的列。
这些限制的范围包括所有使用 InnoDB
存储引擎的表。CREATE TABLE
和 ALTER TABLE
语句如果违反这些限制将不被允许。
ALTER TABLE ... ORDER BY。 对分区表运行 ALTER TABLE ... ORDER BY
语句仅对每个分区中的行进行排序。column
ADD COLUMN ... ALGORITHM=INSTANT。 一旦您在分区表上执行 ALTER TABLE ... ADD COLUMN ... ALGORITHM=INSTANT
,就不再可能与该表交换分区。
REPLACE 语句对主键的修改影响。 在某些情况下(请参阅第 26.6.1 节,“Partitioning Keys, Primary Keys, and Unique Keys”),您可能需要修改表的主键。请注意,如果您的应用程序使用 REPLACE
语句,并且您这样做了,结果可能会大幅改变。请参阅第 15.2.12 节,“REPLACE 语句”,以获取更多信息和示例。
FULLTEXT 索引。 分区表不支持 FULLTEXT
索引或搜索。
空间列。 带有空间数据类型的列,如 POINT
或 GEOMETRY
,不能用于分区表。
日志表。 不可能将日志表分区;对这样的表执行 ALTER TABLE ... PARTITION BY ...
语句将失败并显示错误。
分区键的数据类型。 分区键必须是整数列或解析为整数的表达式。使用 ENUM
列的表达式不能被使用。该列或表达式的值也可以是 NULL
;请参阅第 26.2.7 节,“MySQL 分区如何处理 NULL”。
有两个例外情况:assistant<|end_header_id|> Please note that I've translated the text into Simplified Chinese. If you need Traditional Chinese, please let me know.
-
当按 [
LINEAR
]KEY
分区时,可能使用任何有效的 MySQL 数据类型的列,除了TEXT
或BLOB
,因为内部键哈希函数从这些类型中生成正确的数据类型。例如,以下两个CREATE TABLE
语句是有效的: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 COLUMNS
或LIST COLUMNS
分区时,可能使用字符串、DATE
和DATETIME
列。例如,每个以下CREATE TABLE
语句都是有效的: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) );
子查询 分区键不能是子查询,即使该子查询解析为整数值或 NULL
。
列索引前缀不支持键分区 在创建按键分区的表时,任何在分区键中使用列前缀的列都不会被用于表的分区函数。考虑以下 CREATE TABLE
语句,该语句有三个 VARCHAR
列,并且其主键使用所有三个列并指定了两个列的前缀:
CREATE TABLE t1 (
a VARCHAR(10000),
b VARCHAR(25),
c VARCHAR(10),
PRIMARY KEY (a(10), b, c(2))
) PARTITION BY KEY() PARTITIONS 2;
该语句被接受,但结果表实际上是按照以下语句创建的,只使用不包括前缀的主键列(列 b
)作为分区键:
CREATE TABLE t1 (
a VARCHAR(10000),
b VARCHAR(25),
c VARCHAR(10),
PRIMARY KEY (a(10), b, c(2))
) PARTITION BY KEY(b) PARTITIONS 2;
这种宽松的行为是 deprecated(并且可能在未来版本的 MySQL 中被删除)。在分区键中使用一个或多个带前缀的列将生成警告,每个这样的列都会生成警告,如下所示:
mysql> CREATE TABLE t1 (
-> a VARCHAR(10000),
-> b VARCHAR(25),
-> c VARCHAR(10),
-> PRIMARY KEY (a(10), b, c(2))
-> ) PARTITION BY KEY() PARTITIONS 2;
Query OK, 0 rows affected, 2 warnings (1.25 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Warning
Code: 1681
Message: Column 'test.t1.a' having prefix key part 'a(10)' is ignored by the
partitioning function. Use of prefixed columns in the PARTITION BY KEY() clause
is deprecated and will be removed in a future release.
*************************** 2. row ***************************
Level: Warning
Code: 1681
Message: Column 'test.t1.c' having prefix key part 'c(2)' is ignored by the
partitioning function. Use of prefixed columns in the PARTITION BY KEY() clause
is deprecated and will be removed in a future release.
2 rows in set (0.00 sec)
这包括使用表的主键隐式定义的列的情况,通过使用空的 PARTITION BY KEY()
子句。
如果所有指定的分区键列都使用前缀,则 CREATE TABLE
语句将失败,并生成一个正确地标识问题的错误消息:
mysql> CREATE TABLE t1 (
-> a VARCHAR(10000),
-> b VARCHAR(25),
-> c VARCHAR(10),
-> PRIMARY KEY (a(10), b(5), c(2))
-> ) PARTITION BY KEY() PARTITIONS 2;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's
partitioning function (prefixed columns are not considered).
有关按键分区表的常规信息,请参阅 第 26.2.5 节,“KEY Partitioning”。
子分区问题 子分区必须使用 HASH
或 KEY
分区。只有 RANGE
和 LIST
分区可以被子分区; HASH
和 KEY
分区不能被子分区。
SUBPARTITION BY KEY
需要明确指定子分区列或列, unlike the case with PARTITION BY KEY
, where it can be omitted (in which case the table's primary key column is used by default)。考虑以下创建表的语句:
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;
然而,以下尝试创建子分区表使用默认列作为子分区列的语句将失败,列必须被指定以使语句成功,如下所示:
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 #51470)。
数据目录和索引目录选项。 表级别的 数据目录
和 索引目录
选项被忽略(见 Bug #32091)。您可以为 InnoDB
表的个别分区或子分区使用这些选项。 在 数据目录
子句中指定的目录必须被 InnoDB
所知。有关更多信息,请参阅 使用 DATA DIRECTORY 子句。
修复和重建分区表。 语句 CHECK TABLE
、OPTIMIZE TABLE
、ANALYZE TABLE
和 REPAIR TABLE
支持分区表。
此外,您可以使用 ALTER TABLE ... REBUILD PARTITION
重建一个或多个分区表的分区;ALTER TABLE ... REORGANIZE PARTITION
也会导致分区被重建。请参阅 第 15.1.9 节,“ALTER TABLE 语句”,以获取有关这两个语句的更多信息。
ANALYZE
、CHECK
、OPTIMIZE
、REPAIR
和 TRUNCATE
操作支持子分区。请参阅 第 15.1.9.1 节,“ALTER TABLE 分区操作”。
分区和子分区的文件名分隔符。 表分区和子分区文件名包括生成的分隔符,如 #P#
和 #SP#
。这些分隔符的字母大小写可能会变化,不应该依赖它们。