zoukankan      html  css  js  c++  java
  • MySQL InnoDB 索引(Index)

    MySQL使用索引加快数据的查询,没有索引的话,在查找数据的时候,MySQL会全表扫描。

    数据越多,扫描的速度就会越慢。

    InnoDB存储引擎是MySQL默认的存储引擎,本文整理的是InnoDBIndex

    索引概念

    索引是一个数据结构,一般RDBMS是BTree,通过保存排序后的key value数据字段,以便用来快速查询数据。

    索引分类:聚簇索引(Clustered IndexPrimary Index)和 二级索引(Secondary Index

    clustered index

    clustered index 其实就是根据primary key排过序的数据表,因为clustered index本身和数据一起存储在同一个表的。

    clustered index 使数据行根据创建clustered index所用的字段排序存储;因此,一个InnoDB数据表只能有一个clustered index。

    clustered index创建:

    1. 如果定义了primary key,MySQL则会使用该primary key作为clustered index;
    2. 如果没有定义primary key,MySQL怎会使用第一个字段不为空并且是uniqueindex作为clustered index;
    3. 如果db table既没有primary key,又没有满足条件的unique key,MySQL则会创建一个名为GEN_CLUST_INDEX的clustered index。

    所以,每个InnoDB数据表都会有clustered index。

    Secondary index

    clustered index都是 secondary index

    在InnoDB数据表中,secondary index包含的数据记录是: primary key + 创建secondary index的列值; MySQL会使用primary key回溯原表(也是clustered index)查询具体的数据行。

    MySQL Prefix Index

    当在column上创建secondary index时,MySQL会把此column数据单独存在BTree上。如果column是string类型的,index就会消耗很多硬盘空间,并可能会导致插入数据变很慢。

    为了解决此类问题,MySQL可以创建Prefix Index,

    CREATE TABLE table_name(
        column_list,
        INDEX(column_name(length))
    );
    或
    CREATE INDEX index_name ON table_name(column_name(length));
    
    • 如果string类型是CHAR, VARCHAR, 和 TEXT,则length是字符的个数。
    • 如果string类型是 BINARY, VARBINARY, 和 BLOB,则length是字节的个数。
    • 对于 CHAR, VARCHAR, BINARY, 和 VARBINARY类型的列,MySQL是可选创建prefix key的。
    • 如果想给BLOBTEXT 类型的列创建index,则必须创建prefix index

    如何决定创建prefix indexlength呢?

    • 一个合理的长度,并让唯一性最大化; 即选了一个合理的长度后,所有行此长度的字符串前缀基本都是唯一的、不重复的。

    MySQL Invisible Index

    MySQL 8.x才有的

    CREATE INDEX index_name ON table_name( c1, c2, ...) INVISIBLE;
    
    ALTER TABLE table_name ALTER INDEX index_name [VISIBLE | INVISIBLE];
    
    • 默认情况indexs是可见的visible,MySQL提供关键字 VISIBLEINVISIBLE来设置index的可见性;
    • MySQL primary key是不可以为 invisible的;
    • MySQL查询优化器(query optimizer)不使用invisible的index;那这个invisible有啥用呢?
      • 有些时候,我们可以通过设置index visible或invisible来看看这个index对查询性能有没有作用。

    注意:
    Query Optimizer是否使用invisible indexes是受 optimizer_switch 系统变量的 use_invisible_indexes 参数控制。

    • use_invisible_indexes = off(默认值),则MySQL Server忽略invisible的indexes;
    • use_invisible_indexes = on,则MySQL Server会把invisible的indexes参与到执行计划中;

    MySQL Descending Index

    DROP TABLE IF EXISTS asc_desc_index_test;
    
    create table asc_desc_index_test
    (
    	id int not null auto_increment,
        test_name varchar(50),
        test_age int,
        primary key(id),
        index idx_name_asc_age_desc(test_name ASC, test_age DESC)
    );
    
    • MySQL 5.7 或之前版本,DESC关键字会被忽略掉。
    mysql> show create table asc_desc_index_test G;
    *************************** 1. row ***************************
           Table: asc_desc_index_test
    Create Table: CREATE TABLE `asc_desc_index_test` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `test_name` varchar(50) DEFAULT NULL,
      `test_age` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_name_asc_age_desc` (`test_name`,`test_age`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1
    
    • MySQL 8.x版本:
    mysql> show create table asc_desc_index_test G;
    *************************** 1. row ***************************
           Table: asc_desc_index_test
    Create Table: CREATE TABLE `asc_desc_index_test` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `test_name` varchar(50) DEFAULT NULL,
      `test_age` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_name_asc_age_desc` (`test_name`,`test_age` DESC)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    

    MySQL composite index

    MySQL composite index由多个columns创建的index。

    MySQL Server在使用组合index的时候,遵循leftmost prefix of the index

    语法:

    CREATE TABLE table_name (
        c1 data_type PRIMARY KEY,
        c2 data_type,
        c3 data_type,
        c4 data_type,
        INDEX index_name (c2,c3,c4)
    );
    
    或
    
    CREATE INDEX index_name ON table_name(c2,c3,c4);
    

    示例:

    create index idx_first_last_name on customer(first_name, last_name);
    

    下面用是idx_first_last_name有作用的的查询:

    explain select * from customer where first_name = 'a' ;
    explain select * from customer where first_name like 'a' ;
    explain select * from customer where first_name like 'a%' ;
    explain select * from customer where first_name = 'a' and last_name = 'b';
    explain select * from customer where first_name = 'a' and last_name in ('b', 'c');
    explain select * from customer where last_name = 'b' and  first_name = 'a' ;
    

    没作用的查询:

    explain select * from customer where first_name like '%a' ;
    explain select * from customer where first_name like '%a%' ;
    explain select * from customer where first_name = 'a' or last_name = 'b';
    explain select * from customer where last_name = 'b';
    

    Unique index

    Primary Keycolumn也是unique的,但是一个表只能有一个primary key。

    如果想设置多个column值的唯一性,可以使用unique index

    CREATE UNIQUE INDEX index_name
    ON table_name(index_column_1,index_column_2,...);
    

    另一个方法是使用 UNIQUE constraint,创建了unique constraint后,MySQL会在后台也创建Unique Index

    CREATE TABLE table_name(
    ...
       UNIQUE KEY(index_column_,index_column_2,...) 
    );
    
    或
    
    ALTER TABLE table_name
    ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
    
    • 这里 UNIQUE KEY,也可以换成 UNIQUE INDEX,它们是同义的。

    注意

    • MySQL中,如果column是unique的,column里面可以有多个NULL值;MySQL认为NULL都是唯一的,这是MySQL专门这么设计的。

    创建索引

    CREATE TABLE t(
       c1 INT PRIMARY KEY,
       c2 INT NOT NULL,
       c3 INT NOT NULL,
       c4 VARCHAR(10),
       INDEX (c2,c3) 
    );
    
    CREATE INDEX index_name ON table_name (column_list)
    

    如果不指定索引的类型,MySQL默认创建BTree Index

    查看表的索引

    SELECT 
        index_name, 
        is_visible
    FROM
        information_schema.statistics
    WHERE
        table_schema = 'mysql_practice' AND table_name = 'book';
    

    mysql> show indexes from bookG;
    或
    mysql> show keys in bookG;
    
    *************************** 1. row ***************************
            Table: book
       Non_unique: 0
         Key_name: PRIMARY
     Seq_in_index: 1
      Column_name: id
        Collation: A
      Cardinality: 178
         Sub_part: NULL
           Packed: NULL
             Null: 
       Index_type: BTREE
          Comment: 
    Index_comment: 
          Visible: YES
       Expression: NULL
    *************************** 2. row ***************************
            Table: book
       Non_unique: 1
         Key_name: category_id
     Seq_in_index: 1
      Column_name: category_id
        Collation: A
      Cardinality: 3
         Sub_part: NULL
           Packed: NULL
             Null: 
       Index_type: BTREE
          Comment: 
    Index_comment: 
          Visible: YES
       Expression: NULL
    *************************** 3. row ***************************
    

    上面显示表indexes的sql语句中,

    • indexskeys是同义的
    • fromin 是同义的

    Show Indexes返回字段的信息:

    • Table: 所在表名
    • Non_unique:index是否可以包含重复记录,1是可以重复,0是不可以。
    • Key_name:index的名字,注意 primary key 的index名字是 PRIMARY
    • Seq_in_index: 如果index包含多个column时,index中column的sequence number,从1开始;如果index只包含一个column,那么这个值总是1;
    *************************** 4. row ***************************
            Table: book
       Non_unique: 1
         Key_name: idx_book_no_name     --index名字
     Seq_in_index: 1
      Column_name: no   --index包含的第一个column
        Collation: A
      Cardinality: 178
         Sub_part: NULL
           Packed: NULL
             Null: 
       Index_type: BTREE
          Comment: 
    Index_comment: 
          Visible: YES
       Expression: NULL
    *************************** 5. row ***************************
            Table: book
       Non_unique: 1
         Key_name: idx_book_no_name     --index名字和上面一样
     Seq_in_index: 2
      Column_name: name     --index包含的第二个column
        Collation: A
      Cardinality: 178
         Sub_part: NULL
           Packed: NULL
             Null: 
       Index_type: BTREE
          Comment: 
    Index_comment: 
          Visible: YES
       Expression: NULL
    
    • column_name:index包含的列名;
    • Collation:表名column是如何排序的,A表示升序,B表示降序,NULL表示没有排序;
    • Cardinality:index里不重复记录的预估值;值越大,index就越大概率被用到查询里;
    • Sub_part:index的前缀,如果整个字段都是index,则值为NULL;否则,显示column有多少个字符被index。
    • Packed
    • Null:表示column是否可空;yes表示可空,空白表示不可空;
    • Index_type:idex的方法,如BTREE, HASH, RTREE, or FULLTEXT
    • Comment: index的备注;
    • index_comment
    • Visible:index是否可见,即是否会被用到query中;
    • Expression

    删除表索引

    DROP INDEX index_name ON table_name [algorithm_option | lock_option];
    
    ALGORITHM [=] {DEFAULT|INPLACE|COPY}
    LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}
    

    如果是drop clustered index:

    DROP INDEX `PRIMARY` ON table_name; -- PRIMARY须用引号标识符,因为PRIMARY是关键字。
    

    ALGORITHM

    • DEFAULT
    • INPLACE
    • COPY
    • INSTANT

    LOCK

    • DEFAULT
    • NONE
    • SHARED
    • EXCLUSIVE

    示例:

    DROP INDEX idx_book_no ON book
    ALGORITHM = INPLACE 
    LOCK = DEFAULT;
    

    MySQL FORCE INDEX 和 USE INDEX

    MySQL Use index

    SELECT select_list
    FROM table_name USE INDEX(index_list)
    WHERE condition;
    
    • 注意,实际query执行的时候可能使用指定的index list

    MySQL force index

    SELECT * 
    FROM table_name 
    FORCE INDEX (index_list)
    WHERE condition;
    
    • 注意,实际query执行的时候一定要使用指定的index list

    索引效果

    使用explain关键字查看sql query的执行情况,索引的使用情况。

    给book表no字段加索引前:

    mysql> EXPLAIN SELECT * FROM mysql_practice.book WHERE no = 'f457948c80ac11';
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | book  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  178 |    10.00 | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    

    给book表no字段加了索引后:

    mysql> EXPLAIN SELECT * FROM mysql_practice.book WHERE no = 'f457948c80ac11';
    +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | book  | NULL       | ref  | idx_book_no   | idx_book_no | 202     | const |    1 |   100.00 | NULL  |
    +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
    

    参考资料

    1. 15.6.2 Indexes
    2. MySQL体系构架、存储引擎和索引结构
    3. MySQL Index
    4. MySQL Performance Boosting with Indexes and Explain
    5. 8.8.2 EXPLAIN Output Format

    微信公众号:编码者频道

    扫描二维码(或者长按识别二维码)关注公众号获取最新信息。

    本文版权归作者和博客园共有,欢迎转载,

    转载请保留原文链接,谢谢!

  • 相关阅读:
    最长上升子序列(实验回顾)
    数据库应用开发一、vs
    全文检索
    mangtomant 增删改查
    django
    SQLAlchemy 增删改查 一对多 多对多
    Flask-Sqlalchemy—常用字段类型说明
    flask
    文件下载
    python连接mongodb
  • 原文地址:https://www.cnblogs.com/codesee/p/14581674.html
Copyright © 2011-2022 走看看