为了实现区间查询、减少 IO 次数,MySQL InnoDB 的索引由 B+Tree 实现。

索引组织表

InnoDB 是以索引组织表,即为每张表的每个索引创建一棵树。

  • 聚簇(主键)索引树
    • 非叶子结点中存放主键或 row_id(MySQL 为每条记录生成的唯一id)
    • 叶子结点中存放数据
  • 非簇聚索引(二级索引)树
    • 非叶子结点中存放二级索引信息
    • 叶子结点中存放主键信息

      回表

      查询语句命中二级索引后,通过二级索引中存储的主键信息回主键索引树中查找具体数据。这个过程称之 “回表”。

      最左前缀

      命中二级索引时,可只命中最左的 N 个字段或字符串索引的最左 M 个字符; ``sql create tabletest` ( id int primary key, guid varchar(36) not null default ‘’, name varchar(10) not null default ‘’, city varchar(10) not null default ‘’, index idx_guid_name(guid, name)) engine=InnoDB;

select from test where guid=’abc’ and name=’张三’; — 命中索引 select from test where guid=’abc’; — 命中索引 select from test where guid like ‘abc%’; — 命中索引 select from test where guid like ‘%abc’; — 未命中索引 select * from test where name=’张三’; — 未命中索引

  1. <a name="G5Q6V"></a>
  2. ### 覆盖索引
  3. 查询语句命中二级索引后,若查询的所有字段均在二级索引中,则不需要回表。
  4. ```sql
  5. create table `test` (
  6. id int primary key,
  7. guid varchar(36) not null default '',
  8. name varchar(10) not null default '',
  9. index idx_guid_name(guid, name))
  10. engine=InnoDB;
  11. select name from test where guid='abc'; -- 命中索引且 name 字段存在索引中,不需要回表
  12. select id from test where guid='abc'; -- 命中索引但 id 字段不在索引中,需要回表

索引下推

MySQL version >= 5.6

查询语句命中二级索引后,若后续查询字段存在二级索引中,则不需要回表判断。

  1. create table `test` (
  2. id int primary key,
  3. guid varchar(36) not null default '',
  4. name varchar(10) not null default '',
  5. city varchar(10) not null default '',
  6. index idx_guid_name(guid, name))
  7. engine=InnoDB;
  8. -- 命中索引
  9. -- city 字段不在索引中,需回表判断
  10. select * from test where guid='abc' and city='北京';
  11. -- 根据最左前缀原则只有 guid 命中索引
  12. -- name 字段存在索引中,所以不需要回表判断,可直接在二级索引树中判断
  13. select * from test where guid like 'abc%' and name='张三';

唯一索引还是普通索引?

前提:

  1. 一个 SQL 语句命中数据后,若数据所在数据页不在内存中,会先将该数据页加载至内存中在进行后续操作。
  2. 数据页被加载到内存中时,先执行 change buffer 内记录的 sql,然后在进行当前操作。

唯一索引

  1. UPDATE 语句命中索引
  2. 数据页已在内存中,直接修改数据
  3. 数据页不在内存中,加载数据页 & 修改数据

    普通索引

  4. UPDATE 语句命中索引

  5. 数据页已在内存中,直接修改数据
  6. 数据页不在内存中,将当前语句写入 change buffer
  7. UPDATE 结束

综上所述,唯一索引适合读多写少的场景,普通索引适合写多读少的场景