zoukankan      html  css  js  c++  java
  • 【MySQL】覆盖索引和回表

    • 先来了解一下两大类索引
      • 聚簇索引(也称聚集索引,主键索引等)
      • 普通索引(也成非聚簇索引,二级索引等)
    • 聚簇索引
      • 如果表设置了主键,则主键就是聚簇索引
      • 如果表没有主键,则会默认第一个NOT NULL,且唯一(UNIQUE)的列作为聚簇索引
      • 以上都没有,则会默认创建一个隐藏的row_id作为聚簇索引
    InnoDB的聚簇索引的叶子节点存储的是行记录(其实是页结构,一个页包含多行数据),InnoDB必须要有至少一个聚簇索引。
    由此可见,使用聚簇索引查询会很快,因为可以直接定位到行记录。
    • 普通索引
    普通索引也叫二级索引,除聚簇索引外的索引,即非聚簇索引。
    InnoDB的普通索引叶子节点存储的是主键(聚簇索引)的值,而MyISAM的普通索引存储的是记录指针。

    请看如下示例:

    • 建表
     CREATE TABLE IF NOT EXISTS `user`(
        -> `id` INT UNSIGNED AUTO_INCREMENT,
        -> `name` VARCHAR(60),
        -> `age` TINYINT(4),
        -> PRIMARY KEY (id),
        -> INDEX idx_age (age)
        -> )ENGINE=innodb charset=utf8mb4;

    # id 字段是聚簇索引,age 字段是普通索引(二级索引)
    • 随便加几个数据
    insert into user(name,age) values('张三',30);
    insert into user(name,age) values('李四',20);
    insert into user(name,age) values('王五',40);
    insert into user(name,age) values('刘八',10);
    mysql> select * from user;
    +----+------+-----+
    | id | name | age |
    +----+------+-----+
    |  1 | 张三 |  30 |
    |  2 | 李四 |  20 |
    |  3 | 王五 |  40 |
    |  4 | 刘八 |  10 |
    +----+------+-----+
    4 rows in set (0.06 sec)
    • 索引存储结构
    id 是主键,所以是聚簇索引,其叶子节点存储的是对应行记录的数据

    age 是普通索引(二级索引),非聚簇索引,其叶子节点存储的是聚簇索引的的值

    如果查询条件为主键(聚簇索引),则只需扫描一次B+树即可通过聚簇索引定位到要查找的行记录数据。
    如:select * from user where id = 1;

    如果查询条件为普通索引(非聚簇索引),需要扫描两次B+树,第一次扫描通过普通索引定位到聚簇索引的值,然后第二次扫描通过聚簇索引的值定位到要查找的行记录数据。
    如:select * from user where age = 30;

    1》先通过普通索引【age=30】定位到主键值 【id=1】

     2》在通过聚集索引【id=1】定位到行记录数据

    • 回表查询
    先通过普通索引的值定位到聚簇索引值,在通过聚簇索引的值定位到行记录数据,要通过扫描两次索引B+树,它的性能较扫描一次较低
    •  索引覆盖
    只需在一颗索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。
    例如:select id,age from user where age = 10;
    • 如何实现覆盖索引
    常见的方法是:将被查询的字段,建立到联合索引里去(若查询有where条件,同时where条件字段也必须为索引字段)。
    1》如实现:select id,age from user where age = 10;
    explain分析:因为age是普通索引,使用到了age索引,通过一次扫描B+树即可查询到相应的结果,这样就实现了覆盖索引
    此时的Extra列的【Using Index】表示进行了聚簇索引
    mysql> explain select id,age from user where age = 10;
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | ref  | idx_age       | idx_age | 2       | const |    1 |   100.00 | Using index |
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
    1 row in set (0.07 sec)
    2》如实现:select id,age,name from user where age = 10;
    explain分析:age是普通索引,但name列不在索引树上,所以通过age索引在查询到id和age的值后,需要进行回表再查询name的值。
    此时的Extra列的NULL表示进行了回表查询
    mysql> explain select id,age,name from user where age = 10;
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | user  | NULL       | ref  | idx_age       | idx_age | 2       | const |    1 |   100.00 | NULL  |
    +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
    1 row in set (0.05 sec)


    explain 使用方式如下:
    EXPLAIN +SQL语句
    如:EXPLAIN SELECT * FROM t1
    • 为了实现索引覆盖,需要建组合索引idx_age_name(age,name)
    mysql> drop index idx_age on user;
    mysql> create index idx_age_name on user(`age`,`name`);

    我们再次EXPLAIN分析一次:

    mysql> explain select id,age,name from user where age = 10;
    +----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | ref  | idx_age_name  | idx_age_name | 2       | const |    1 |   100.00 | Using index |
    +----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
    1 row in set (0.05 sec)

    #可见Extra的值为【Using Index】,表示使用的覆盖索引

    哪些场景适合使用索引覆盖来优化SQL:

    • 全表count查询优化
    mysql> explain select count(age) from user;
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | index | NULL          | idx_age_name | 245     | NULL |    4 |   100.00 | Using index |
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    1 row in set (0.06 sec)
    • 分页查询
    mysql> explain select id,age,name from user order by age limit 100,2;
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | index | NULL          | idx_age_name | 245     | NULL |    4 |   100.00 | Using index |
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
    1 row in set (0.06 sec)
    学而不思则罔 思而不学则殆 !
  • 相关阅读:
    如何使用sendEmail发送邮件
    Linux curl命令详解
    linux比较两个文件是否一样(linux命令md5sum使用方法)
    strace命令用法详解
    strace用法说明
    ORA-12154 TNS无法解析指定的连接标识符
    VNC远程连接阿里云Linux服务器 图形界面
    pycharm配置Git 代码管理
    FireFox浏览器-xpath快速定位插件:Xpath Checker
    odoo 前端模板引擎 Qweb
  • 原文地址:https://www.cnblogs.com/boluopabo/p/12869348.html
Copyright © 2011-2022 走看看