MySQL分区表的局限和限制

禁止构建

分区表达式不支持以下几种构建:

  • 存储过程,存储函数,UDFS或者插件
  • 声明变量或者用户变量

可以参考分区不支持的SQL函数

算术和逻辑运算符

分区表达式支持+,-,*算术运算,但是不支持DIV和/运算(还存在,可以查看Bug #30188, Bug #33182)。但是,结果必须是整形或者NULL(线性分区键除外,想了解更多信息,可以查看分区类型)。

分区表达式不支持位运算:|,&,^,<<,>>,~ .

HANDLER语句

在MySQL 5.7.1之前的分区表不支持HANDLER语句,以后的版本取消了这一限制。

服务器SQL模式

如果要用用户自定义分区的表的话,需要注意的是,在创建分区表时的SQL模式是不保留的。在服务器SQL模式一章中已经讨论过,大多数MySQL函数和运算符的结果可能会根据服务器SQL模式而改变。所以,一旦SQL模式在创建分区表后改变,可能导致这些表的行为发生重大变化,很容易导致数据丢失或者损坏。基于以上原因,强烈建议你在创建分区表后千万不要修改服务器的SQL模式。

举个例子来说明下上述情况:

1.错误处理


1

2

3

4

5

6

mysql> CREATE TABLE tn (c1 INT)

    ->       PARTITION BY LIST(1 DIV c1) (

    ->       PARTITION p0 VALUES IN (NULL),

    ->       PARTITION p1 VALUES IN (1)

    -> );

    Query OK, 0 rows affected (0.05 sec)

MySQL默认除以0的结果是NULL,而不是报错:


1

2

3

4

5

6

7

8

9

10

11

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模式的话,就会报错:


1

2

3

4

5

mysql> SET sql_mode=‘STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO‘;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tn VALUES (NULL), (0), (1);

ERROR 1365 (22012): Division by 0

2.表辅助功能

有时候修改SQL模式可能会导致分区表不可用。比如有些表只有在SQL模式为NO_UNSIGNED_SUBTRACTION才发挥作用,比如:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

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模式,就可能再也不能访问这个表了:


1

2

3

4

5

6

7

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在运维过程中需要注意的)。

性能注意事项

下面是一些会影响分区操作性能的因素:

  • 文件系统操作

分区或者重新分区(比如ALTER TABLE ...PARTITION BY ...REORGANIZE PARTITION, 或者REMOVE PARTITIONING )操作取决于文件系统的实现。意思是说上述操作会受操作系统上,比如:文件系统的类型和特性,磁盘速度,swap空间,操作系统上的文件处理效率,以及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表,语句如下:


1

2

3

CREATE TABLE t (c1 VARCHAR(50))

PARTITION BY KEY (c1) PARTITIONS 100

ENGINE=MYISAM;

简单来讲,在这个例子中,虽然我们用的KEY分区,但是文件描述符的问题,在所有使用表引擎是MyISAM的分区里都会遇到,不管是分区类型是哪种。但是使用其他存储引擎(比如InnoDB)的分区表没有这个问题。

假设你想对t重新分区,想让它有101个分区的话,使用下面的语句:


1

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 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引擎的分区表不支持外键。下面的两种具体情况来阐述:

  1. 在InnoDB表不能使用包含有外键的自定义分区;如果已经使用了外键的InnoDB表,则不能被分区。
  2. 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 ...语句会报错。

分区键的数据类型

分区键必须是整形或者结果是整形的表达式。不能用结果为ENUM类型的表达式。因为这种类型的表达式可能是NULL

下面两种情况是例外的:

  1. 当用LINER分区时,可以使用除TEXT或者BLOBS以外的数据类型作为分区键,因为MySQL内部的 hash函数会从这些列中产生正确的数据类型。例如,下面的创建语句是合法的:
  2. 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;
  3. 当用RANGE,LIST,DATE或者DATETIME列分区的话,可能会用string。例如,下面的创建语句是合法的:
  4. 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)。例如,如果是这样创建表:


1

2

3

4

CREATE TABLE ts (

    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

    name VARCHAR(30)

);

你也可以使用相同的列的创建分区表(以KEY分区),使用下面语句:


1

2

3

4

5

6

CREATE TABLE ts (

    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

    name VARCHAR(30)

)

PARTITION BY KEY()

PARTITIONS 4;

前面的语句其实和下面的语句是一样的:


1

2

3

4

5

6

CREATE TABLE ts (

    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

    name VARCHAR(30)

)

PARTITION BY KEY(id)

PARTITIONS 4;

但是,如果尝试使用缺省列作为子分区列,创建子分区表的话,以下语句将失败,必须指定该语句才能执行成功,如下所示:(bug已知 Bug #51470)。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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)。

参考资料

https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations.html

https://www.slideshare.net/datacharmer/mysql-partitions-tutorial/34-Partition_pruning_unpartitioned_tableexplain_partitions

https://www.percona.com/blog/2010/12/11/mysql-partitioning-can-save-you-or-kill-you/

时间: 2024-08-05 15:23:00

MySQL分区表的局限和限制的相关文章

MySQL分区表(转)

查看分区情况 SELECT * FROM information_schema.PARTITIONS WHERE table_name='table_name': PARTITION_NAME:分区的名称 PARTITION_METHOD:分区的类型 TABLE_ROWS:分区数据条数 RANGE分区: create table t ( id int) engine=innodb partition by range (id) ( partition p0 values less than (1

MySQL分区表使用方法

原文:MySQL分区表使用方法 1. 确认MySQL服务器是否支持分区表 命令: show plugins; 2. MySQL分区表的特点 在逻辑上为一个表,在物理上存储在多个文件中 HASH分区(HASH) HASH分区的特点 根据MOD(分区键,分区数)的值把数据行存储到表的不同分区中 数据可以平均的分布在各个分区中 HASH分区的键值必须是一个INT类型的值,或是通过函数可以转为INT类型 如何建立HASH分区表 以INT类型字段 customer_id为分区键 CREATE TABLE

MySQL分区表的使用介绍

MySQL使用分区表的好处: 1,可以把一些归类的数据放在一个分区中,可以减少服务器检查数据的数量加快查询. 2,方便维护,通过删除分区来删除老的数据. 3,分区数据可以被分布到不同的物理位置,可以做分布式有效利用多个硬盘驱动器. MySQL可以建立四种分区类型的分区: RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区. ·         LIST 分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择.  www.2cto.com

MYSQL分区表详解

分区表对用户来说是一个独立的逻辑表,但是底层是多个物理字表组成的.分区代码实际上是对一组底层表的句柄对象封装.对分区表的请求,都会通过句柄对象转化成储存引擎的接口调用.所以分区对于SQL层来说是一个完全封装底层实现的黑盒子,对应用是透明的.但是底层每个分区都有个使用#分割命名的表文件. Mysql实现分区的方式--对底层表的封装--意味着索引也按照分区的子表定义的.但是没有全局索引. 和其他数据库一样patition by关键字定义分区,存放分区数据.在执行查询是,优化器会根据分区定义过滤我们不

MySQL 分区表原理及数据备份转移实战

1.分区表含义 分区表定义指根据可以设置为任意大小的规则,跨文件系统分配单个表的多个部分.实际上,表的不同部分在不同的位置被存储为单独的表.用户所选择的.实现数据分割的规则被称为分区函数,这在MySQL中它可以是模数,或者是简单的匹配一个连续的数值区间或数值列表,或者是一个内部HASH函数,或一个线性HASH函数. 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表. 2.分区表优点 1)分区表更容易维护.对于那些已经失去保存意义的数据,通常可以通过删除与那些数据有

mysql分区表

分区表   分区表的优点: 数据分开存放在不同的文件,可以支持更大的数据文件 在某些特定条件下能提高查询.删除.更新的效率   1.range分区表   mysql> create table mytest(id int,name varchar(20) not null,birthday date,primary key(id,birthday))auto_increment=1 partition by range(year(birthday))( partition p01 values

MySQL分区表姿势

大部分内容整理自姜承尧的innodb存储引擎2学习笔记. 分区: 分区的功能不是在存储引擎层实现的.因此不只是InnoDB才支持分区.MyISAM.NDB都支持分区操作. 分区的过程是将一个表或者索引分解为多个更小.更可管理的部分.从逻辑上将,只有一个表或者索引,但是在物理上这个表或索引可能由数十个物理分区组成. 每个分区都是独立的对象,可以独自处理,也可以作为一个更大对象的一部分进行处理. MySQL只支持水平分区,不支持垂直分区. 水平分区:将同一表中不同行的记录分配到不同的物理文件中. 垂

MySQL分区表管理

RANGE,LIST分区管理 1:为未分区表创建分区 ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2; 2:删除某个分区的数据 ALTER TABLE tr DROP PARTITION p2; 3:为分区表添加一个分区 ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000)); ALTER TABLE tt ADD PARTITION (PARTITIO

MySQL 分区表

今天统计数据的时候发现一张表使用了表分区,借此机会记录一下. 1. 什么是表分区? 表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分.从逻辑上看,只有一张表,但是底层却是由多个物理分区组成. 2. 表分区与分表的区别 分表:指的是通过一定规则,将一张表分解成多张不同的表.比如将用户订单记录根据时间成多个表. 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表. 3. 表分区有什么好处? 1)分区表的数据可以分布在不同的物理设备上,从而高效