MySQL Index Condition Pushdown(ICP)性能优化方法实例

 更新时间:2015年05月29日 10:54:48   投稿:junjie  
这篇文章主要介绍了MySQL Index Condition Pushdown(ICP)性能优化方法实例,本文讲解了概念介绍、原理、实践案例、案例分析、ICP的使用限制等内容,需要的朋友可以参考下

一 概念介绍

Index Condition Pushdown (ICP)是MySQL 5.6 版本中的新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。

a 当关闭ICP时,index 仅仅是data access 的一种访问方式,存储引擎通过索引回表获取的数据会传递到MySQL Server 层进行where条件过滤。

b 当打开ICP时,如果部分where条件能使用索引中的字段,MySQL Server 会把这部分下推到引擎层,可以利用index过滤的where条件在存储引擎层进行数据过滤,而非将所有通过index access的结果传递到MySQL server层进行where过滤.

优化效果:ICP能减少引擎层访问基表的次数和MySQL Server 访问存储引擎的次数,减少io次数,提高查询语句性能。

二 原理

Index Condition Pushdown is not used:

  1 Get the next row, first by reading the index tuple, and then by using the index tuple to locate and read the full table row.
  2 Test the part of the WHERE condition that applies to this table. Accept or reject the row based on the test result.
Index Condition Pushdown is used
  1 Get the next row s index tuple (but not the full table row).
  2 Test the part of the WHERE condition that applies to this table and can be checked using only index columns.
    If the condition is not satisfied, proceed to the index tuple for the next row.
  3 If the condition is satisfied, use the index tuple to locate and read the full table row.
  4 est the remaining part of the WHERE condition that applies to this table. Accept or reject the row based on the test result.

三 实践案例

a 环境准备
   数据库版本 5.6.16
   关闭缓存
  

复制代码 代码如下:

     set query_cache_size=0;
     set query_cache_type=OFF;
 

   测试数据下载地址
b 当开启ICP时
复制代码 代码如下:

mysql> SET profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> select * from employees where first_name='Anneke' and last_name like '%sig' ;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10006  | 1953-04-20 | Anneke     | Preusig   | F      | 1989-06-02 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)
mysql> show profiles;
+----------+------------+--------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                          |
+----------+------------+--------------------------------------------------------------------------------+
| 1        | 0.00060275 | select * from employees where first_name='Anneke' and last_name like '%sig'    |
+----------+------------+--------------------------------------------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

此时情况下根据MySQL的最左前缀原则, first_name 可以使用索引,last_name采用了like 模糊查询,不能使用索引。
c 关闭ICP

复制代码 代码如下:

mysql> set optimizer_switch='index_condition_pushdown=off';
Query OK, 0 rows affected (0.00 sec)
mysql> SET profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> select * from employees where first_name='Anneke' and last_name like '%sig' ;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10006  | 1953-04-20 | Anneke     | Preusig   | F      | 1989-06-02 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)
mysql> SET profiling = 0;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show profiles;
+----------+------------+--------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                          |
+----------+------------+--------------------------------------------------------------------------------+
| 2        | 0.00097000 | select * from employees where first_name='Anneke' and last_name like '%sig'    |
+----------+------------+--------------------------------------------------------------------------------+
6 rows in set, 1 warning (0.00 sec)

当开启ICP时 查询在sending data环节时间消耗是 0.000189s

复制代码 代码如下:

mysql> show profile cpu,block io for query 1;
+----------------------+----------+----------+------------+--------------+---------------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting             | 0.000094 | 0.000000 | 0.000000   | 0            | 0             |
| checking permissions | 0.000011 | 0.000000 | 0.000000   | 0            | 0             |
| Opening tables       | 0.000025 | 0.000000 | 0.000000   | 0            | 0             |
| init                 | 0.000044 | 0.000000 | 0.000000   | 0            | 0             |
| System lock          | 0.000014 | 0.000000 | 0.000000   | 0            | 0             |
| optimizing           | 0.000021 | 0.000000 | 0.000000   | 0            | 0             |
| statistics           | 0.000093 | 0.000000 | 0.000000   | 0            | 0             |
| preparing            | 0.000024 | 0.000000 | 0.000000   | 0            | 0             |
| executing            | 0.000006 | 0.000000 | 0.000000   | 0            | 0             |
| Sending data         | 0.000189 | 0.000000 | 0.000000   | 0            | 0             |
| end                  | 0.000019 | 0.000000 | 0.000000   | 0            | 0             |
| query end            | 0.000012 | 0.000000 | 0.000000   | 0            | 0             |
| closing tables       | 0.000013 | 0.000000 | 0.000000   | 0            | 0             |
| freeing items        | 0.000034 | 0.000000 | 0.000000   | 0            | 0             |
| cleaning up          | 0.000007 | 0.000000 | 0.000000   | 0            | 0             |
+----------------------+----------+----------+------------+--------------+---------------+
15 rows in set, 1 warning (0.00 sec)

当关闭ICP时 查询在sending data环节时间消耗是 0.000735s

复制代码 代码如下:

mysql> show profile cpu,block io for query 2;
+----------------------+----------+----------+------------+--------------+---------------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting             | 0.000045 | 0.000000 | 0.000000   | 0            | 0             |
| checking permissions | 0.000007 | 0.000000 | 0.000000   | 0            | 0             |
| Opening tables       | 0.000015 | 0.000000 | 0.000000   | 0            | 0             |
| init                 | 0.000024 | 0.000000 | 0.000000   | 0            | 0             |
| System lock          | 0.000009 | 0.000000 | 0.000000   | 0            | 0             |
| optimizing           | 0.000012 | 0.000000 | 0.000000   | 0            | 0             |
| statistics           | 0.000049 | 0.000000 | 0.000000   | 0            | 0             |
| preparing            | 0.000016 | 0.000000 | 0.000000   | 0            | 0             |
| executing            | 0.000005 | 0.000000 | 0.000000   | 0            | 0             |
| Sending data         | 0.000735 | 0.001000 | 0.000000   | 0            | 0             |
| end                  | 0.000008 | 0.000000 | 0.000000   | 0            | 0             |
| query end            | 0.000008 | 0.000000 | 0.000000   | 0            | 0             |
| closing tables       | 0.000009 | 0.000000 | 0.000000   | 0            | 0             |
| freeing items        | 0.000023 | 0.000000 | 0.000000   | 0            | 0             |
| cleaning up          | 0.000007 | 0.000000 | 0.000000   | 0            | 0             |
+----------------------+----------+----------+------------+--------------+---------------+
15 rows in set, 1 warning (0.00 sec)

从上面的profile 可以看出ICP 开启时整个sql 执行时间是未开启的2/3,sending data 环节的时间消耗前者仅是后者的1/4。
ICP 开启时的执行计划 含有 Using index condition 标示 ,表示优化器使用了ICP对数据访问进行优化。

复制代码 代码如下:

mysql> explain select * from employees where first_name='Anneke' and last_name like '%nta' ;
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
| id | select_type | table     | type | possible_keys | key          | key_len | ref   | rows | Extra                 |
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
| 1  | SIMPLE      | employees | ref  | idx_emp_fnln  | idx_emp_fnln | 44      | const | 224  | Using index condition |
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

ICP 关闭时的执行计划显示use where.
复制代码 代码如下:

mysql> explain select * from employees where first_name='Anneke' and last_name like '%nta' ;
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-------------+
| id | select_type | table     | type | possible_keys | key          | key_len | ref   | rows | Extra       |
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-------------+
| 1  | SIMPLE      | employees | ref  | idx_emp_fnln  | idx_emp_fnln | 44      | const | 224  | Using where |
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-------------+
1 row in set (0.00 sec)

案例分析

以上面的查询为例关闭ICP 时,存储引擎通前缀index first_name 访问表中225条first_name 为Anneke的数据,并在MySQL server层根据last_name like '%sig' 进行过滤
开启ICP 时,last_name 的like '%sig'条件可以通过索引字段last_name 进行过滤,在存储引擎内部通过与where条件的对比,直接过滤掉不符合条件的数据。该过程不回表,只访问符合条件的1条记录并返回给MySQL Server ,有效的减少了io访问和各层之间的交互。

ICP 关闭时 ,仅仅使用索引作为访问数据的方式。

ICP 开启时 ,MySQL将在存储引擎层 利用索引过滤数据,减少不必要的回表,注意 虚线的using where 表示如果where条件中含有没有被索引的字段,则还是要经过MySQL Server 层过滤。

四 ICP的使用限制

1 当sql需要全表访问时,ICP的优化策略可用于range, ref, eq_ref,  ref_or_null 类型的访问数据方法 。
2 支持InnoDB和MyISAM表。
3 ICP只能用于二级索引,不能用于主索引。
4 并非全部where条件都可以用ICP筛选。
   如果where条件的字段不在索引列中,还是要读取整表的记录到server端做where过滤。
5 ICP的加速效果取决于在存储引擎内通过ICP筛选掉的数据的比例。
6 5.6 版本的不支持分表的ICP 功能,5.7 版本的开始支持。
7 当sql 使用覆盖索引时,不支持ICP 优化方法。

复制代码 代码如下:

mysql> explain select * from employees where first_name='Anneke' and last_name='Porenta' ;
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+-----------------------+
| id | select_type | table     | type | possible_keys | key          | key_len | ref         | rows | Extra                 |
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+-----------------------+
| 1  | SIMPLE | employees      | ref  | idx_emp_fnln  | idx_emp_fnln | 94      | const,const | 1    | Using index condition |
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)
mysql> explain select first_name,last_name from employees where first_name='Anneke' and last_name='Porenta' ;
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+--------------------------+
| id | select_type | table     | type | possible_keys | key          | key_len | ref         | rows | Extra                    |
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+--------------------------+
| 1  | SIMPLE      | employees | ref  | idx_emp_fnln  | idx_emp_fnln | 94      | const,const | 1    | Using where; Using index |
+----+-------------+-----------+------+---------------+--------------+---------+-------------+------+--------------------------+
1 row in set (0.00 sec)

相关文章

  • Mysql保持现有内容在后面增加内容的sql语句

    Mysql保持现有内容在后面增加内容的sql语句

    这篇文章主要介绍了Mysql保持现有内容在后面增加内容的sql语句,需要的朋友可以参考下
    2017-05-05
  • MySQL中通过EXPLAIN如何分析SQL的执行计划详解

    MySQL中通过EXPLAIN如何分析SQL的执行计划详解

    这篇文章主要给大家介绍了关于MySQL中通过EXPLAIN如何分析SQL的执行计划的相关资料,文中通过图文以及示例代码介绍的非常详细,对大家的学习或者工作具有一定的安康学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • Mysql 本地计算机无法启动 mysql 服务 错误 1067:进程意外终止。

    Mysql 本地计算机无法启动 mysql 服务 错误 1067:进程意外终止。

    初学php接触mysql,遇到一些问题,卸载重装后,无法启动mysql服务,网络上有很多种说法,我这里将我解决这个问题的办法提出
    2009-12-12
  • 一篇文章带你了解MySQL之undo日志

    一篇文章带你了解MySQL之undo日志

    Undo日志也叫做回滚日志,是MySQL数据库当中一种重要的日志,用于记录更新操作之前的数据状态,这篇文章主要给大家介绍了关于如何通过一篇文章带你了解MySQL之undo日志的相关资料,需要的朋友可以参考下
    2023-06-06
  • MySql实现分布式锁详解

    MySql实现分布式锁详解

    这篇文章主要为大家详细介绍了如何使用本地MySql实现一把分布式锁,以及Mysql实现分布式锁的原理是怎么样的,有需要的小伙伴可以了解下
    2024-11-11
  • Node-Red实现MySQL数据库连接的方法

    Node-Red实现MySQL数据库连接的方法

    这篇文章主要介绍了Node-Red实现MySQL数据库连接的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • MySQL外键约束(Foreign Key)案例详解

    MySQL外键约束(Foreign Key)案例详解

    MySQL外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用,下面这篇文章主要给给大家介绍了关于MySQL外键约束(Foreign Key)的相关资料,需要的朋友可以参考下
    2022-06-06
  • mysql增量备份及恢复的操作方法

    mysql增量备份及恢复的操作方法

    增量备份是在全备或上次增量备份基础上,只备份新增或修改的文件,减少数据量和时间,binlog记录数据库变更,重启时创建新日志文件,增量备份复杂但减轻服务器负担,而binlog帮助精确恢复数据
    2023-09-09
  • 使用mysql workbench自动生成ER图的实现步骤

    使用mysql workbench自动生成ER图的实现步骤

    MySQL Workbench是一款专为MySQL设计的ER/数据库建模工具,它是著名的数据库设计工具DBDesigne4的继任者,可以通过MySQL Workbench设计和创建新的数据库图示,本文给大家介绍了使用mysql workbench自动生成ER图的实现步骤,需要的朋友可以参考下
    2024-06-06
  • 批量替换 MySQL 指定字段中的字符串

    批量替换 MySQL 指定字段中的字符串

    批量替换 MySQL 指定字段中的字符串是数据库应用中很常见的需求,但是有很多初学者在遇到这种需求时,通常都是用脚本来实现;其实,MySQL 内置的有批量替换语法,效率也会高很多;想了解具体方法,继续阅读本文吧 :)
    2009-09-09

最新评论