MySQL Index--InnoDB引擎的主键索引

Posted gaogao67

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL Index--InnoDB引擎的主键索引相关的知识,希望对你有一定的参考价值。

无主键表

在Innodb存储引擎中,每张表都会有主键,数据按照主键顺序组织存放,该类表成为索引组织表 Index Ogranized Table

如果表定义时没有显示定义主键,则会按照以下方式选择或创建主键:

a) 先判断表中是否有"非空的唯一索引",如果有
    1) 如果仅有一条"非空唯一索引",则该索引为主键
    2) 如果有多条"非空唯一索引",根据索引索引的先后顺序,选择第一个定义的非空唯一索引为主键。
b) 如果表中无"非空唯一索引",则自动创建一个6字节大小的指针作为主键。

如果主键索引只有一个索引键,那么可以使用_rowid来显示主键,如:

## 删除测试表
DROP TABLE IF EXISTS tb2001;

## 创建测试表
CREATE TABLE `tb2001` (
    `id` int(11) NOT NULL,
    `c1` int(11) DEFAULT NULL,
    UNIQUE uni_id (id),
    INDEX idx_c1(c1)
) ENGINE = InnoDB CHARSET = utf8;

## 插入测试数据
INSERT INTO tb2001 (id, c1)
SELECT 1, 1;

INSERT INTO tb2001 (id, c1)
SELECT 2, 2;

INSERT INTO tb2001 (id, c1)
SELECT 4, 4;

## 查看数据和_rowid
SELECT *, _rowid
FROM tb2001;
+----+------+--------+
| id | c1   | _rowid |
+----+------+--------+
|  1 |    1 |      1 |
|  2 |    2 |      2 |
|  4 |    4 |      4 |
+----+------+--------+

可以发现,上面的_rowid与id的值相同,因为id列是表中第一个唯一且NOT NULL的索引。

 

强制主键索引

由于主键索引只能为PRIMARY:

SHOW INDEXES IN tb2001 \G
*************************** 1. row ***************************
        Table: tb2001
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 2
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 

因此在强制走主键索引FORCE INDEX(PRIMARY)时,使用:

SELECT * 
FROM tb2001 FORCE INDEX(PRIMARY)
WHERE C1=2;

 

非聚集索引中的聚集索引键

mysql 5.6.9版本前,Innodb的非聚集索引中包含聚集索引的索引键,但只起到通过非聚集索引定位记录的作用,但在MySQL 5.6.9之后版本中,优化器会考虑非聚集索引中包含的聚集索引键来提升查询性能,并提供优化器选项use_index_extensions来开启或关闭该特性。

假设有表TB1(ID,C1,C2), ID为主键聚集索引,然后在列C1建立索引IDX_C1(C1):

在MySQL 5.6版本前,索引类似于IDX_C1(C1) INCLUDE(ID);

在MySQL 5.6版本中,索引类似于IDX_C1(C1,ID);

无论是MySQL 5.5还是MySQL 5.6版本中,非聚集索引上的数据都是先按照非聚集索引键在按照聚集索引键进行排序,即在非聚集索引键上值相同的记录会按照聚集索引进行排序。

 

对于查询:

SELECT * 
FROM TB1
WHERE C1=ABC
AND ID>10
AND ID<1000

在MySQL 5.5版本中,需要按照索引IDX_C1扫描所有C1=‘ABC‘的记录,再根据ID进行过滤。

在MySQL 5.6版本中,进行按照索引IDX_C1就可以先定位C1=‘ABC‘ AND ID>10的第一条记录,再顺序扫描至C1=‘ABC‘ AND ID<1000的记录,有效减少扫描的数据量。

 

对于查询:

SELECT * 
FROM TB1
WHERE C1=ABC
ORDER BY ID
LIMIT 1

如果满足C1=‘ABC‘的记录数较多,那么查询在MySQL 5.5版本就会性能极差,而在MySQL 5.6版本中变得性能极好,MySQL 5.5版本可以将IDX_C1(C1)优化为 IDX(C1,ID)。

 

在MySQL 5.5.14版本进行测试,准备测试数据:

## 删除测试表
DROP TABLE IF EXISTS tb2001;

## 创建测试表
CREATE TABLE `tb2001` (
    `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `c1` int(11) DEFAULT NULL,
    `c2` int(11) DEFAULT NULL,
    INDEX idx_c1(c1),
    INDEX idx_c2(c2,id)
) ENGINE = InnoDB CHARSET = utf8;

## 插入测试数据(10万左右)
## 如果information_schema.columns数据较少,可以重复多次
INSERT INTO tb2001 (c1,c2)
SELECT 1,1 from information_schema.columns;

测试1:

## 测试SQL 1
SELECT c1,id
FROM tb2001 FORCE INDEX(idx_c1)
WHERE C1=1 and id<2;

## 执行时间超过20MS ## 对应执行计划为:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: tb2001 type: ref possible_keys: idx_c1 key: idx_c1 key_len: 5 ref: const rows: 44196 Extra: Using where; Using index ## 对应PROFILING数据为: +----------------------+----------+----------+------------+--------------+---------------+-------+ | Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps | +----------------------+----------+----------+------------+--------------+---------------+-------+ | starting | 0.000039 | 0.000000 | 0.000000 | 0 | 0 | 0 | | checking permissions | 0.000007 | 0.000000 | 0.000000 | 0 | 0 | 0 | | Opening tables | 0.000011 | 0.000000 | 0.000000 | 0 | 0 | 0 | | System lock | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | 0 | | init | 0.000012 | 0.000000 | 0.000000 | 0 | 0 | 0 | | optimizing | 0.000009 | 0.000000 | 0.000000 | 0 | 0 | 0 | | statistics | 0.000041 | 0.000000 | 0.000000 | 0 | 0 | 0 | | preparing | 0.000012 | 0.000000 | 0.000000 | 0 | 0 | 0 | | executing | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 0 | | Sending data | 0.020529 | 0.019997 | 0.000000 | 0 | 0 | 0 | | end | 0.000006 | 0.000000 | 0.000000 | 0 | 0 | 0 | | query end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 0 | | closing tables | 0.000006 | 0.000000 | 0.000000 | 0 | 0 | 0 | | freeing items | 0.000020 | 0.000000 | 0.000000 | 0 | 0 | 0 | | logging slow query | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 0 | | cleaning up | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 0 | +----------------------+----------+----------+------------+--------------+---------------+-------+

 

测试2:

## 测试SQL:
SELECT c2,id
FROM tb2001 FORCE INDEX(idx_c2)
WHERE C2=1 and id<2;

##执行时间2ms

## 对应执行计划为:
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tb2001
         type: range
possible_keys: idx_c2
          key: idx_c2
      key_len: 9
          ref: NULL
         rows: 1
        Extra: Using where; Using index

## 对应PROFILING数据为:
+----------------------+----------+----------+------------+--------------+---------------+-------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps |
+----------------------+----------+----------+------------+--------------+---------------+-------+
| starting             | 0.000042 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| checking permissions | 0.000007 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Opening tables       | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| System lock          | 0.000007 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| init                 | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| optimizing           | 0.000008 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| statistics           | 0.000033 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| preparing            | 0.000011 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| executing            | 0.000004 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Sending data         | 0.000031 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| end                  | 0.000005 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| query end            | 0.000005 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| closing tables       | 0.000006 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| freeing items        | 0.000022 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| logging slow query   | 0.000004 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| cleaning up          | 0.000004 | 0.000000 |   0.000000 |            0 |             0 |     0 |
+----------------------+----------+----------+------------+--------------+---------------+-------+

 参考链接:https://dev.mysql.com/doc/refman/5.6/en/index-extensions.html

 

以上是关于MySQL Index--InnoDB引擎的主键索引的主要内容,如果未能解决你的问题,请参考以下文章

mysql表设计的一些面试题

mysql外键约束

[MySQL] innoDB引擎的主键与聚簇索引

Mysql索引学习笔记

innodb 存储引擎为啥要用一个自增的主键

MySQL Group Replication(组复制MGR)