[3.1 在 where 筛选条件当中使用不到的字段,不要设置索引](#3.1 在 where 筛选条件当中使用不到的字段,不要设置索引)
[3.2 数据量小的表最好不要使用索引](#3.2 数据量小的表最好不要使用索引)
[3.3 有大量重复数据的列上不要建立索引](#3.3 有大量重复数据的列上不要建立索引)
[3.4 避免对经常更新的表创建过多的索引](#3.4 避免对经常更新的表创建过多的索引)
[3.5 不建议用无序的值作为索引](#3.5 不建议用无序的值作为索引)
[3.6 删除不再使用或者很少使用的索引](#3.6 删除不再使用或者很少使用的索引)
[3.7 不要定义冗余或重复的索引](#3.7 不要定义冗余或重复的索引)
[4. 最后:](#4. 最后:)
1. 测试数据准备
第1步:创建数据库、创建表
mysql复制代码
CREATE DATABASE dbtest3;
USE dbtest3;
#1.创建学生表和课程表
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;
第2步:创建模拟数据必需的存储函数
说明: 创建函数,假如报错:
sh复制代码
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
#函数1:创建随机产生字符串函数
DELIMITER //
CREATE FUNCTION rand_string(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
mysql复制代码
#函数2:创建随机数函数
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 //
DELIMITER;
第3步:创建插入模拟数据的存储过程
mysql复制代码
# 存储过程1:创建插入课程表存储过程
DELIMITER //
CREATE PROCEDURE insert_course( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO course (course_id, course_name ) VALUES (rand_num(10000,10100),rand_string(6));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER;
mysql复制代码
# 存储过程2:创建插入学生信息表存储过程
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 ;
SELECT course_id,class_id,`name`,create_time,student_id
FROM student_info
WHERE student_id = 123110;
mysql复制代码
# 给 student_id 字段添加上索引之后
ALTER TABLE student_info
ADD INDEX ids_sid(student_id);
2.3 经常 GROUP BY 和 ORDER BY 的列
索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者 使用 ORDER BY 对数据进行排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多 个,那么可以在这些列上建立 组合索引 。
mysql复制代码
# 经常 GROUP BY 和 ORDER BY 的列
# student_id 字段上有索引的
SELECT student_id, COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100;
mysql复制代码
# 删除 idx_sid 索引
DROP INDEX ids_sid ON student_info;
# student_id 字段上没有索引的
SELECT student_id, COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100;
如果同时有 GROUP By 和 ORDER BY 的情况:比如我们按照 student_id 进行分组,同时按照创建时间降序的方式进行排序,这时我们就需要同时进行 GROUP BY 和 ORDER BY,那么是不是需要单独创建 student_id 的索引和 create_time 的索引呢?
当我们对 student_id 和 create_time 分别创建索引,执行下面的SQL查询
mysql复制代码
# 添加单例索引
ALTER TABLE student_info
ADD INDEX idx_sid(student_id);
ALTER TABLE student_info
ADD INDEX idx_cre_time(create_time);
SELECT student_id, COUNT(*) AS num
FROM student_info
GROUP BY student_id
ORDER BY create_time DESC
LIMIT 100; # 5.212s
# 添加为 student_id 和 create_time 组合创建联合索引
ALTER TABLE student_info
ADD INDEX idx_sid_cre_time(student_id,create_time DESC);
SHOW INDEX FROM student_info;
再次执行该SQL语句:
2.4 UPDATE、DELETE 的 中的WHERE 条件列添加索引
对数据按照某个条件进行查询后再进行 UPDATE 或 DELETE 的操作,如果对 WHERE 字段创建了索引,就 能大幅提升效率。原理是因为我们需要先根据 WHERE 条件列检索出来这条记录,然后再对它进行更新或 删除。 如果进行更新的时候,更新的字段是非索引字段,提升的效率会更明显,这是因为非索引字段更 新不需要对索引进行维护。
mysql复制代码
# update, delete 的 where 条件列添加索引
SHOW INDEX FROM student_info;
UPDATE student_info SET student_id = 10002
WHERE NAME = 'jfiodasjfoj';
为 name 添加上索引
mysql复制代码
# 添加索引
ALTER TABLE student_info
ADD INDEX idx_name(`name`);
UPDATE student_info SET student_id = 10002
WHERE NAME = 'jfiodasjfoj';
SELECT s.course_id, NAME, s.student_id, c.course_name
FROM student_info s JOIN course c
ON s.course_id = c.course_id
WHERE NAME = '462eed7ac6e791292a79';
mysql复制代码
DROP INDEX idx_name ON student_info;
SELECT s.course_id, NAME, s.student_id, c.course_name
FROM student_info s JOIN course c
ON s.course_id = c.course_id
WHERE NAME = '462eed7ac6e791292a79';
我们在定义表结构的时候要显式的指定列的类型,以整数类型为例,有TINYINT,MEDIUMINT,INT,BIGINT 等,它们占用的存储空间依次递增,能表示的整数范围当然也是依次递增,如果我们想要对某个整数列建立索引的话,在表示的整数范围允许的情况下,尽量让索引列使用较小的类型,比如我们能使用 INT 就不要使用 BIGINT ,能使用 MEDIUMINT 就不要使用 INT 。
2.8 使用字符串前缀创建索引
创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引
创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引
mysql复制代码
create table shop(address varchar(120) not null);
alter table shop add index(address(12));
CREATE TABLE t_without_index(
a INT PRIMARY KEY AUTO_INCREMENT, b INT
);
提供存储过程1:
mysql复制代码
#创建存储过程
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:
mysql复制代码
CREATE TABLE t_with_index(
a INT PRIMARY KEY AUTO_INCREMENT, b INT,
INDEX idx_b(b) );
创建存储过程2:
mysql复制代码
#创建存储过程
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复制代码
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)
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 列的索引就算是一个 冗余索引 ,维护这个索引只会增加维护的成本,并不会对搜索有 什么好处.。
重复索引:
另一种情况,我们可能会对某个列 重复建立索引 ,比方说这样:
mysql复制代码
CREATE TABLE repeat_index_demo (
col1 INT PRIMARY KEY,
col2 INT,
UNIQUE uk_idx_c1 (col1),
INDEX idx_c1 (col1)
);