hello,我又来啦!在上一节中,我们聊了索引的重要性,以及如何通过合理的设计让数据库性能起飞。然而,索引并不是万能的工具,用得好是加速器,用得不好反而可能成为“性能杀手”。本篇文章,我们将聚焦索引的另一面,讨论哪些情况下不适合创建索引,帮助你更全面地理解索引的适用场景。
哪些情况不适合创建索引
1. 在where中使用不到的字段,不要设置索引
WHERE条件(包括GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的。举个例子:
SELECT course_id,student_id, create_time
FROM student_info
WHERE student_id = 41251;
因为我们是按照student_id来进行检索的,所以不需要对其他字段创建索引,即使这些字段出现在SELECT 字段中。
2. 数据量小的表最好不要使用索引
如果表记录太少,比如少于1000个,那么是不需要创建索引的。表记录太少,是否创建索引对查询效率的影响并不大
。甚至说,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。
举例:创建表 1 :
CREATE TABLE t_without_index(
a INT PRIMARY KEY AUTO_INCREMENT,
b INT
);
提供存储过程 1 :
#创建存储过程
DELIMITER //
CREATE PROCEDURE t_wout_insert()
BEGIN
DECLARE i INT DEFAULT 1 ;
WHILE i <= 900
DO
INSERT INTO t_without_index(b) SELECT RAND()* 10000 ;
SET i = i + 1 ;
END WHILE;
COMMIT;
END //
DELIMITER ;
#调用
CALL t_wout_insert();
创建表 2 :
CREATE TABLE t_with_index(
a INT PRIMARY KEY AUTO_INCREMENT,
b INT,
INDEX idx_b(b)
);
创建存储过程 2 :
#创建存储过程
DELIMITER //
CREATE PROCEDURE t_with_insert()
BEGIN
DECLARE i INT DEFAULT 1 ;
WHILE i <= 900
DO
INSERT INTO t_with_index(b) SELECT RAND()* 10000 ;
SET i = i + 1 ;
END WHILE;
COMMIT;
END //
DELIMITER ;
#调用
CALL t_with_insert();
查询对比:
你能看到运行结果相同,但是在数据量不大的情况下,索引就发挥不出作用了。
mysql> select * from t_without_index where b = 9879 ;
+------+------+
| a | b |
+------+------+
| 1242 | 9879 |
+------+------+
1 row in set (0.00 sec)
mysql> select * from t_with_index where b = 9879 ;
+-----+------+
| a | b |
+-----+------+
| 112 | 9879 |
+-----+------+
1 row in set (0.00 sec)
结论:在数据表中的数据行数比较少的情况下,比如不到 1000 行,是不需要创建索引的。
3. 有大量重复数据的列上不要建立索引
在条件表达式中经常用到的不同值较多的列上建立索引,但字段中如果有大量重复数据,也不用创建索引。比如在学生表的"性别
"字段上只有“男”与“·女"两个不同值,因此无须建立索引。如果建立索引,不但不会提高查询效率,反而会严重降低数据更新速度
。
举例 1 :要在 100 万行数据中查找其中的 50 万行(比如性别为男的数据),一旦创建了索引,你需要先访问 50 万次索引,然后再访问 50 万次数据表,这样加起来的开销比不使用索引可能还要大。
举例 2 :假设有一个学生表,学生总数为 100 万人,男性只有 10 个人,也就是占总人口的 10 万分之 1 。
学生表 student_gender 结构如下。其中数据表中的 student_gender 字段取值为 0 或 1 , 0 代表女性, 1 代表男性。
CREATE TABLE student_gender(
student_id INT( 11 ) NOT NULL,
student_name VARCHAR( 50 ) NOT NULL,
student_gender TINYINT( 1 ) NOT NULL,
PRIMARY KEY(student_id)
)ENGINE = INNODB;
如果我们要筛选出这个学生表中的男性,可以使用:
SELECT * FROM student_gender WHERE student_gender = 1
运行结果( 10 条数据,运行时间 0.696s):
你能看到在未创建索引的情况下,运行的效率并不高。如果针对 student_gender字段创建索引呢?
SELECT * FROM student gender WHERE student_gender = 1
同样是10条数据,运行结果相同,时间却缩短到了0.052s,大幅提升了查询的效率。
其实通过这两个实验你也能看出来,索引的价值是帮你快速定位。如果想要定位的数据有很多,那么索引就失去了它的使用价值,比如通常情况下的性别字段。
在这个例子中,索引可以快速定位出男生是有用的。
4.避免对经常更新的表创建过多的索引
第一层含义: 频繁更新的字段
不一定要创建索引。因为更新数据的时候,也需要更新索引,如果索引太多,在更新索引的时候也会造成负担,从而影响效率。
第二层含义: 避免对经常更新的表
创建过多的索引,并且索引中的列尽可能少。此时,虽然提高了查询速度,同时却会降低更新表的速度。
5.不建议用无序的值作为索引
例如身份证、UUID(在索引比较时需要转为ASCII,并且插入时可能造成页分裂
)、MD5、HASH、无序长字符串等。
6.删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
7.不要定义冗余或重复的索引
① 冗余索引
举例:建表语句如下
CREATE TABLE person_info(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR( 100 ) NOT NULL,
birthday DATE NOT NULL,
phone_number CHAR( 11 ) NOT NULL,
country varchar( 100 ) NOT NULL,
PRIMARY KEY (id),
KEY idx_name_birthday_phone_number (name( 10 ), birthday, phone_number),
KEY idx_name (name( 10 ))
);
我们知道,通过idx_name_birthday_phone_number
索引就可以对name
列进行快速搜索,再创建一个专门针对name
列的索引就算是一个冗余索引
,维护这个索引只会增加维护的成本,并不会对搜索有什么好处。
② 重复索引
另一种情况,我们可能会对某个列重复建立索引
,比方说这样:
CREATE TABLE repeat_index_demo (
col1 INT PRIMARY KEY,
col2 INT,
UNIQUE uk_idx_c1 (col1),
INDEX idx_c1 (col1)
);
我们看到,col 1 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。
小结
索引是一把双刃剑
,可提高查询效率,但也会降低插入和更新的速度并占用磁盘空间。
选择索引的最终目的是为了使查询的速度变快,上面给出的原则是最基本的准则,但不能拘泥于上面的准则,在以后的学习和工作中进行不断的实践,根据应用的实际情况进行分析和判断,选择最合适的索引方式。
交流学习
最后,如果这篇文章对你有所启发,请帮忙转发给更多的朋友,让更多人受益!如果你有任何疑问或想法,欢迎随时留言与我讨论,我们一起学习、共同进步。别忘了关注我,我将持续分享更多有趣且实用的技术文章,期待与你的交流!