一、介绍

1、什么是索引

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针
通俗的讲,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。在没有索引的情况下,数据库会按照顺序逐条遍历记录,直至找到需要的数据为止。而有了相应的索引之后,数据库会直接在索引中查找符合条件的选项,直接在索引中定位需要的数据。
通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

2、索引的原理

索引一般以文件形式存在磁盘中(也可以存于内存中),存储的索引的原理大致概括为以空间换时间,数据库在未添加索引的时候进行查询默认的是进行全量搜索,也就是进行全局扫描,有多少条数据就要进行多少次查询,然后找到相匹配的数据就把他放到结果集中,直到全表扫描完。而建立索引之后,会将建立索引的KEY值放在一个n叉树上(BTree)。因为B树的特点就是适合在磁盘等直接存储设备上组织动态查找表,每次以索引进行条件查询时,会去树上根据key值直接进行搜索。

3、索引的优点

建立索引的目的是加快对表中记录的查找或排序
① 建立索引的列可以保证行的唯一性,生成唯一的rowId
② 建立索引可以有效缩短数据的检索时间
③ 建立索引可以加快表与表之间的连接
④ 为用来排序或者是分组的字段添加索引可以加快分组和排序顺序

4、索引的缺点

① 创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大
② 创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)
③ 会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长

二、聚簇索引与非聚簇索引

1、聚簇索引

InnoDB将通过主键聚集数据,如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引,一个表只能有一个聚簇索引
聚簇索引可以说是一种索引,也可以说是一种数据存储的方式。它的索引号和对应的记录是存在一起的。存储数据的顺序和索引顺序一致
优势:当主键为自增时,做按主键的范围查询
缺点:主键如果时uuid,无法保证顺序,做范围条件查询时,开销很大

2、非聚簇索引

非聚簇索引的叶子节点仍然是索引节点,只有有指向对应数据块的指针
一张表可以最多建249个非聚簇索引,建索引需要额外的内存。索引不是越多越好。每次往表里插入数据时,要同步更新索引。所以建索引是要慎重考虑
非聚簇索引中叶子节点的记录中需要保存主键,如需访问记录中其他部分还需要通过主键回表查询。即两次索引查找。有人疑问非聚簇索引中为什么不保存记录项的物理地址呢,当然可以记录物理地址,但是主键索引更新操作带来的索引分裂合并会改变其物理地址,这样索引的维护代价比较大,而即使回表查询,主键查找速度一般较快,影响不大。另外也可以通过覆盖索引【即索引项覆盖了select中的项】避免回表查询
索引的使用 - 图1

三、索引的数据结构分类

mysql默认存储引擎innodb只显式支持B树索引,对于频繁访问的表,innodb会透明建立自适应hash索引, 即在B树索引基础上建立hash索引,可以显著提高查找效率,对于客户端是透明的,不可控制的,隐式的。支持范围查询,前缀匹配查询,等值查询,可以避免排序

1、B+TREE

索引用的数据结构是B+TREE,B是balance
① 普通索引
② 唯一性索引:普通索引+字段取值必须唯一
③ 主键自带索引
④ 联合索引(多列)

2、HASH索引

① 通过hash函数将键值直接映射为物理存储地址,使时间复杂度降低到O(1),本身存储是无序的,所以不能通过hash索引避免排序
② 很快、占内存,需要算hash值。只支持包括 “=” “in “在内的等值查询,不支持范围、前缀匹配查询

3、Mysql采用B+TREE索引的原因

B-树和B+树的区别在于B+树所有键值全部保存在叶子节点,而B-树则不然,B-树的键值根据树的结构分布在整个树上
① 遍历方便。B+树可以将键值保存在(线性表【数组或链表】)中,遍历线性表比索引树要快,因为保存在线性表中数据存储更加密集,B-Tree分散的 存储会导致更多的随机I/O,对于磁盘访问,随机I/O是比顺序I/O慢很多的,因为随机I/O需要额外的磁头寻道操作。顺序I/O有效减少寻道的次数
② 插入更新索引树时可以避免移动节点
③ 遍历任何节点的时间复杂度相同,即访问路径总是从根节点到叶子节点.相比B-树,访问时间略长.所以某些高频访问的搜索采用B-树,即访问频率越高 使其距离根节点越近
④ 范围查找方便。对于[A,B]区间的范围查找,B-树索引可以直接找到A,B对应的线性表中节点,只需要返回区间的所有节点 即为目标结果。而B-树则稍显麻烦需要继续遍历索引树

四、Mysql索引管理

1、MySQL的索引分类

① 普通索引:index:加速查找
② 唯一索引:unique:加速查找+约束(唯一)
③ 主键索引:primary key :加速查找+约束(不为空且唯一)
④ 联合索引
-primary key(id,name):联合主键索引
-unique(id,name):联合唯一索引
-index(id,name):联合普通索引
⑤ 全文索引fulltext:用于搜索很长一篇文章的时候,效果最好

2、索引创建的语句

  1. CREATE TABLE table_name[col_name data type][unique|fulltext][index|key][index_name](col_name[length])[asc|desc]
  2. unique|fulltext为可选参数,分别表示唯一索引、全文索引
  3. indexkey为同义词,两者作用相同,用来指定创建索引
  4. col_name为需要创建索引的字段列,该列必须从数据表中该定义的多个列中选择
  5. index_name指定索引的名称,为可选参数,如果不指定,默认col_name为索引值
  6. length为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度
  7. ascdesc指定升序或降序的索引值存储

3、索引的创建

(1)普通索引(单列索引):

单列索引是最基本的索引,它没有任何限制。
① 直接创建索引:CREATE INDEX index_name ON table_name(col_name);
② 修改表结构的方式添加索引:ALTER TABLE table_name ADD INDEX index_name(col_name);
③ 创建表的时候同时创建索引

  1. CREATETABLE表名 (
  2. 字段名1 数据类型 [完整性约束条件…],
  3. 字段名2 数据类型 [完整性约束条件…],
  4. [UNIQUE| FULLTEXT | SPATIAL ] INDEX| KEY
  5. [索引名] (字段名[(长度)] [ASC|DESC])
  6. );
  7. CREATETABLE`news` (
  8. `id` int(11) NOTNULLAUTO_INCREMENT ,
  9. `title` varchar(255) NOTNULL,
  10. `content` varchar(255) NULL,
  11. `time` varchar(20) NULLDEFAULTNULL,
  12. PRIMARYKEY(`id`),
  13. INDEXindex_name (title(255))
  14. )

(2)复合索引(组合索引)

复合索引是在多个字段上创建的索引。复合索引遵守“最左前缀”原则,即在查询条件中使用了复合索引的第一个字段,索引才会被使用。因此,在复合索引中索引列的顺序至关重要。
① 创建复合索引:create index index_name on table_name(col_name1,col_name2,…);
② 修改表结构的方式添加索引:alter table table_name add index index_name(col_name,col_name2,…);

(3)唯一索引

唯一索引和普通索引类似,主要的区别在于,唯一索引限制列的值必须唯一,但允许存在空值(只允许存在一条空值)
如果在已经有数据的表上添加唯一性索引的话:

  • 如果添加索引的列的值存在两个或者两个以上的空值,则不能创建唯一性索引会失败。(一般在创建表的时候,要对自动设置唯一性索引,需要在字段上加上 not null)
  • 如果添加索引的列的值存在两个或者两个以上的null值,还是可以创建唯一性索引,只是后面创建的数据不能再插入null值 ,并且严格意义上此列并不是唯一的,因为存在多个null值。

对于多个字段创建唯一索引规定列值的组合必须唯一。
比如:在order表创建orderId字段和 productId字段 的唯一性索引,那么这两列的组合值必须唯一!

“空值” 和”NULL”的概念:
1:空值是不占用空间的
2:MySQL中的NULL其实是占用空间的
长度验证:注意空值的之间是没有空格的。

  1. > select length(''),length(null),length(' ');
  2. +------------+--------------+-------------+
  3. | length('') | length(null) | length(' ') |
  4. +------------+--------------+-------------+
  5. | 0 | NULL | 1 |
  6. +------------+--------------+-------------+

① 创建唯一索引

  1. # 创建单个索引
  2. CREATE UNIQUE INDEX index_name ON table_name(col_name);
  3. # 创建多个索引
  4. CREATE UNIQUE INDEX index_name on table_name(col_name,...);

② 修改表结构

  1. # 单个
  2. ALTER TABLE table_name ADD UNIQUE index index_name(col_name);
  3. # 多个
  4. ALTER TABLE table_name ADD UNIQUE index index_name(col_name,...);

③ 创建表的时候直接指定索引

  1. CREATE TABLE `news` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT ,
  3. `title` varchar(255) NOT NULL ,
  4. `content` varchar(255) NULL ,
  5. `time` varchar(20) NULL DEFAULT NULL ,
  6. PRIMARY KEY (`id`),
  7. UNIQUE index_name_unique(title)
  8. )

(4)主键索引

主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引
① 主键索引(创建表时添加)

  1. CREATE TABLE `news` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT ,
  3. `title` varchar(255) NOT NULL ,
  4. `content` varchar(255) NULL ,
  5. `time` varchar(20) NULL DEFAULT NULL ,
  6. PRIMARY KEY (`id`)
  7. )

② 主键索引(创建表后添加)

  1. alter table tbl_name add primary key(col_name);
  2. CREATE TABLE `order` (
  3. `orderId` varchar(36) NOT NULL,
  4. `productId` varchar(36) NOT NULL ,
  5. `time` varchar(20) NULL DEFAULT NULL
  6. )
  7. alter table `order` add primary key(`orderId`);

(5)全文索引

在一般情况下,模糊查询都是通过 like 的方式进行查询。但是,对于海量数据,这并不是一个好办法,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询,这在数据量小的表,不存在性能问题,但是对于海量数据,全表扫描是非常可怕的事情,所以 like 进行模糊匹配性能很差。
这种情况下,需要考虑使用全文搜索的方式进行优化。全文搜索在 MySQL 中是一个 FULLTEXT 类型索引。FULLTEXT 索引在 MySQL 5.6 版本之后支持 InnoDB,而之前的版本只支持 MyISAM 表
全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。目前只有char、varchar,text 列上可以创建全文索引。
小技巧:在数据量较大时候,先将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

① 创建表的适合添加全文索引

  1. CREATE TABLE `news` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT ,
  3. `title` varchar(255) NOT NULL ,
  4. `content` text NOT NULL ,
  5. `time` varchar(20) NULL DEFAULT NULL ,
  6. PRIMARY KEY (`id`),
  7. FULLTEXT (content)
  8. )

② 修改表结构添加全文索引:

  1. ALTER TABLE table_name ADD FULLTEXT index_fulltext_content(col_name)

③ 直接创建索引:

  1. CREATE FULLTEXT INDEX index_fulltext_content ON table_name(col_name)

4、索引的查询和删除

索引的查看:show indexes from 表名;
或者 show keys from 表名;


索引的删除: DROP INDEX index_name ON table_name;
或者 alter table 表名 drop index 索引名;

五、正确使用索引

1、覆盖索引

分析
select * from s1 where id=123;

该sql命中了索引,但未覆盖索引。 利用id=123到索引的数据结构中定位到该id在硬盘中的位置,或者说再数据表中的位置。 但是我们select的字段为*,除了id以外还需要其他字段,这就意味着,我们通过索引结构取到id还不够, 还需要利用该id再去找到该id所在行的其他字段值,这是需要时间的,很明显,如果我们只select id, 就减去了这份苦恼,如下 select id from s1 where id=123; 这条就是覆盖索引了,命中索引,且从索引的数据结构直接就取到了id在硬盘的地址,速度很快
索引的使用 - 图2

2、联合索引

索引的使用 - 图3

3、索引合并

  1. #索引合并:把多个单列索引合并使用
  2. #分析:
  3. 组合索引能做到的事情,我们都可以用索引合并去解决,比如
  4. create index ne on s1(name,email);#组合索引
  5. 我们完全可以单独为nameemail创建索引
  6. 组合索引可以命中:
  7. select * from s1 where name='egon' ;
  8. select * from s1 where name='egon' and email='adf';
  9. 索引合并可以命中:
  10. select * from s1 where name='egon' ;
  11. select * from s1 where email='adf';
  12. select * from s1 where name='egon' and email='adf';
  13. 乍一看好像索引合并更好了:可以命中更多的情况,但其实要分情况去看,如果是name='egon' and email='adf',
  14. 那么组合索引的效率要高于索引合并,如果是单条件查,那么还是用索引合并比较合理

六、使用索引应注意的规则

1、查看索引的使用情况:

show status like ‘Handler_read%’;


handler_read_key:这个值越高越好,越高表示使用索引查询到的次数
handler_read_rnd_next:这个值越高,说明查询低效

2、使用索引时,有以下一些技巧和注意事项:

(1) 越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。
(2) 简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。
(3) 尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
(4) 索引不会包含有NULL值的列。
注:如果是同样的sql如果在之前能够使用到索引,那么现在使用不到索引,以下几种主要情况:
① 随着表的增长,where条件出来的数据太多,大于15%,使得索引失效(会导致CBO计算走索引花费大于走全表)
② 统计信息失效:需要重新搜集统计信息
③ 索引本身失效:需要重建索引

  1. #1.最左前缀匹配原则,非常重要的原则,
  2. create index ix_name_email on s1(name,email,)
  3. - 最左前缀匹配:必须按照从左到右的顺序匹配
  4. select * from s1 where name='egon'; #可以
  5. select * from s1 where name='egon' and email='asdf'; #可以
  6. select * from s1 where email='alex@oldboy.com'; #不可以
  7. mysql会一直向右匹配直到遇到范围查询(>、<、betweenlike)就停止匹配,
  8. 比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,
  9. d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  10. #2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器
  11. 会帮你优化成索引可以识别的形式
  12. #3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),
  13. 表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、
  14. 性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,
  15. 这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
  16. #4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = 2014-05-29
  17. 就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,
  18. 但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。
  19. 所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

3、常见索引失效的情况:使用explain查看索引是否生效

  1. 创建一个students表:
  2. 其中stud_id为主键!
  3. DROP TABLE IF EXISTS `students`;
  4. CREATE TABLE `students` (
  5. `stud_id` int(11) NOT NULL AUTO_INCREMENT,
  6. `name` varchar(50) NOT NULL,
  7. `email` varchar(50) NOT NULL,
  8. `phone` varchar(1) NOT NULL,
  9. `create_date` date DEFAULT NULL,
  10. PRIMARY KEY (`stud_id`)
  11. )
  12. INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`)
  13. VALUES ('1', 'admin', 'student1@gmail.com', '18729902095', '1983-06-25');
  14. INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`)
  15. VALUES ('2', 'root', '74298110186@qq.com', '2', '1983-12-25');
  16. INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`)
  17. VALUES ('3', '110', '7429811086@qq.com', '3dsad', '2017-04-28');

① 在where后使用or,导致索引失效(尽量少用or)

  1. 简单实例演示:
  2. 创建两个普通索引,
  3. CREATE INDEX index_name_email ON students(email);
  4. CREATE INDEX index_name_phone ON students(phone);
  5. 使用下面查询sql
  6. # 使用了索引
  7. EXPLAIN select * from students where stud_id='1' or phone='18729902095'
  8. # 使用了索引
  9. EXPLAIN select * from students where stud_id='1' or email='742981086@qq.com'
  10. #--------------------------
  11. # 没有使用索引
  12. EXPLAIN select * from students where phone='18729902095' or email='742981086@qq.com'
  13. # 没有使用索引
  14. EXPLAIN select * from students where stud_id='1' or phone='222' or email='742981086@qq.com'

② 使用like ,like查询是以%开头

  1. 1的基础上,还是使用 index_name_email 索引。
  2. 使用下面查询sql
  3. # 使用了index_name_email索引
  4. EXPLAIN select * from students where email like '742981086@qq.com%'
  5. # 没有使用index_name_email索引,索引失效
  6. EXPLAIN select * from students where email like '%742981086@qq.com'
  7. # 没有使用index_name_email索引,索引失效
  8. EXPLAIN select * from students where email like '%742981086@qq.com%'

③ 复合索引遵守“最左前缀”原则,即在查询条件中使用了复合索引的第一个字段,索引才会被使用

  1. 删除1的基础创建的 index_name_email index_name_phone 索引。
  2. 重新创建一个复合索引:
  3. create index index_email_phone on students(email,phone);
  4. 使用下面查询sql
  5. # 使用了 index_email_phone 索引
  6. EXPLAIN select * from students where email='742981086@qq.com' and phone='18729902095'
  7. # 使用了 index_email_phone 索引
  8. EXPLAIN select * from students where phone='18729902095' and email='742981086@qq.com'
  9. # 使用了 index_email_phone 索引
  10. EXPLAIN select * from students where email='742981086@qq.com' and name='admin'
  11. # 没有使用index_email_phone索引,复合索引失效
  12. EXPLAIN select * from students where phone='18729902095' and name='admin'

④ 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

  1. name创建一个索引!
  2. CREATE INDEX index_name ON students(name);
  3. # 使用索引
  4. EXPLAIN select * from students where name='110'
  5. # 没有使用索引
  6. EXPLAIN select * from students where name=110

⑤ 使用in导致索引失效

  1. # 使用索引
  2. EXPLAIN select * from students where name='admin'
  3. # 没有使用索引
  4. EXPLAIN SELECT * from students where name in ('admin')

⑥ DATE_FORMAT()格式化时间,格式化后的时间再去比较,可能会导致索引失效。

  1. 删除 students 上的创建的索引!重新在create_date创建一个索引!
  2. CREATE INDEX index_create_date ON students(create_date);
  3. # 使用索引
  4. EXPLAIN SELECT * from students where create_date >= '2010-05-05'
  5. # 没有使用索引
  6. EXPLAIN SELECT * from students where DATE_FORMAT(create_date,'%Y-%m-%d') >= '2010-05-05'

⑦ 对于order by、group by 、 union、 distinc 中的字段出现在where条件中时,才会利用索引!

七、应用索引进行查询

1、索引的应用
2、利用索引进行排序
① MySQL中,有两种方式生成有序结果集:一是使用filesort(慢查询),二是按索引顺序扫描
② 当索引的顺序与order by中的列顺序相同且所有的列是同一方向(全部升序或全部降序)时,可以使用索引来排序
③ 如果查询是连接多个表,仅当order by中的所有列都是第一个表的列时才会使用索引,其他情况都会使用filesort。
④ 当查询排序次数非常多,建议不在MySQL中做排序,可以放到Java中
3、数据准备:
索引的使用 - 图4
4、检测是否使用索引:
索引的使用 - 图5
索引的使用 - 图6
索引的使用 - 图7
5、注:不是索引越多越好
索引的使用 - 图8

八、索引列选择合适的数据类型

1、越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快 VARCHAR(1000) INT(10) ✔
2、简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较复杂。IP的存储问题
3、主键整型最好
4、尽量避免NULL,含有NULL的列做统计和查询优化很麻烦

九、一般原则

1、有大量重复值、经常进行范围查询(=、>、<、>=、<=、between、in)和order by、group by发生的列,可考虑建立聚簇索引。主键的范围查询有走索引,非主键的范围查询不走索引
索引的使用 - 图9
2、经常同时存取多列,且每列都含有重复值可考虑建立组合索引,其前导列一定是使用最频繁的列
3、索引不会包含有NULL值的列。只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以在数据库设计时不要让字段的默认值为NULL
4、尽量使用短索引。对字符串列进行索引,如果可能应该指定一个前缀长度。例如有一个CHAR(255)的列,如果在前10个或20个字符内,多数值时惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度,而且可以节省磁盘空间和I/O操作
5、索引列排序。MYSQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引
6、like语句操作。一般情况下不鼓励使用like操作,如果非使用不可,如果使用也是一个问题。like “%aaa%”不会使用索引,而like “aaa%”可以使用索引
7、尽量不要在列上进行运算。如select from users where YEAR(adddate) < 2007,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此可以改成:select from users where adddate < ‘2007-01-01’

十、虽然建了索引,但是不会走索引的情况

1、存在NULL值
2、NOT条件:包括<>、NOT、IN、not exists
3、like通配符的后匹配
4、函数运算
select from test where upper(name) = ‘ZHANGSHAN’
可以改为:select
from test where name =upper( ‘ZHANGSHAN’)
5、数据类型的转换:当查询条件存在隐式转换时,索引会失效,比如在数据库里id村的int类型,但在查询时,却用了下面的形式:
select * from test where id = ‘123’
6、复合索引前导列区分不大,如性别,取值只有两个
索引的使用 - 图10