`
cfyme
  • 浏览: 275154 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

MySQL索引与Index Condition Pushdown

 
阅读更多

大约在两年前,我写了一篇关于MySQL索引的文章。最近有同学在文章的评论中对文章的内容提出质疑,质疑主要集中在联合索引的使用方式上。在那篇文章中,我说明联合索引是将各个索引字段做字符串连接后作为key,使用时将整体做前缀匹配。

而这名同学在这个页面找到了如下一句话:index condition pushdown is usually useful with multi-column indexes: the first component(s) is what index access is done for, the subsequent have columns that we read and check conditions on。从而认为联合索引的使用方式与文中不符。

实际上,这个页面所讲述的是在MariaDB 5.3.3(MySQL是在5.6)开始引入的一种叫做Index Condition Pushdown(以下简称ICP)的查询优化方式。由于本身不是一个层面的东西,前文中说的是Index Access,而这里是Query Optimization,所以并不构成对前文正确性的影响。在写前文时,MySQL还没有ICP,所以文中没有涉及相关内容,但考虑到新版本的MariaDB或MySQL中ICP的启用确实影响了一些查询行为的外在表现。所以决定写这篇文章详细讲述一下ICP的原理以及对索引使用方式的优化。

实验

先从一个简单的实验开始直观认识ICP的作用。

安装数据库

首先需要安装一个支持ICP的MariaDB或MySQL数据库。我使用的是MariaDB 5.5.34,如果是使用MySQL则需要5.6版本以上。

Mac环境下可以通过brew安装:

  1. brew install mairadb

其它环境下的安装请参考MariaDB官网关于下载安装的文档

导入示例数据

与前文一样,我们使用Employees Sample Database,作为示例数据库。完整示例数据库的下载地址为:https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2

将下载的压缩包解压后,会看到一系列的文件,其中employees.sql就是导入数据的命令文件。执行

  1. mysql -h[host]-u[user]-p < employees.sql

就可以完成建库、建表和load数据等一系列操作。此时数据库中会多一个叫做employees的数据库。库中的表如下:

  1. MariaDB[employees]> SHOW TABLES;
  2. +---------------------+
  3. |Tables_in_employees|
  4. +---------------------+
  5. | departments |
  6. | dept_emp |
  7. | dept_manager |
  8. | employees |
  9. | salaries |
  10. | titles |
  11. +---------------------+
  12. 6 rows inset(0.00 sec)

我们将使用employees表做实验。

建立联合索引

employees表包含雇员的基本信息,表结构如下:

  1. MariaDB[employees]> DESC employees.employees;
  2. +------------+---------------+------+-----+---------+-------+
  3. |Field|Type|Null|Key|Default|Extra|
  4. +------------+---------------+------+-----+---------+-------+
  5. | emp_no |int(11)| NO | PRI | NULL ||
  6. | birth_date | date | NO || NULL ||
  7. | first_name | varchar(14)| NO || NULL ||
  8. | last_name | varchar(16)| NO || NULL ||
  9. | gender |enum('M','F')| NO || NULL ||
  10. | hire_date | date | NO || NULL ||
  11. +------------+---------------+------+-----+---------+-------+
  12. 6 rows inset(0.01 sec)

这个表默认只有一个主索引,因为ICP只能作用于二级索引,所以我们建立一个二级索引:

  1. ALTER TABLE employees.employees ADD INDEX first_name_last_name (first_name, last_name);

这样就建立了一个first_name和last_name的联合索引。

查询

为了明确看到查询性能,我们启用profiling并关闭query cache:

  1. SET profiling =1;
  2. SET query_cache_type =0;
  3. SET GLOBAL query_cache_size =0;

然后我们看下面这个查询:

  1. MariaDB[employees]> SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man';
  2. +--------+------------+------------+-----------+--------+------------+
  3. | emp_no | birth_date | first_name | last_name | gender | hire_date |
  4. +--------+------------+------------+-----------+--------+------------+
  5. |254642|1959-01-17|Mary|Botman| M |1989-11-24|
  6. |471495|1960-09-24|Mary|Dymetman| M |1988-06-09|
  7. |211941|1962-08-11|Mary|Hofman| M |1993-12-30|
  8. |217707|1962-09-05|Mary|Lichtman| F |1987-11-20|
  9. |486361|1957-10-15|Mary|Oberman| M |1988-09-06|
  10. |457469|1959-07-15|Mary|Weedman| M |1996-11-21|
  11. +--------+------------+------------+-----------+--------+------------+

根据MySQL索引的前缀匹配原则,两者对索引的使用是一致的,即只有first_name采用索引,last_name由于使用了模糊前缀,没法使用索引进行匹配。我将查询联系执行三次,结果如下:

  1. +----------+------------+---------------------------------------------------------------------------+
  2. |Query_ID|Duration|Query|
  3. +----------+------------+---------------------------------------------------------------------------+
  4. |38|0.00084400| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  5. |39|0.00071800| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  6. |40|0.00089600| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  7. +----------+------------+---------------------------------------------------------------------------+

然后我们关闭ICP:

  1. SET optimizer_switch='index_condition_pushdown=off';

在运行三次相同的查询,结果如下:

  1. +----------+------------+---------------------------------------------------------------------------+
  2. |Query_ID|Duration|Query|
  3. +----------+------------+---------------------------------------------------------------------------+
  4. |42|0.00264400| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  5. |43|0.01418900| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  6. |44|0.00234200| SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man'|
  7. +----------+------------+---------------------------------------------------------------------------+

有意思的事情发生了,关闭ICP后,同样的查询,耗时是之前的三倍以上。下面我们用explain看看两者有什么区别:

  1. MariaDB[employees]> EXPLAIN SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man';
  2. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-----------------------+
  3. | id | select_type | table | type | possible_keys | key | key_len |ref| rows |Extra|
  4. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-----------------------+
  5. |1| SIMPLE | employees |ref| first_name_last_name | first_name_last_name |44|const|224|Using index condition |
  6. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-----------------------+
  7. 1 row inset(0.00 sec)
  1. MariaDB[employees]> EXPLAIN SELECT * FROM employees WHERE first_name='Mary' AND last_name LIKE '%man';
  2. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key | key_len |ref| rows |Extra|
  4. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-------------+
  5. |1| SIMPLE | employees |ref| first_name_last_name | first_name_last_name |44|const|224|Usingwhere|
  6. +------+-------------+-----------+------+----------------------+----------------------+---------+-------+------+-------------+
  7. 1 row inset(0.00 sec)

前者是开启ICP,后者是关闭ICP。可以看到区别在于Extra,开启ICP时,用的是Using index condition;关闭ICP时,是Using where。

其中Using index condition就是ICP提高查询性能的关键。下一节说明ICP提高查询性能的原理。

原理

ICP的原理简单说来就是将可以利用索引筛选的where条件在存储引擎一侧进行筛选,而不是将所有index access的结果取出放在server端进行where筛选。

以上面的查询为例,在没有ICP时,首先通过索引前缀从存储引擎中读出224条first_name为Mary的记录,然后在server段用where筛选last_name的like条件;而启用ICP后,由于last_name的like筛选可以通过索引字段进行,那么存储引擎内部通过索引与where条件的对比来筛选掉不符合where条件的记录,这个过程不需要读出整条记录,同时只返回给server筛选后的6条记录,因此提高了查询性能。

下面通过图两种查询的原理详细解释。

关闭ICP

在不支持ICP的系统下,索引仅仅作为data access使用。

开启ICP

在ICP优化开启时,在存储引擎端首先用索引过滤可以过滤的where条件,然后再用索引做data access,被index condition过滤掉的数据不必读取,也不会返回server端。

注意事项

有几个关于ICP的事情要注意:

  • ICP只能用于二级索引,不能用于主索引。
  • 也不是全部where条件都可以用ICP筛选,如果某where条件的字段不在索引中,当然还是要读取整条记录做筛选,在这种情况下,仍然要到server端做where筛选。
  • ICP的加速效果取决于在存储引擎内通过ICP筛选掉的数据的比例。

 

转载地址:http://blog.codinglabs.org/articles/index-condition-pushdown.html

分享到:
评论

相关推荐

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

    ICP(Index Condition Pushdown)简单来说,就是将查询语句中的WHERE子句中可以利用索引的部分条件,提前推送到存储引擎层进行判断,而不是等到所有的索引数据都读取到服务器层再进行过滤。这样可以避免无谓的全表...

    mysql面试题,涉及MySQL基础、MySQL索引、MySQL语句 MySQL进阶等方面,适用于初、中级开发人员

    索引下推(Index Condition Pushdown,简称 ICP)是 MySQL 5.6 版本的新特性,可以在对联合索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,能有效的减少回表次数。 5. 索引...

    一篇文章讲清楚MySQL的聚簇-联合-覆盖索引、回表、索引下推.doc

    索引下推(Index Condition Pushdown)是 MySQL 5.6 引入的一个优化索引的特性。它可以将 WHERE 条件下推到索引层面,从而减少回表查询的次数。例如,在(age,name)上面建联合索引,并且查询 SQL 是这样的时候:...

    Mysql覆盖索引详解

    在5.6之后,由于改进的查询优化器(如ICP,即Index Condition Pushdown),这种情况得到了改善。 在排序方面,MySQL有两种主要的排序算法: 1. 两遍扫描算法:先在内存中排序字段和行指针,然后通过行指针获取完整...

    mySQL面试题.docx

    索引下推(Index Condition Pushdown, ICP)是MySQL的一个优化特性,它允许部分查询条件在索引查找过程中提前应用,减少了回表查询的次数,提高了查询性能。例如,如果一个索引包含了查询条件的一部分,MySQL可以在...

    Mysql之索引分享

    MySQL 5.6引入的Index Condition Pushdown(ICP)优化策略,也称为索引下推,允许部分查询条件在存储引擎层面执行,减少了回传到服务器的数据量,进一步提高了查询效率。通过设置optimizer_switch变量可以开启或关闭...

    精心整理的MySQL 高频面试题

    - **索引下推(Index Condition Pushdown,ICP)**:部分过滤条件可以在索引遍历过程中执行,减少回表次数。 - **查询语句优化**:避免全表扫描,使用合适的连接类型(INNER JOIN, LEFT JOIN等),减少子查询,使用...

    【mysql】关于ICP、MRR、BKA等特性1

    一、Index Condition Pushdown (ICP) ICP,即索引条件下推,是MySQL从5.6版本开始引入的一项优化策略,它旨在减少不必要的数据读取和提高查询效率。在ICP未启用时,MySQL会先通过辅助索引找到主键,然后再到基表中...

    mysql-5.6.11.tar

    还有更高效的索引策略和查询优化器,如索引条件推导(Index Condition Pushdown)和并行复制等。 3. **半同步复制**:在5.6版本中,引入了半同步复制(Semi-Synchronous Replication),确保至少有一个从库接收到...

    MySQL 5.7(GA)版本新特性

    在日志功能上,MySQL 5.7.1支持mysqlbinlog工具中row模式下的二进制日志库名重命名操作,而MySQL 5.7.3则支持mysql分区表的index condition pushdown (ICP) 功能。 MySQL 5.7还引入了在线DDL(Data Definition ...

    一些比较常见的mysql面试题

    索引下推:MySQL 5.6 引入的索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。 十、哪些操作会导致索引失效 对索引使用...

    MySQL面试题精选60道

    索引下推:MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。 哪些操作会导致索引失效 对索引使用左...

    MySQL面试精选60题,附详细答案

    索引下推:MySQL 5.6引入的索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。 哪些操作会导致索引失效 对索引使用左或者...

    Mysql原理(二):索引的底层原理

    3. 对于`SELECT name FROM table WHERE name = "小明" AND new_name LIKE "%liu"`的查询,如果name和new_name有联合索引,MySQL的优化器可能会使用索引下推(Index Condition Pushdown, ICP),先通过name索引找到...

    MySQL5.7解压版

    还有优化的索引处理,如Index Condition Pushdown(ICP),允许在索引查找过程中更早地应用过滤条件,减少回表操作。 3. **JSON支持**:MySQL5.7引入了内置的JSON数据类型和相关的操作函数,使得数据库能够直接处理...

Global site tag (gtag.js) - Google Analytics