简单分析MySQL中的primary key功能

 更新时间:2015年05月08日 10:06:50   作者:罗龙九  
这篇文章主要介绍了MySQL中的primary key功能,包括讲到了其对InnoDB使用的影响,需要的朋友可以参考下

在5.1.46中优化器在对primary key的选择上做了一点改动:

Performance: While looking for the shortest index for a covering index scan, the optimizer did not consider the full row length for a clustered primary key, as in InnoDB. Secondary covering indexes will now be preferred, making full table scans less likely。

该版本中增加了find_shortest_key函数,该函数的作用可以认为是选择最小key length的

索引来满足我们的查询。

该函数是怎么工作的:

复制代码 代码如下:
What find_shortest_key should do is the following. If the primary key is a covering index

and is clustered, like in MyISAM, then the behavior today should remain the same. If the

primary key is clustered, like in InnoDB, then it should not consider using the primary

key because then the storage engine will have to scan through much more data.

调用Primary_key_is_clustered(),当返回值为true,执行find_shortest_key:选择key length最小的覆盖索引(Secondary covering indexes),然后来满足查询。

首先在5.1.45中测试:

$mysql -V

mysql Ver 14.14 Distrib 5.1.45, for unknown-linux-gnu (x86_64) using EditLine wrapper

root@test 03:49:45>create table test(id int,name varchar(20),name2 varchar(20),d datetime,primary key(id)) engine=innodb;

Query OK, 0 rows affected (0.16 sec)

root@test 03:49:47>insert into test values(1,'xc','sds',now()),(2,'xcx','dd',now()),(3,'sdds','ddd',now()),(4,'sdsdf','dsd',now()),(5,'sdsdaa','sds',now());

Query OK, 5 rows affected (0.00 sec)

Records: 5 Duplicates: 0 Warnings: 0

root@test 03:49:51>

root@test 03:49:51>insert into test values(6,'xce','sdsd',now()),(7,'xcx','sdsd',now()),(8,'sdds','sds',now()),(9,'sdsdsdf','sdsdsd',now()),(10,'sdssdfdaa','sdsdsd',now());

Query OK, 5 rows affected (0.00 sec)

Records: 5 Duplicates: 0 Warnings: 0

创建索引ind_1:

root@test 03:49:53>alter table test add index ind_1(name,d);

Query OK, 0 rows affected (0.09 sec)

Records: 0 Duplicates: 0 Warnings: 0

root@test 03:50:08>explain select count(*) from test;

+—-+————-+——-+——-+—————+———+———+——+——+————-+

| id | select_type | table | type | possible_keys | key   | key_len | ref | rows | Extra    |

+—-+————-+——-+——-+—————+———+———+——+——+————-+

| 1 | SIMPLE   | test | index | NULL     | PRIMARY | 4    | NULL |  10 | Using index |

+—-+————-+——-+——-+—————+———+———+——+——+————-+

1 row in set (0.00 sec)

添加ind_2:

root@test 08:04:35>alter table test add index ind_2(d);

Query OK, 0 rows affected (0.07 sec)

Records: 0 Duplicates: 0 Warnings: 0

root@test 08:04:45>explain select count(*) from test;

+—-+————-+——-+——-+—————+———+———+——+——+————-+

| id | select_type | table | type | possible_keys | key   | key_len | ref | rows | Extra    |

+—-+————-+——-+——-+—————+———+———+——+——+————-+

| 1 | SIMPLE   | test | index | NULL     | PRIMARY | 4    | NULL |  10 | Using index |

+—-+————-+——-+——-+—————+———+———+——+——+————-+

1 row in set (0.00 sec)

上面的版本【5.1.45】中,可以看到优化器选择使用主键来完成扫描,并没有使用ind_1,ind_2来完成查询;

接下来是:5.1.48

$mysql -V

mysql Ver 14.14 Distrib 5.1.48, for unknown-linux-gnu (x86_64) using EditLine wrapper

root@test 03:13:15> create table test(id int,name varchar(20),name2 varchar(20),d datetime,primary key(id)) engine=innodb;

Query OK, 0 rows affected (0.00 sec)

root@test 03:48:04>insert into test values(1,'xc','sds',now()),(2,'xcx','dd',now()),(3,'sdds','ddd',now()),(4,'sdsdf','dsd',now()),(5,'sdsdaa','sds',now());

Query OK, 5 rows affected (0.00 sec)

Records: 5 Duplicates: 0 Warnings: 0

root@test 03:48:05>insert into test values(6,'xce','sdsd',now()),(7,'xcx','sdsd',now()),(8,'sdds','sds',now()),(9,'sdsdsdf','sdsdsd',now()),(10,'sdssdfdaa','sdsdsd',now());

Query OK, 5 rows affected (0.01 sec)

Records: 5 Duplicates: 0 Warnings: 0

创建索引ind_1:

root@test 03:13:57>alter table test add index ind_1(name,d);

Query OK, 0 rows affected (0.01 sec)

Records: 0 Duplicates: 0 Warnings: 0

root@test 03:15:55>explain select count(*) from test;

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra    |

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

| 1 | SIMPLE   | test | index | NULL     | ind_1 | 52   | NULL |  10 | Using index |

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

root@test 08:01:56>alter table test add index ind_2(d);

Query OK, 0 rows affected (0.03 sec)

Records: 0 Duplicates: 0 Warnings: 0

添加ind_2:

root@test 08:02:09>explain select count(*) from test;

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra    |

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

| 1 | SIMPLE   | test | index | NULL     | ind_2 | 9    | NULL |  10 | Using index |

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

1 row in set (0.00 sec)

版本【5.1.48】中首先明智的选择ind_1来完成扫描,并没有考虑到使用主键(全索引扫描)来完成查询,随后添加ind_2,由于 ind_1的key长度是大于ind_2 key长度,所以mysql选择更优的ind_2来完成查询,可以看到mysql在选择方式上也在慢慢智能了。

观察性能:

5.1.48

root@test 08:49:32>set profiling =1;

Query OK, 0 rows affected (0.00 sec)

root@test 08:49:41>select count(*) from test;

+———-+

| count(*) |

+———-+

| 5242880 |

+———-+

1 row in set (1.18 sec)

root@test 08:56:30>show profile cpu,block io for query 1;

+——————————–+———-+———-+————+————–+—————+

| Status             | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |

+——————————–+———-+———-+————+————–+—————+

| starting            | 0.000035 | 0.000000 |  0.000000 |      0 |       0 |

| checking query cache for query | 0.000051 | 0.000000 |  0.000000 |      0 |       0 |

| Opening tables         | 0.000014 | 0.000000 |  0.000000 |      0 |       0 |

| System lock          | 0.000005 | 0.000000 |  0.000000 |      0 |       0 |

| Table lock           | 0.000010 | 0.000000 |  0.000000 |      0 |       0 |

| init              | 0.000015 | 0.000000 |  0.000000 |      0 |       0 |

| optimizing           | 0.000007 | 0.000000 |  0.000000 |      0 |       0 |

| statistics           | 0.000015 | 0.000000 |  0.000000 |      0 |       0 |

| preparing           | 0.000012 | 0.000000 |  0.000000 |      0 |       0 |

| executing           | 0.000007 | 0.000000 |  0.000000 |      0 |       0 |

| Sending data          | 1.178452 | 1.177821 |  0.000000 |      0 |       0 |

| end              | 0.000016 | 0.000000 |  0.000000 |      0 |       0 |

| query end           | 0.000005 | 0.000000 |  0.000000 |      0 |       0 |

| freeing items         | 0.000040 | 0.000000 |  0.000000 |      0 |       0 |

| logging slow query       | 0.000002 | 0.000000 |  0.000000 |      0 |       0 |

| logging slow query       | 0.000086 | 0.000000 |  0.000000 |      0 |       0 |

| cleaning up          | 0.000006 | 0.000000 |  0.000000 |      0 |       0 |

+——————————–+———-+———-+————+————–+—————+

对比性能:

5.1.45

root@test 08:57:18>set profiling =1;

Query OK, 0 rows affected (0.00 sec)

root@test 08:57:21>select count(*) from test;

+———-+

| count(*) |

+———-+

| 5242880 |

+———-+

1 row in set (1.30 sec)

root@test 08:57:27>show profile cpu,block io for query 1;

+——————————–+———-+———-+————+————–+—————+

| Status             | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |

+——————————–+———-+———-+————+————–+—————+

| starting            | 0.000026 | 0.000000 |  0.000000 |      0 |       0 |

| checking query cache for query | 0.000041 | 0.000000 |  0.000000 |      0 |       0 |

| Opening tables         | 0.000014 | 0.000000 |  0.000000 |      0 |       0 |

| System lock          | 0.000005 | 0.000000 |  0.000000 |      0 |       0 |

| Table lock           | 0.000008 | 0.000000 |  0.000000 |      0 |       0 |

| init              | 0.000015 | 0.000000 |  0.000000 |      0 |       0 |

| optimizing           | 0.000006 | 0.000000 |  0.000000 |      0 |       0 |

| statistics           | 0.000014 | 0.000000 |  0.000000 |      0 |       0 |

| preparing           | 0.000012 | 0.000000 |  0.000000 |      0 |       0 |

| executing           | 0.000007 | 0.000000 |  0.000000 |      0 |       0 |

| Sending data          | 1.294178 | 1.293803 |  0.000000 |      0 |       0 |

| end              | 0.000016 | 0.000000 |  0.000000 |      0 |       0 |

| query end           | 0.000004 | 0.000000 |  0.000000 |      0 |       0 |

| freeing items         | 0.000040 | 0.000000 |  0.001000 |      0 |       0 |

| logging slow query       | 0.000002 | 0.000000 |  0.000000 |      0 |       0 |

| logging slow query       | 0.000080 | 0.000000 |  0.000000 |      0 |       0 |

| cleaning up          | 0.000006 | 0.000000 |  0.000000 |      0 |       0 |

+——————————–+———-+———-+————+————–+—————+

从上面的profile中可以看到在Sending data上,差异还是比较明显的,mysql不需要扫描整个表的页块,而是扫描表中索引key最短的索引页块来完成查询,这样就减少了很多不必要的数据。

PS:innodb是事务引擎,所以在叶子节点中除了存储本行记录外,还会多记录一些关于事务的信息(DB_TRX_ID ,DB_ROLL_PTR 等),因此单行长度额外开销20个字节左右,最直观的方法是将myisam转为innodb,存储空间会明显上升。那么在主表为t(id,name,pk(id)),二级索引ind_name(name,id),这个时候很容易混淆,即使只有两个字段,第一索引还是比第二索引要大(可以通过innodb_table_monitor观察表的的内部结构)在查询所有id的时候,优化器还是会选择第二索引ind_name。

相关文章

  • MYSQL SERVER收缩日志文件实现方法

    MYSQL SERVER收缩日志文件实现方法

    这篇文章主要介绍了MYSQL SERVER收缩日志文件实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • 一篇文章看懂MySQL主从复制与读写分离

    一篇文章看懂MySQL主从复制与读写分离

    在实际的生产环境中,由单台Mysql作为独立的数据库是完全不能满足实际需求的,一般都是通过主从复制的方式来同步数据,再通过读写分离(来提升数据库的并发负载能力,这篇文章主要给大家介绍了关于MySQL主从复制与读写分离的相关资料,需要的朋友可以参考下
    2021-11-11
  • 解决从集合运算到mysql的not like找不出NULL的问题

    解决从集合运算到mysql的not like找不出NULL的问题

    这篇文章主要介绍了解决从集合运算到mysql的not like找不出NULL的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • insert...on duplicate key update语法详解

    insert...on duplicate key update语法详解

    本文主要介绍了insert...on duplicate key update语法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 如何利用MySQL的binlog恢复误删数据库详解

    如何利用MySQL的binlog恢复误删数据库详解

    MySQL一旦误删数据库之后恢复数据很麻烦,这里记录一下艰辛的恢复过程,这篇文章主要给大家介绍了关于如何利用MySQL的binlog恢复误删数据库的相关资料,需要的朋友可以参考下
    2021-09-09
  • MySQL表数据文件损坏导致数据库无法启动的原因与解决方案

    MySQL表数据文件损坏导致数据库无法启动的原因与解决方案

    在日常的数据库管理中,遇到MySQL表数据文件损坏的情况并不罕见,这种情况下,MySQL数据库可能会无法正常启动,给业务运行带来严重影响,本文将探讨如何诊断和解决MySQL表数据文件损坏导致的数据库无法启动问题,需要的朋友可以参考下
    2025-03-03
  • 一文带你搞懂MySQL中的隐式类型转换和显式类型转换

    一文带你搞懂MySQL中的隐式类型转换和显式类型转换

    在mysql中,当操作涉及不同类型的数据时,会根据一定的规则自动进行类型转换,本文主要来和大家聊聊隐式类型转换和显式类型转换的相关知识,需要的可以参考一下
    2025-04-04
  • MySQL WorkBench管理操作MySQL教程

    MySQL WorkBench管理操作MySQL教程

    MySQL Workbench提供DBAs和developers一个集成工具环境,方便管理mysql数据库,这里简单介绍下MySQL Workbench使用方法,需要的朋友可以参考下
    2014-03-03
  • MySQL中使用replace、regexp进行正则表达式替换的用法分析

    MySQL中使用replace、regexp进行正则表达式替换的用法分析

    这篇文章主要介绍了MySQL中使用replace、regexp进行正则表达式替换的用法,结合具体实例形式分析了replace、regexp正则替换的使用技巧与相关注意事项,需要的朋友可以参考下
    2017-03-03
  • 详解一条sql语句在mysql中是如何执行的

    详解一条sql语句在mysql中是如何执行的

    这篇文章主要介绍了一条sql语句在mysql中是如何执行的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03

最新评论