数据准备:
- 创建学生表和课程表
#创建学生表
CREATE TABLE `student_info`(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`student_id` INT NOT NULL,
`name` VARCHAR(20) DEFAULT NULL,
`course_id` INT NOT NULL,
`class_id` INT(11) DEFAULT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
#创建课程表
CREATE TABLE `course`(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`course_id` INT NOT NULL,
`course_name` VARCHAR(40) DEFAULT NULL,
PRIMARY KEY(`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- 创建俩函数生成随机数和随机字符串
# 创建随机产生字符串函数
DELIMITER //
CREATE FUNCTION RAND_STRING(n INT)
RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
DECLARE ret_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
while i < n DO
SET ret_str = CONCAT(ret_str,SUBSTRING(chars_str, FLOOR(1 + RAND() * 52), 1));
SET i = i + 1;
END WHILE;
RETURN ret_str;
END //
# 创建随机数函数
DELIMITER //
CREATE FUNCTION rand_num (from_num INT, to_num INT)
RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num + RAND() * (to_num - from_num + 1));
RETURN i;
END //
DELIMETER;
- 创建随机过程模拟插入数据
#创建插入课程表存储过程
DELIMITER //
CREATE PROCEDURE insert_course(max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO cours(course_id, course_name)
VALUES (rand_num(10000, 10100), rand_string(6));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER;
# 创建插入学生信息存储过程
DELIMITER //
CREATE PROCEDURE insert_stu(max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO student_info(course_id, class_id, student_id, name)
VALUES(rand_num(10000, 10100), rand_num(10000, 10200), rand_num(1, 200000), rand_string(6));
UNTIL i = max_num
END REPEAT;
COMMIT;
END//
DELIMITER;
- 调用存储过程
# 调用存储过程
CALL insert_course(100);
CALL insert_stu(1000000);
哪些情况适合创建索引
- 字段的数值有唯一性的限制
具有唯一索引的字段,即使是组合字段,也必须建成唯一索引。唯一索引对 insert 的速度损耗可以忽略,但是提高的查找速度是明显的。
- 频繁作为 where
在 student_id 字段上没有索引时查询:
耗时235ms
添加索引:
CREATE INDEX idx_sid ON student_info(student_id);
此时再次查询:
,耗时23ms,时间相差十倍左右!所以当某字段经常做 where的查询条件,一定要为其添加索引。
以下每个情况代码验证和以上一样,分别运行有相应索引和没有索引两种情况,比较运行时间。明显可以看出索引可以大大的降低查询时间。一一列举代码略显冗余,所以这里不再赘述代码,只列出剩余的情况及相关说明。
- 经常GROUP BY 或 ORDER BY的列
排序后分组或排序自然是非常快了!
- UPDATE、DELETE 的 WHERE 条件列
- DISTINCT 字段需要创建索引
排序后去重自然也更快了!
- 多表 JOIN 连接操作时,创建索引注意事项
首先,连接表的数量尽量不要超过三张,因为每增加一张表就相当于增加了一次嵌套的循环。数量级增长会非常快,严重影响查询的效率。
其次,对 WHERE 条件创建索引, 因为 WHERE 才是对数据过滤的条件。如果数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
最后,对用于连接的字段创建索引,并且该字段在多张表中的类型必须一致。比如 course_id 在两张表中都为 int(11) 类型,而不能一个为 int 另一个为varchar 类型。
- 使用列的类型小的创建索引
- 使用字符串前缀创建索引
在 VARCHAR 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
- 区分度高(散列度高)的列适合作为索引
- 使用越频繁的列要放到联合索引的越左侧
- 在多个字段都要创建索引的情况下,联合索引优于单值索引
限制索引的数目
在实际工作中,我们也需要注意平衡。索引的数目不是越多越好。我们需要限制每张表上的索引数量,建议单张表索引数量不超过6个,原因如下:
- 每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
- 索引会印象 INSERT, DELETE, UPDATE 等语句的性能,因为表中数据更改的同时,索引也会调整和更新,造成负担。
- 优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估。以生成出一个最好的计划。如果同时有多个索引都可以用于查询时,会增加Mysql优化器生成执行计划时间,降低查询性能。
哪些情况不适合创建索引
- 在 WHERE 中使用不到的字段,不要设置索引
同理,如GROUP BY 或 ORDER BY 条件里用不到的字段,也不要设置索引。原因也很简单,不需要快速定位,设置索引也没有用!
- 数据量小的表最好不要使用索引
在数据量很小的时候,原本查询时间已经很短了,不必要再添加索引。索引的回表等操作可能会使耗费时间更多!
- 有大量重复数据的列上不要建立索引
- 避免对经常需要更新的表创建过多的索引
- 不建议用无序的值作为索引
- 删除不再使用或很少使用的索引
- 不要定义冗余或者重复的索引
小结
索引是一把双刃剑,可提高查询效率,但也会降低插入和更新的速度并占用磁盘空间。选择索引的最终目的是为了使查询的速度更快,上面给出的原则是最基本的原则。也要结合实际情况来决定是否使用索引!