本文共 1567 字,大约阅读时间需要 5 分钟。
上个星期在排查一个外包项目时,发现了一个非常奇怪的现象。该项目使用MySQL 5.7数据库管理几个频繁操作的表,并对这些表进行了key分区划分,分区数为10和100。创建表的语句类似于以下所示:
CREATE TABLE `tbl_key_partition` ( `id` varchar(64) NOT NULL COMMENT 'id', `updateTime` datetime DEFAULT NULL PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 PARTITION BY KEY () PARTITIONS 100 ;
在观察过程中,发现所有的数据都分布在了偶数编号的分区上,而奇数编号的分区几乎没有数据。通过查询information_schema.partitions
表或直接执行select count(*) from tbl_key_partition partition(p91)
,可以看到奇数编号分区的ibd文件大小始终固定为96K,而实际数据并未插入其中。这表明奇数编号分区完全没有数据。
为了验证问题,用户在MySQL中手动创建了tbl_key_partition
表,并使用load_data
存储过程插入了10万条数据。存储过程的源码如下:
delimiter $$ CREATE PROCEDURE load_data (in num int) BEGIN DECLARE v INT DEFAULT 0; WHILE v < num DO INSERT INTO tbl_key_partition VALUES (concat(substring(md5(rand()), 1, 10), v), date_add('2020-01-01 00:00:00', interval v second)); SET v = v + 1; END WHILE; END$$ delimiter ; call load_data(100000);
在插入数据后,用户发现数据基本上都写到了偶数编号的分区上,而奇数编号的分区只写了极少数的数据。同时,奇数编号分区的ibd文件大小始终保持在96K,这进一步证明了奇数编号分区没有实际数据。
key分区的原理是通过MySQL内置的哈希算法对分片键计算哈希值后,再对分区数取模来实现数据的分布分区。与哈希分区类似,key分区的哈希函数由MySQL服务器提供。在NDB集群中,通常使用MD5()
函数来实现哈希计算。而对于其他存储引擎的表,MySQL服务器使用基于PASSWORD()
函数的内部散列函数来生成哈希值。
通过对问题的深入分析,发现这是一个MySQL 5.7的已知bug,尽管官方没有明确说明,但许多技术文章和论坛讨论了这一问题。具体来说,当分区数为偶数时,key分区的哈希函数可能存在偏移,导致数据无法均匀分布到所有分区中。例如,当分区数为10或100时,数据主要分布在偶数编号的分区,而奇数编号的分区几乎没有数据。
此外,许多文章指出,只有当分区数为质数时,key分区才能保证数据的均匀分布。例如,分区数为11、13或17时,数据分布较为均匀。而如果分区数为偶数(如64或128),则会导致奇数编号的分区完全没有数据。对于奇数但非质数的分区数(如63或121),数据分布虽然不均匀,但仍然会有数据存在于所有分区中。
综上所述,使用key分区在分区数为偶数时存在问题,建议避免使用key分区,而改用hash分区或范围分区来实现数据的分布分区。
转载地址:http://iubc.baihongyu.com/