MySQL 分区实践
在项目中有需要优化之前有涉及分区的表,这里记录下不同的分区方法的相关测试。
基础
在未来,native 的分区功能会被移除,只有在 InnoDB 和 NDB 才会继续保存该功能,本文是以 InnoDB 为例进行说明。简单来说,分区是指在根据表的某种用户自定义的规则,将数据分到不同的物理存储的文件中。在外部看来,这个表还是一样的,只是在 query 的时候,会根据具体的分区规则查询具体的分区。
hash
1 |
|
旨在优化的原始表的大体结构如上。里面的字段与实际表相比,只是少了部分的数据字段,索引和分区策略是一样的。当前的问题是,在这样子的分区策略下,查询的效率低下(特别是对 h 字段的跨日 \ 跨月查询),常见的是会去通过 h 做范围查询。
1 |
|
1 |
|
origin 表的测试数据为 99999 条,上面的结果可以看到,两个语句都能使用到索引,但是不同的是,后者需要扫所有的分区,以至于其所扫的 rows 比前者多。这里我们可以得出结论
- hash 分区的字段不适合范围或者比较查询,如果在 where 条件中涉及到 hash;
- 所涉及的字段,应该使用相等判断(不等于也不行)索引是在分区之后的数据范围内查询。
上述的测试结果与我们日常的使用经验相吻合,我们希望找到方法可以做范围查询。
range 分区
1 |
|
上述 SQL 是另一种分区模式,通过 range 分区,与 hash 不同的是,其需要指定某些字段具体的范围确定到某个分区。month_field 这个表与之前的相比,只是加多了一个 m 字段来存相应的月份的信息,如 201701。
这里需要注意的是这个新增的 m 字段也加到了主键中,原因是 MySQL 本身有约束,用于分区的字段,必须在所有的唯一索引列。
every unique key on the table must use every column in the table’s partitioning expression.
下面测试范围查找和精确查找
1 |
|
1 |
|
第一个查询可以看出来,对其是用范围查询,也能根据分区策略只查对应的分区而不用像 hash 那样去扫全部的分区。同样的精确查找能去到指定的分区。与 hash 相比,这种分区策略适合有范围并且分布均衡的数据。后期也可以根据需要定期扩展分区。
range 中二次计算
1 |
|
这里有一种特殊情况,在 range 中的值如果是是通过某些字段的值二次运算算出来的话,范围查询时也是会扫所有的分区。
1 |
|
list 分区
1 |
|
list 的分区精确查询和范围查询都能使用其分区策略
1 |
|
从建表语句可以看到,分区的字段的值是明确的,如果插入的数据不在指定的数据内,会报错。与 range 相比,你的数据分区可以由你自己指定,但是不能选择 MAXVALUE 类似的值,所以你必须提前规划好所有可能的值,包括是否分配均匀也是由自己确定。
1 |
|
写在后面
在分区之后,也可以通过相应的语句查询分区是否平均
1 |
|
这里主要讲了三种分区策略,其实 hash 还包括另一种简单的 hash ,我测试中用的是 liner hash,可以理解为更为平均的 hash,文档可见 这里。三种分区的策略简单如下
- hash:适用于数据范围较分散或者说暂不明确上下限,最后的查询也不涉及范围查询的情况;
- range:适合于数据需要范围查询的,并且需要数据的分布也分区的字段有关;
- list:适用于数据范围明确的,数据范围需要自己去控制。
与 hash 相比,range 和 list 也都是后期通过增加相应的分区而不移动数据的,如果是 hash 修改分区策略的话就会涉及到数据的移动
1 |
|
还有需要注意的是,如果在 range 中使用了 MAXVALUE 的话,该分区必须是最后一个分区的定义,并且你也不能往这个表加分区了
1 |
|