深入理解MySQL之分区表原理和注意事项

分区表原理

分区表由多个相关的底层表实现,这个底层表也是由句柄对象标识,我们可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引知识在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。

分区表的操作按照以下的操作逻辑进行:

select查询

当查询一个分区表的时候,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据

insert操作

当写入一条记录的时候,分区层先打开并锁住所有的底层表,然后确定哪个分区接受这条记录,再将记录写入对应底层表

delete操作

当删除一条记录时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作

update操作

当更新一条记录时,分区层先打开并锁住所有的底层表,mysql先确定需要更新的记录再哪个分区,然后取出数据并更新,再判断更新后的数据应该再哪个分区,最后对底层表进行写入操作,并对源数据所在的底层表进行删除操作

有些操作时支持过滤的,例如,当删除一条记录时,MySQL需要先找到这条记录,如果where条件恰好和分区表达式匹配,就可以将所有不包含这条记录的分区都过滤掉,这对update同样有效。如果是insert操作,则本身就是只命中一个分区,其他分区都会被过滤掉。mysql先确定这条记录属于哪个分区,再将记录写入对应得曾分区表,无须对任何其他分区进行操作

虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的,如果存储引擎能够自己实现行级锁,例如innodb,则会在分区层释放对应表锁。

如何使用分区表

日志系统可以用分区,一般日志数量都是比较多的,按年或者月份来分区,一般来说都需要在日志系统中查询出某一段时间的历史记录,因为数据量巨大,肯定不能走全表扫描,全表扫描会引发大量的随机io,当数据量超大的时候,索引也无法起作用;此时应该考虑用分区进行解决;

并不是数据量大才需要用分区,数据量小的时候也可以用分区,怎样的场景下数据量小呢?答案是你每次查询的数据都是某一个批次的时候就可以用分区,比如说字典,业务的字典和用户类型的字典一般都是存放在同一张表里面的,且你每次查询的时候不是差一个业务或者一个用户类型,而是查询整个业务或者用户类型,这就是一个批次,此时也可以用分区来实现;

使用分区后,就可以不用索引了,因为一般使用分区的话都是范围查询,范围查询也就没必要使用索引了;已经将数据分布在不同的分区中了;

要使用索引的话,也可以,但是要分离热数据和冷数据,热数据就是经常要查询的数据,在热数据的表上加索引来加快访问速度;

分区隐患

1: NULL 空值可能导致分区过滤失效:当分区函数可能是 NULL 时,分区工作的结果就会很奇特。它会假设第一个分区是特殊的。假设使用 PARTITION BY RANGE YEAR(order_date)这样的分区方法,如果 order_date 这个列是 NULL 或者无效的日期都会存储在第一个分区。假设写了一个查询使用了这样的查询条件 :WHERE order_date BETWEEN ‘2021-01-01’ AND ‘2021-01-31’。MySQL 实际上会检查2个分区,一个是 YEAR 这个函数 在接收到无效输入时可能会返回 NULL,另一个是符合条件的值可能是 NULL(存储在第一个分区中)。这种情况对其他函数也可能,例如 TO_DAYS。如果第一个分区很大的话,就会产生问题,尤其是使用第一种不使用索引策略时。从两个分区查找数据而不是一个分区的效果是完全意外的。为了避免这种情况,应该创造“假的”第一分区,例如 PARTITION p_nulls VALUES LESS THAN (0)。如果没有无效数据存入数据表的话,这个第一分区将是空的,即便它也会被扫描,但是因为是空的或者数据量很少,对性能影响不大。这种情况在 MySQL 5.5以后,如果直接使用列进行分区的话就不需要处理,但是如果是使用函数的话就要这样做。

2: 决定使用哪个分区代价可能很高:分区实现的方式各有差异,因此实际的性能并不总是一致。特别是当遇到“这个数据行属于哪个分区”或者“如何才能查找到与查询条件匹配的数据行”这样的问题时。在众多分区的情况下来回答这样的问题很费劲。线性搜索并不总是那么有效,结果是随着分区数的增长代价也在上升。最为糟糕的形式是逐行插入。每次插入一行数据到分区的数据表,服务器都需要扫描一次使用哪个分区存放新的数据行。可以通过限制分区的数量来减轻这个问题,事实上,一般不建议超过100个分区。当然,对于其他分区类型,如键值和哈希分区则不会有这样的限制。

3:打开和锁定分区代价也可能很高:分区表带来的一个负面效应是查询时需要对每个分区进行打开和锁定。而这个过程是在过滤分区前进行的。这个代价与分区类型无关,且会影响所有的操作语句。这种影响对于短数据量的查询尤其明显,例如只查询一行数据时。这种缺陷可以通过批量操作替代单次来降低,例如一次插入多行,或 LOAD DATA INFILE,一次按范围删除数据等等。当然,限制分区的数量也是有效的。

4: 维护操作代价可能很高:有些分区的维护是很快的,例如创建或者删除分区。而其他操作,例如调整分区,就有点像 ALTER 对表的操作那样了:需要循环复制数据行。例如,调整分区会创建一个临时分区,然后将数据移入到新的分区,再删除旧的分区。

如上所述,分区并不是完美解决方案,目前版本的 MySQL还有一些其他的约束:

1:所有分区必须使用相同的存储引擎。

2: 分区函数能够选用的函数或表达式有一定的限制。

3:有些存储引擎并不支持分区。

4: 对于 MYISAM 数据表,无法使用 LOAD INDEX INTO CACHE。

5 : 对于 MYISAM 数据表,分区表需要更多的打开文件描述符,这意味着单个数据表的缓存入口可能对应多个文件描述符。因此基本配置限制了数据表的缓存以避免超出服务器操作系统的预处理量,而分区表可能导致实际超出这个限制。

当然,随着 MySQL 版本的更新迭代,对分区的支持也越来越好,并且很多分区的问题都得到了修复。

MySQL 8.0 中分区表的变化

在 MySQL 5.7 中,对于分区表,有个很重大的更新,即 InnoDB 存储引擎原生支持了分区,无需再通过 ha_partition 接口来实现。

而在 MySQL 8.0 中,则更为彻底,server 层移除了 ha_partition 接口代码。

如果要使用分区表,只能使用支持原生分区的存储引擎。在 MySQL 8.0 中,就只有 InnoDB。

这就意味着,在 MySQL 8.0 中,如果要创建 MyISAM 分区表,基本上就不可能了。

你可能感兴趣的:(#,MySQL,mysql,数据库)