MySQL分区表及MySQL5.7对其的改进

基础认识

分区是一种表的设计模式。正确的分区可以极大的提升数据库的查询效率,完成更高质量的SQL编程。但是如果错误的使用分区,那么分区反而是一个累赘。

分区类型

range分区

行数据基于一个给定的连续区间的列值放入分区。

CREATE TABLE `test_11` (
  `id` int(11) NOT NULL,
  `t` date NOT NULL,
  PRIMARY KEY (`id`,`t`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
 PARTITION BY RANGE (to_days(t))
(PARTITION p20170801 VALUES LESS THAN (736907) ENGINE = InnoDB,
 PARTITION p20170901 VALUES LESS THAN (736938) ENGINE = InnoDB,
 PARTITION pmax VALUES LESS THAN maxvalue ENGINE = InnoDB);

然后插入4条数据:

insert into test_11 values (1,"20170722"),(2,"20170822"),(3,"20170823"),(4,"20170824");

然后查看information下partitions对分区别信息的统计:

select PARTITION_NAME as "分区",TABLE_ROWS as "行数" from information_schema.partitions where table_schema="mysql_test" and table_name="test_11";
+-----------+--------+
| 分区      | 行数   |
+-----------+--------+
| p20170801 |      1 |
| p20170901 |      3 |
+-----------+--------+
2 rows in set (0.00 sec)

可以看出分区p20170801插入1行数据,p20170901插入的3行数据。
可以是用year、to_days、unix_timestamp等函数对相应的时间字段进行转换,然后分区。

list分区

和range分区一样,只是list分区面向的是离散的值

mysql> CREATE TABLE h2 (
    ->   c1 INT,
    ->   c2 INT
    -> )
    -> PARTITION BY LIST(c1) (
    ->   PARTITION p0 VALUES IN (1, 4, 7),
    ->   PARTITION p1 VALUES IN (2, 5, 8)
    -> );
Query OK, 0 rows affected (0.11 sec)

与RANGE分区的情况不同,没有“catch-all”,如MAXVALUE; 分区表达式的所有预期值应在PARTITION … VALUES IN(…)子句中涵盖。 包含不匹配的分区列值的INSERT语句失败并显示错误,如此示例所示:

mysql> INSERT INTO h2 VALUES (3, 5);
ERROR 1525 (HY000): Table has no partition for value 3

hash分区

根据用户自定义表达式的返回值来进行分区,返回值不能为负数

CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
    PARTITION BY HASH( YEAR(col3) )
    PARTITIONS 4;

如果你插入col3的数值为’2005-09-15’,那么根据以下计算来选择插入的分区:

MOD(YEAR('2005-09-01'),4)
    =  MOD(2005,4)
    =  1

key分区

根据MySQL数据库提供的散列函数进行分区

CREATE TABLE k1 (
    id INT NOT NULL,
    name VARCHAR(20),
    UNIQUE KEY (id)
)
PARTITION BY KEY()
PARTITIONS 2;

KEY仅列出零个或多个列名称。 用作分区键的任何列必须包含表的主键的一部分或全部,如果该表具有一个。 如果没有列名称作为分区键,则使用表的主键(如果有)。如果没有主键,但是有一个唯一的键,那么唯一键用于分区键。但是,如果唯一键列未定义为NOT NULL,则上一条语句将失败。

与其他分区类型不同,KEY使用的分区不限于整数或空值。 例如,以下CREATE TABLE语句是有效的:

CREATE TABLE tm1 (
    s1 CHAR(32) PRIMARY KEY
)
PARTITION BY KEY(s1)
PARTITIONS 10;

注意:对于key分区表,不能执行ALTER TABLE DROP PRIMARY KEY,因为这样做会生成错误 ERROR 1466 (HY000): Field in list of fields for partition function not found in table.

子分区

在分区的基础上再进一步分区,有时成为复合分区;

MySQL数据库允许在range和list的分区上进行HASH和KEY的子分区。例如:

CREATE TABLE ts (id INT, purchased DATE)
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) )
    SUBPARTITIONS 2 (
        PARTITION p0 VALUES LESS THAN (1990),
        PARTITION p1 VALUES LESS THAN (2000),
        PARTITION p2 VALUES LESS THAN MAXVALUE
    );
[root@mycat-3 ~]# ll /data/mysql_data_3306/mysql_test/ts*
-rw-r----- 1 mysql mysql  8596 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts.frm
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p0#SP#p0sp0.ibd
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p0#SP#p0sp1.ibd
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p1#SP#p1sp0.ibd
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p1#SP#p1sp1.ibd
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p2#SP#p2sp0.ibd
-rw-r----- 1 mysql mysql 98304 Aug  8 13:54 /data/mysql_data_3306/mysql_test/ts#P#p2#SP#p2sp1.ibd

ts表根据purchased进行range分区,然后又进行了一次hash分区,最后形成了3*2个分区,可以从物理文件证实此分区方式。可以通过subpartition语法来显示指定子分区名称。

注意:每个子分区的数量必须相同;如果一个分区表的任何子分区已经使用subpartition,那么必须表明所有的子分区名称;每个subpartition子句必须包括子分区的一个名字;子分区的名字必须是一致的

另外,对于MyISAM表可以使用index directory和data direactory来指定各个分区的数据和索引目录,但是对于innodb表来说,因为该存储引擎使用表空间自动的进行数据和索引的管理,因此会忽略指定index和data的语法。

NULL值处理

range分区处理NULL值方式:使用RANGE分区处理NULL。如果将一行插入到由RANGE分区的表中,以便用于确定分区的列值为NULL,则该行将插入最小分区。

list分区处理NULL值方式:当且仅当其中一个分区使用包含NULL的值列表定义时,由LIST分区的表将承认NULL值。 相反的是,由LIST分区的表在值列表中未明确使用NULL的表将拒绝导致分区表达式的NULL值的行,即插入null将会报错。

hash和key分区处理NULL值方式:对于由HASH或KEY分区的表,NULL的处理方式略有不同。 在这些情况下,产生NULL值的任何分区表达式将被视为其返回值为零。

进一步认识

深入认识

从MySQL 5.7.17起,MySQL服务器中的通用分区处理程序已被弃用,并且在用于给定表的存储引擎预期提供自己的(“native”)分区处理程序时,将在MySQL 8.0中删除。 目前,只有InnoDB和NDB存储引擎才能做到。

SELECT
        PLUGIN_NAME AS NAME,
        PLUGIN_VERSION AS Version,
        PLUGIN_STATUS AS STATUS
FROM    INFORMATION_SCHEMA. PLUGINS
WHERE   PLUGIN_TYPE = 'STORAGE ENGINE';

这里写图片描述

要创建分区表,可以使用MySQL服务器支持的大多数存储引擎; MySQL分区引擎在一个单独的层中运行,并且可以与任何这些相互作用。 在MySQL 5.7中,同一分区表的所有分区必须使用相同的存储引擎; 例如,您不能将MyISAM用于一个分区,而InnoDB不能用于另一个分区。

MySQL分区不能与MERGE,CSV或FEDERATED存储引擎一起使用。

要为分区表使用特定的存储引擎,只需要使用[STORAGE] ENGINE选项,就像使用非分区表一样。 但是,您应该记住,在CREATE TABLE语句中使用任何分区选项之前,需要列出[STORAGE] ENGINE,比如:

CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)
    ENGINE=INNODB
    PARTITION BY HASH( MONTH(tr_date) )
    PARTITIONS 6;

每个PARTITION子句都可以包含一个[STORAGE] ENGINE选项,但是在MySQL 5.7中没有任何效果。

分区适用于表的所有数据和索引; 您不能仅分割数据而不是分区索引,反之亦然,也不能仅对表的一部分进行分区。

限制

表的分区表达式中使用的所有列必须是表可能具有的每个唯一键的一部分,包括任何主键。 这意味着由以下SQL语句创建的这样的表,不能被分区:

CREATE TABLE tnp (
    id INT NOT NULL AUTO_INCREMENT,
    ref BIGINT NOT NULL,
    name VARCHAR(255),
    PRIMARY KEY pk (id),
    UNIQUE KEY uk (name));

从表结构中可以看出pk和uk没有共同字段,这就无法建立分区键,同时满足pk和uk包含分区键。

无论您使用的分区类型,分区总是自动编号,并在创建时依次编号,从0开始。当一个新行插入到分区表中时,这些分区号是用于识别正确的分区。例如,如果您的表使用4个分区,则这些分区的编号为0,1,2和3.对于RANGE和LIST分区类型,必须确保为每个分区号定义一个分区。对于HASH分区,用户提供的表达式必须计算为大于0的整数值。对于KEY分区,此问题由MySQL服务器内部使用的散列函数自动处理。

分区名称通常遵循管理其他MySQL标识符的规则,如表和数据库。但是,您应该注意分区名称不区分大小写。例如,以下CREATE TABLE语句所示失败:

mysql> CREATE TABLE t2 (val INT)
    -> PARTITION BY LIST(val)(
    ->     PARTITION mypart VALUES IN (1,3,5),
    ->     PARTITION MyPart VALUES IN (2,4,6)
    -> );
ERROR 1488 (HY000): Duplicate partition name mypart

发生错误是因为MySQL在分区名称mypart和MyPart之间没有区别。

算术和逻辑运算符支持: 在分区表达式中允许使用算术运算符+, - 和*。 但是,结果必须是整数值或NULL(在[LINEAR] KEY分区的情况下除外),还支持DIV运算符,但是不允许/运算符;另外分区表达式中不允许使用位运算符|,&,^,<<,>>和~。

数据库sql_mode:在创建分区表的时候,不会保留创建时的sql_mode,许多MySQL函数和运算符会根据sql_mode的不同导致运算结果改变。有时,sql_mode的改变会导致分区表的损坏,比如:

mysql> SET sql_mode='NO_UNSIGNED_SUBTRACTION';
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)

此时如果移除sql_mode中NO_UNSIGNED_SUBTRACTION选项,之后:

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

NO_UNSIGNED_SUBTRACTION可以参考:https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html

服务器SQL_mode影响分区表的同步复制。 主机和从机上的不同SQL_mode可能会导致sql语句; 这可能导致分区之间的数据分配给定主从位置不同,甚至可能导致插入主机上成功的分区表在从库上失败。 为了获得最佳效果,您应该始终在主机和从机上使用相同的服务器SQL模式。

Table locks:The process executing a partitioning operation on a table takes a write lock on the table. Reads from such tables are relatively unaffected; pending INSERT and UPDATE operations are performed as soon as the partitioning operation has completed.

存储引擎: 通过MyISAM表的分区操作,查询和更新操作通常比使用InnoDB或NDB表更快。

最大分区数: 不使用NDB存储引擎的给定表的最大可能分区数为8192(包括子分区)。如果当分区数很大,但是未达到8192时提示 Got error … from storage engine: Out of resources when opening file,可以通过增加open_files_limit系统变量的值来解决问题,当然同时打开文件的数量也可能由操作系统限制。

不支持查询缓存: 分区表不支持查询缓存,对于涉及分区表的查询,它自动禁用。 查询缓存无法启用此类查询。

分区的innodb表不支持外键。

ALTER TABLE … ORDER BY: 对分区表运行的ALTER TABLE … ORDER BY列语句只会导致每个分区中的行排序。

全文索引。 分区表不支持全文索引,即使是使用InnoDB或MyISAM存储引擎的分区表。

Spatial columns: 具有空间数据类型(如POINT或GEOMETRY)的列不能在分区表中使用。

临时表: 临时表不能分区。

subpartition问题: subpartition必须使用HASH或KEY分区。 只有RANGE和LIST分区可能被分区; HASH和KEY分区不能被子分区。

分区表不支持mysqlcheck,myisamchk和myisampack。

**与function有关的分区限制:
在分区表达式中只允许下表中显示的MySQL函数:**
这里写图片描述

MySQL5.7对于partition表的改进

  • HANDLER statements:MySQL 5.7.1分区表开始支持HANDLER语句;
  • index condition pushdown:MySQL5.7.3分区表开始支持ICP;
  • load data:MySQL5.7开始使用缓存来实现性能提升,每个分区使用130KB缓冲区来实现这一点;
  • Per-partition索引缓存:MySQL5.7开始支持使用CACHE INDEX和LOAD INDEX INTO CACHE语句对分区的MyISAM表支持索引缓存;
  • FOR EXPORT选项(FLUSH TABLES):MySQL 5.7.4分区的InnoDB表开始支持FLUSH TABLES语句FOR EXPORT选项;
  • 从MySQL 5.7.2开始,子分区支持ANALYZE,CHECK,OPTIMIZE,REPAIR和TRUNCATE操作;

详见:https://dev.mysql.com/doc/refman/5.7/en/partitioning.html

猜你喜欢

转载自blog.csdn.net/poxiaonie/article/details/76914305