1.索引概述及其优缺点

1.1索引概述

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
索引的本质:索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”,满足特定查找算法。
这些数据结构以某种方式指向数据, 这样就可以在这些数据结构的基础上实现 高级查找算法 。

注:本文主要说的是MySQL57版本

1.2为什么使用索引

假如给数据使用 二叉树 这样的数据结构进行存储,如下图所示

索引的作用:能以更少的查询次数来找到数据,减少与磁盘的IO交互次数,更快地查找到数据

1.3优点

(1)类似大学图书馆建书目索引,提高数据检索的效率,降低 数据库的IO成本 ,这也是创建索引最主
要的原因。

(2)通过创建唯一索引,可以保证数据库表中每一行 数据的唯一性

(3)在实现数据的参考完整性方面,可以 加速表和表之间的连接 。换句话说,对于有依赖关系的子表和父表联合查询时,
可以提高查询速度

(4)在使用分组和排序子句进行数据查询时,可以显著减少查询中分组和排序的时间 ,降低了CPU的消耗。

1.4缺点

增加索引也有许多不利的方面,主要表现在如下几个方面: (1)创建索引和维护索引要 耗费时间 ,并 且随着数据量的增加,所耗费的时间也会增加。

(2)索引需要占磁盘空间 ,除了数据表占数据空间之 外,每一个索引还要占一定的物理空间, 存储在磁盘上 ,如果有大量的索引,索引文件就可能比数据文 件更快达到最大文件尺寸。 (3)虽然索引大大提高了查询速度,同时却会 降低更新表的速度 。当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护,这样就降低了数据的维护速度。 因此,选择使用索引时,需要综合考虑索引的优点和缺点。

(所以一般都是先删了索引,进行增删改之后再重新建索引)

2.MySQL的三层逻辑架构

简述一下:

  1. 连接层:客户端和服务器端建立连接,客户端发送 SQL 至服务器端;

  2. 服务层(SQL层):SQL接口、解析器、查询优化器、查询缓存组件(8.0版本被删掉了)

    对 SQL 语句进行查询处理;与数据库文件的存储方式无关;

  3. 存储引擎层:与数据库文件打交道,负责数据的存储和读取。

3.MySQL存储引擎

3.1存储引擎概述

存储引擎是数据库底层的组件,是数据库的核心。 使用存储引擎可以创建、查询、更新、删除数据库。存储引擎可以理解为数据库的操作系统,不同的存储引擎提供的存储方式、索引机制等也不相同,就像 Windows 系统和 Mac 系统一样。在数据库开发时,为了提高 MySQL 的灵活性和高效性,可以根据实际情况来选择存储引擎。

3.2查看mysql提供什么存储引擎:

show engines;

可以看见,MySQL57默认用的是InnoDB(5.5之后就一直如此)

3.设置系统默认的存储引擎

如果在创建表的语句中没有显式指定表的存储引擎的话,那就会默认使用 InnoDB 作为表的存储引擎。
如果我们想改变表的默认存储引擎的话,可以这样写启动服务器的命令行:

SET DEFAULT_STORAGE_ENGINE=MyISAM;

或者修改 my.cnf 文件:

default-storage-engine=MyISAM
# 重启服务
systemctl restart mysqld.service

4.设置表的存储引擎

存储引擎是负责对表中的数据进行提取和写入工作的,我们可以为 不同的表设置不同的存储引擎 ,也就是
说不同的表可以有不同的物理存储结构,不同的提取和写入方式。

4.1创建表时指定存储引擎

CREATE TABLE 表名(
建表语句;
) ENGINE = 存储引擎名称;

4.2修改表的存储引擎

ALTER TABLE 表名 ENGINE = 存储引擎名称;

比如我们修改一下 engine_demo_table 表的存储引擎:

ALTER TABLE engine_demo_table ENGINE = InnoDB;

5.MyISAM和InnoDB

5.1哪个好?—–InnoDB

除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。

MySQL5.5之前的默认存储引擎是MyISAM,5.5之后改为了InnoDB。

对比项 MyISAM InnoDB
默认安装 Y Y
默认使用 N Y
外键 不支持 支持
事务 不支持 支持
关注点 性能:节省资源、消耗少、简单业 务 事务:并发写、事务、更大资源
缓存 只缓存索引,不缓存真实数据 不仅缓存索引还要缓存真实数据,
对内存要求较 高,
而且内存大小对性能有决定性的影响
自带系 统表使 用 Y N
行表锁 表锁,即使操作一条记录也会锁住 整个表,不适合高并发的操作 行锁,操作时只锁某一行,不对其它行有影响, 适合高并发的操作

对比MyISAM的存储引擎, InnoDB写的处理效率差一些 ,并且会占用更多的磁盘空间以保存数据和索引。

5.2阿里巴巴、淘宝用哪个

  • Percona 为 MySQL 数据库服务器进行了改进,在功能和性能上较 MySQL 有很显著的提升。
  • 该版本提升了在高负载情况下的 InnoDB 的性能、为 DBA 提供一些非常有用的性能诊断工具;另外 有更多的参数和命令来控制服务器行为。
  • 该公司新建了一款存储引擎叫 Xtradb 完全可以替代 Innodb ,并且在性能和并发上做得更好
  • 阿里巴巴大部分mysql数据库其实使用的percona的原型加以修改。

5.3InnoDB中的索引方案 B+树(重要)

建一个表:

mysql> CREATE TABLE index_demo(
-> c1 INT,
-> c2 INT,
-> c3 CHAR(1),
-> PRIMARY KEY(c1)
-> ) ROW_FORMAT = Compact;

这个新建的 index_demo 表中有2个INT类型的列,1个CHAR(1)类型的列,而且我们规定了c1列为主键,
这个表使用 Compact 行格式来实际存储记录的。这里我们简化了index_demo表的行格式示意图:

把一些记录放到页里的示意图就是:

(此处略过其他迭代过程)

迭代3次:目录项记录页的目录页:

如图,我们生成了一个存储更高级目录项的 页33 ,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在 [1, 320) 之间,则到页30中查找更详细的目录项记录,如果主键值 不小于320 的话,就到页32中查找更详细的目录项记录。

我们可以用下边这个图来描述它:

这个数据结构,它的名称是 B+树

一个B+树的节点其实可以分成好多层,规定最下边的那层,也就是存放我们用户记录的那层为第 0 层,之后依次往上加。之前我们做了一个非常极端的假设:存放用户记录的页 最多存放3条记录 ,存放目录项记录的页 最多存放4条记录 。其实真实环境中一个页存放的记录数量是非常大的,假设所有存放用户记录的叶子节点代表的数据页可以存放 100条用户记录 ,所有存放目录项记录的内节点代表的数据页可以存放 1000条目录项记录 ,那么:

  • 如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放 100 条记录。

  • 如果B+树有2层,最多能存放 1000×100=10,0000 条记录。

  • 如果B+树有3层,最多能存放 1000×1000×100=1,0000,0000 条记录。

  • 如果B+树有4层,最多能存放 1000×1000×1000×100=1000,0000,0000 条记录。相当多的记 录!!!

你的表里能存放 100000000000 条记录吗?所以一般情况下,我们 用到的B+树都不会超过4层 ,那我们通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又因为在每个页面内有所谓的 **Page Directory(页目录),所以在页面内也可以通过 二分法 实现快速定位记录。

6.索引的创建与设计原则

6.1索引的声明与使用

6.1.1索引的分类

MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。

  • 功能逻辑 上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。
  • 按照 物理实现方式 ,索引可以分为 2 种:聚簇索引和非聚簇索引。
  • 按照 作用字段个数 进行划分,分成单列索引和联合索引

6.1.2创建索引

1.创建表的时候创建索引

# 01-索引的创建

#第1种:CREATE TABLE

#隐式的方式创建索引。在声明有主键约束、唯一性约束、外键约束的字段上,会自动的添加相关的索引
CREATE DATABASE dbtest2;

USE dbtest2;

CREATE TABLE dept(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20)
);

CREATE TABLE emp(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20) UNIQUE,
dept_id INT,
CONSTRAINT emp_dept_id_fk FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
);

但是,如果显式创建表时创建索引的话,基本语法格式如下:

CREATE TABLE table_name [col_name data_type]
[UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name [length]) [ASC | DESC]
  • UNIQUE 、 FULLTEXT 和 SPATIAL 为可选参数,分别表示唯一索引、全文索引和空间索引;
  • INDEX 与 KEY 为同义词,两者的作用相同,用来指定创建索引;
  • index_name 指定索引的名称,为可选参数,如果不指定,那么MySQL默认col_name为索引名;
  • col_name 为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择;
  • length 为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度;
  • ASC 或 DESC 指定升序或者降序的索引值存储。

1.创建普通索引

#显式的方式创建:
#① 创建普通的索引
CREATE TABLE book(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR,
#声明索引
INDEX idx_bname(book_name)
);

#通过命令查看索引
#方式1
SHOW CREATE TABLE book;

#方式2
SHOW INDEX FROM book;


ALTER TABLE `book` CHARSET=utf8;

ALTER TABLE `book` CONVERT TO CHARACTER SET utf8;

#性能分析工具:EXPLAIN
EXPLAIN SELECT * FROM book WHERE book_name = 'mysql高级';

2.创建唯一索引

#② 创建唯一索引
# 声明有唯一索引的字段,在添加数据时,要保证唯一性,但是可以添加null
CREATE TABLE book1(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR,
#声明索引
UNIQUE INDEX uk_idx_cmt(COMMENT)
);

SHOW INDEX FROM book1;

ALTER TABLE `book1` CONVERT TO CHARACTER SET utf8;

INSERT INTO book1(book_id,book_name,COMMENT)
VALUES(1,'Mysql高级','适合有数据库开发经验的人员学习');

INSERT INTO book1(book_id,book_name,COMMENT)
VALUES(2,'Mysql高级',NULL);

SELECT * FROM book1;

3.主键索引

#③ 主键索引
#通过定义主键约束的方式定义主键索引
CREATE TABLE book2(
book_id INT PRIMARY KEY ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR
);

SHOW INDEX FROM book2;

#通过删除主键约束的方式删除主键索引
ALTER TABLE book2
DROP PRIMARY KEY;

4.创建单列索引

#④ 创建单列索引
CREATE TABLE book3(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR,
#声明索引
UNIQUE INDEX idx_bname(book_name)
);

SHOW INDEX FROM book3;

5.创建联合索引

#⑤ 创建联合索引
CREATE TABLE book4(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR,
#声明索引
INDEX mul_bid_bname_info(book_id,book_name,info)
);

SHOW INDEX FROM book4;

#分析
EXPLAIN SELECT * FROM book4 WHERE book_id = 1001 AND book_name = 'mysql';

EXPLAIN SELECT * FROM book4 WHERE book_name = 'mysql';

6.创建全文索引

#⑥ 创建全文索引
CREATE TABLE test4(
id INT NOT NULL,
NAME CHAR(30) NOT NULL,
age INT NOT NULL,
info VARCHAR(255),
FULLTEXT INDEX futxt_idx_info(info(50))
)

SHOW INDEX FROM test4;

7.创建空间索引

空间索引创建中,要求空间类型的字段必须为 非空 。 举例:创建表test5,在空间类型为GEOMETRY的字段上创建空间索引,SQL语句如下:

CREATE TABLE test5(
geo GEOMETRY NOT NULL,
SPATIAL INDEX spa_idx_geo(geo)
) ENGINE=MyISAM;

2.在已经存在的表上创建索引

ALTER TABLE … ADD …

#第2种:表已经创建成功

#① ALTER TABLE ... ADD ...

CREATE TABLE book5(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR
);

SHOW INDEX FROM book5;

ALTER TABLE book5 ADD INDEX idx_cmt(COMMENT);

ALTER TABLE book5 ADD UNIQUE uk_idx_bname(book_name);

ALTER TABLE book5 ADD INDEX mul_bid_bname_info(book_id,book_name,info);

CREATE INDEX … ON …

#② CREATE INDEX ... ON ...

CREATE TABLE book6(
book_id INT ,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100) ,
COMMENT VARCHAR(100),
year_publication YEAR
);

SHOW INDEX FROM book6;

CREATE INDEX idx_cmt ON book6(COMMENT);

CREATE UNIQUE INDEX uk_idx_bname ON book6(book_name);

CREATE INDEX mul_bid_bname_info ON book6(book_id,book_name,info);

6.1.3删除索引

方式1:ALTER TABLE …. DROP INDEX ….

# 02-索引的删除

SHOW INDEX FROM book5;

#方式1ALTER TABLE .... DROP INDEX ....
ALTER TABLE book5
DROP INDEX idx_cmt;

方式2:DROP INDEX … ON …

#方式2DROP INDEX ... ON ...
DROP INDEX uk_idx_bname ON book5;

测试

#测试:删除联合索引中的相关字段,索引的变化
ALTER TABLE book5
DROP COLUMN book_name;

ALTER TABLE book5
DROP COLUMN book_id;

ALTER TABLE book5
DROP COLUMN info;
SHOW ENGINES;

7.索引的设计原则

7.1使用

#04-索引的设计原则

#1. 数据的准备

CREATE DATABASE atguigudb1;

USE atguigudb1;

#1.创建学生表和课程表
CREATE TABLE `student_info` (
`id` INT(11) 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;


#函数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 ;

SELECT @@log_bin_trust_function_creators;

SET GLOBAL log_bin_trust_function_creators = 1;


#函数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 ;

# 存储过程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 ;


# 存储过程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 ;

#调用存储过程:
CALL insert_course(100);

SELECT COUNT(*) FROM course;

CALL insert_stu(1000000);

SELECT COUNT(*) FROM student_info;

#2. 哪些情况适合创建索引
#① 字段的数值有唯一性的限制
#② 频繁作为 WHERE 查询条件的字段
#查看当前stduent_info表中的索引
SHOW INDEX FROM student_info;
#student_id字段上没有索引的:
SELECT course_id, class_id, NAME, create_time, student_id
FROM student_info
WHERE student_id = 123110; #276ms

#给student_id字段添加索引
ALTER TABLE student_info
ADD INDEX idx_sid(student_id);

#student_id字段上有索引的:
SELECT course_id, class_id, NAME, create_time, student_id
FROM student_info
WHERE student_id = 123110; #43ms

#③ 经常 GROUP BYORDER BY 的列

#student_id字段上有索引的:
SELECT student_id, COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100; #41ms

#删除idx_sid索引
DROP INDEX idx_sid ON student_info;


#student_id字段上没有索引的:
SELECT student_id, COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100; #866ms

#再测试:
SHOW INDEX FROM student_info;

#添加单列索引
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

#修改sql_mode

SELECT @@sql_mode;

SET @@sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';

#添加联合索引
ALTER TABLE student_info
ADD INDEX idx_sid_cre_time(student_id,create_time DESC);

SELECT student_id, COUNT(*) AS num FROM student_info
GROUP BY student_id
ORDER BY create_time DESC
LIMIT 100; #0.257s

#再进一步:
ALTER TABLE student_info
ADD INDEX idx_cre_time_sid(create_time DESC,student_id);

DROP INDEX idx_sid_cre_time ON student_info;


#会使用idx_sid单列索引而不会使用idx_cre_time_sid索引
SELECT student_id, COUNT(*) AS num FROM student_info
GROUP BY student_id
ORDER BY create_time DESC
LIMIT 100; #3.790s
# 总结:创建联合索引的时候,先写GROUP BY字段的,后写ORDER BY字段的

#④ UPDATEDELETEWHERE 条件列
SHOW INDEX FROM student_info;


UPDATE student_info SET student_id = 10002
WHERE NAME = '462eed7ac6e791292a79'; #0.633s

#添加索引
ALTER TABLE student_info
ADD INDEX idx_name(NAME);


UPDATE student_info SET student_id = 10001
WHERE NAME = '462eed7ac6e791292a79'; #0.001s

# ⑤ DISTINCT 字段需要创建索引

# ⑥ #### 多表 JOIN 连接操作时,创建索引注意事项

#首先,`连接表的数量尽量不要超过 3 张`,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。

#其次,`对 WHERE 条件创建索引`,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。

#最后,`对用于连接的字段创建索引`,并且该字段在多张表中的`类型必须一致`。比如 course_id 在 student_info 表和 course 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。


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'; #0.001s

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'; #0.227s


#⑦使用列的类型小的创建索引

#⑧使用字符串前缀创建索引

#⑨ 区分度高(散列性高)的列适合作为索引

#⑩ 使用最频繁的列放到联合索引的左侧
SELECT *
FROM student_info
WHERE student_id = 10013 AND course_id = 100;

#补充:在多个字段都要创建索引的情况下,联合索引优于单值索引

# 3. 哪些情况不适合创建索引
# ① 在where中使用不到的字段,不要设置索引

# ② 数据量小的表最好不要使用索引

# ③ 有大量重复数据的列上不要建立索引
#结论:当数据重复度大,比如`高于 10% `的时候,也不需要对这个字段使用索引。

#④ 避免对经常更新的表创建过多的索引

#⑤ 不建议用无序的值作为索引

# ⑥ 删除不再使用或者很少使用的索引

# ⑦ 不要定义冗余或重复的索引

7.2哪些情况适合创建索引

1. 字段的数值有唯一性的限制

2.频繁作为 WHERE 查询条件的字段

3.经常 GROUP BY 和 ORDER BY 的列

4.UPDATE、DELETE 的 WHERE 条件列

5.DISTINCT 字段需要创建索引

6.多表 JOIN 连接操作时,创建索引注意事项

连接表的数量尽量不要超过 3 张、 对 WHERE 条件创建索引 、 对用于连接的字段创建索引,并且该字段在多张表中的 类型必须一致

7.使用列的类型小的创建索引

8.使用字符串前缀创建索引

公式:

count(distinct left(列名, 索引长度))/count(*)

一般长度为 20 的索引能区分90%的问题

9.区分度高(散列性高)的列适合作为索引

eg:id适合,gender不适合

10.使用最频繁的列放到联合索引的左侧

11.在多个字段都要创建索引的情况下,联合索引优于单值索引

因为查单值得时候可以用联合索引

补充:限制索引的数目(一般6个)

7.3哪些情况不适合创建索

① 在where中使用不到的字段,不要设置索引

② 数据量小的表最好不要使用索引

③ 有大量重复数据的列上不要建立索引

#结论:当数据重复度大,比如高于 10% 的时候,也不需要对这个字段使用索引。

④ 避免对经常更新的表创建过多的索引

⑤ 不建议用无序的值作为索引

⑥ 删除不再使用或者很少使用的索引

⑦ 不要定义冗余或重复的索引