版权声明:其他网站转载请注明原始链接,尽量不要破坏格式 https://blog.csdn.net/landstream/article/details/79673176
你可能需要给 Primary Key 加上 Uniqle 约束了
问题描述
数据表结构:
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
id | int(10) unsigned | NO | PRI | NULL | auto_increment |
mid | varchar(50) | NO | UNI | NULL | |
url | varchar(500) | NO | NULL | ||
status | tinyint(1) | NO | 0 | ||
worker | varchar(45) | YES | NULL |
在存入了400多万条数据库之后我发现即便是简单的查询操作也变得慢地难以接受(count 查询),一开始我怀疑原因出在主键的选择上,于是后来重新设计表,添加了自增字段 id,然而,count 操作还是很慢。
mysql> select count(*) from develop.task;
+----------+
| count(*) |
+----------+
| 4570649 |
+----------+
1 row in set (2 min 44.73 sec)
看看查询计划:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | task | NULL | index | NULL | mid_UNIQUE | 152 | NULL | 4446235 | 100.00 | Using index |
还是按照 mid 这个字段来查询。不应该呀,新添加的 id 字段可是主键啊,为什么不按这个键来查,而是来按 Uniqle 的 mid 来查询呢?后来我为 id 也添加了Uniqle的约束之后,查询计划就变了,查询速度也快了许多:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | task | NULL | index | NULL | id_UNIQUE | 4 | NULL | 4446235 | 100.00 | Using index |
> mysql> select count(*) from develop.task;
+----------+
| count(*) |
+----------+
| 4570649 |
+----------+
1 row in set (1.11 sec)
问题
- 为什么 count 查询会优先选择 Uniqle 键而不是主键呢,而在主键添加Uniqle约束之后又会选择主键,我还不明白。
- 为什么key的长度会显著影响遍历速度?
前后两个查询计划中最显著的区别就是 key_len,一个长度为4字节,一个为152字节,这导致了count查询速度的巨大差异。
在试图求解这个问题过程中,觉得必要有用的参考资料:
- explain 返回结果说明:https://dev.mysql.com/doc/refman/5.6/en/explain-output.html
- 网络讨论:https://www.zhihu.com/question/34781415
其中有提到几种解决 count 问题的方法,有依靠统计量近似估计的,也有另外通过添加字段在增删改查时记录数据变化的。 - PRIMARY KEY与UNIQUE KEY对比:
来源
KEY是INDEX的同义词。当要为列创建索引,但不是主键或唯一键时使用KEY。
UNIQUE索引为其值必须是唯一的列创建约束。与PRIMARY索引不同,MySQL在UNIQUE索引中允许有NULL值。 一个表也可以有多个UNIQUE索引。
例如,users表中的用户的email和username必须是唯一的。可以为email和username列定义UNIQUE索引,如下语句所示:
在username列上添加UNIQUE索引
ALTER TABLE users
ADD UNIQUE INDEX username_unique (username ASC) ;