ICP 概念
ICP(Index Condition Pushdown,索引下推),是 MySQL 5.6 版本推出的功能,用于优化 MySQL 查询。
ICP 可以减少存储引擎查询回表的次数以及 MySQL server 层访问存储引擎的次数。
ICP 的目标是减少整行记录读取的次数,从而减少 I/O 操作。
使用 ICP 索引扫描过程
1)存储引擎读取索引记录
2)根据索引中的主键值,定位并读取完整行
3)存储引擎把记录交给 Server 层去检测该记录是否满足 where 条件
不使用 ICP 索引扫描过程
1)存储引擎读取索引记录(不是完整行)
2)判断 where 条件部分能否用索引做列检查
- 如果条件不满足,则处理下一行的索引记录
- 如果条件满足,使用索引中的主键去定位并读取完整行的记录(也就是回表)
3)存储引擎把记录交给 Server 层,Server 层检测该记录是否满足 where 条件的其余部分。
通过使用ICP,MySQL可以将部分WHERE条件的检查下推到索引层级,而不需要读取完整的表行。这样可以减少I/O操作和内存开销,提高查询性能。
以下是一个示例来说明ICP的应用:
假设有一个名为"Products"的表,包含以下列:ProductID(主键)、ProductName和Price。我们希望查询价格低于100的产品。
不使用ICP,索引扫描步骤:
1)读取索引记录,定位并读取完整的表行。
3)存储引擎把记录交给Server层去检测该记录价格是否低于100。
3)根据检查结果决定是否返回该行。
使用ICP,索引扫描步骤:
1)读取索引记录,但不读取完整的表行。
2)判断价格能否用索引列做检查:
- 如果价格不低于100,处理下一行的索引记录
- 如果价格低于100,使用索引记录定位并读取完整的表行
4)根据检查结果决定是否返回该行。
通过使用ICP,MySQL可以在不读取完整的表行的情况下,首先检查价格是否低于100。这样可以避免读取不符合条件的行,从而提高查询效率。
ICP 测试
准备数据
CREATE TABLE `icp` (
`employee_id` int(6) NOT NULL AUTO_INCREMENT,
`first_name` varchar(20) DEFAULT NULL,
`last_name` varchar(25) DEFAULT NULL,
`email` varchar(25) DEFAULT NULL,
`phone_number` varchar(20) DEFAULT NULL,
PRIMARY KEY (`employee_id`)
);
insert into `icp`(`employee_id`,`first_name`,`last_name`,`email`,`phone_number`) values (100,'Steven','K_ing','SKING','515.123.4567'),(101,'Neena','Kochhar','NKOCHHAR','515.123.4568'),(102,'Lex','De Haan','LDEHAAN','515.123.4569'),(103,'Mary','Hunold','AHUNOLD','590.423.4567'),(104,'Mary','Ernst','BERNST','590.423.4568'),(105,'Mary','Austin','DAUSTIN','590.423.4569'),(106,'Valli','Pataballa','VPATABAL','590.423.4560'),(107,'Diana','Lorentz','DLORENTZ','590.423.5567'),(108,'Nancy','Greenberg','NGREENBE','515.124.4569'),(109,'Daniel','Faviet','DFAVIET','515.124.4169'),(110,'Mary','Chen','JCHEN','515.124.4269'),(111,'Ismael','Sciarra','ISCIARRA','515.124.4369'),(112,'Jose Manuel','Urman','JMURMAN','515.124.4469'),(113,'Mary','Popp','LPOPP','515.124.4567'),(114,'Den','Raphaely','DRAPHEAL','515.127.4561'),(115,'Alexander','Khoo','AKHOO','515.127.4562'),(116,'Mary','Lichtman','SBAIDA','515.127.4563'),(117,'Mary','Tobias','STOBIAS','515.127.4564'),(118,'Mary','Oberman','GHIMURO','515.127.4565'),(119,'Karen','Colmenares','KCOLMENA','515.127.4566'),(120,'Matthew','Weiss','MWEISS','650.123.1234'),(121,'Adam','Fripp','AFRIPP','650.123.2234'),(122,'Payam','Kaufling','PKAUFLIN','650.123.3234'),(123,'Mary','Vollman','SVOLLMAN','650.123.4234'),(124,'Kevin','Mourgos','KMOURGOS','650.123.5234'),(125,'Julia','Nayer','JNAYER','650.124.1214'),(126,'Irene','Mikkilineni','IMIKKILI','650.124.1224'),(127,'Mary','Landry','JLANDRY','650.124.1334'),(128,'Mary','Markle','SMARKLE','650.124.1434'),(129,'Mary','Bissot','LBISSOT','650.124.5234'),(130,'Mozhe','Atkinson','MATKINSO','650.124.6234'),(131,'Mary','Marlow','JAMRLOW','650.124.7234'),(132,'TJ','Olson','TJOLSON','650.124.8234'),(133,'Mary','Mallin','JMALLIN','650.127.1934'),(134,'Michael','Rogers','MROGERS','650.127.1834'),(135,'Ki','Gee','KGEE','650.127.1734'),(136,'Mary','Philtanker','HPHILTAN','650.127.1634'),(137,'Renske','Ladwig','RLADWIG','650.121.1234'),(138,'Stephen','Stiles','SSTILES','650.121.2034'),(139,'Mary','Seo','JSEO','650.121.2019'),(140,'Mary','Hofman','JPATEL','650.121.1834'),(141,'Trenna','Rajs','TRAJS','650.121.8009'),(142,'Curtis','Davies','CDAVIES','650.121.2994'),(143,'Mary','Matos','RMATOS','650.121.2874'),(144,'Mary','Vargas','PVARGAS','650.121.2004'),(145,'John','Russell','JRUSSEL','011.44.1344.429268'),(146,'Karen','Partners','KPARTNER','011.44.1344.467268'),(147,'Mary','Errazuriz','AERRAZUR','011.44.1344.429278'),(148,'Gerald','Cambrault','GCAMBRAU','011.44.1344.619268'),(149,'Eleni','Zlotkey','EZLOTKEY','011.44.1344.429018'),(150,'Mary','Weedman','PTUCKER','011.44.1344.129268'),(151,'Mary','Bernstein','DBERNSTE','011.44.1344.345268'),(152,'Peter','Hall','PHALL','011.44.1344.478968'),(153,'Christopher','Olsen','COLSEN','011.44.1344.498718'),(154,'Mary','Cambrault','NCAMBRAU','011.44.1344.987668'),(155,'Oliver','Tuvault','OTUVAULT','011.44.1344.486508'),(156,'Mary','K_ing','JKING','011.44.1345.429268'),(157,'Mary','Sully','PSULLY','011.44.1345.929268'),(158,'Mary','Dymetman','AMCEWEN','011.44.1345.829268'),(159,'Mary','Smith','LSMITH','011.44.1345.729268'),(160,'Mary','Doran','LDORAN','011.44.1345.629268'),(161,'Mary','Sewall','SSEWALL','011.44.1345.529268'),(162,'Mary','Vishney','CVISHNEY','011.44.1346.129268'),(163,'Mary','Greene','DGREENE','011.44.1346.229268'),(164,'Mattea','Marvins','MMARVINS','011.44.1346.329268'),(165,'David','Lee','DLEE','011.44.1346.529268'),(166,'Mary','Ande','SANDE','011.44.1346.629268'),(167,'Mary','Banda','ABANDA','011.44.1346.729268'),(168,'Lisa','Ozer','LOZER','011.44.1343.929268'),(169,'Mary','Bloom','HBLOOM','011.44.1343.829268'),(170,'Tayler','Fox','TFOX','011.44.1343.729268'),(171,'Mary','Mary','WSMITH','011.44.1343.629268'),(172,'Mary','Bates','EBATES','011.44.1343.529268'),(173,'Mary','Kumar','SKUMAR','011.44.1343.329268'),(174,'Ellen','Abel','EABEL','011.44.1644.429267'),(175,'Alyssa','Hutton','AHUTTON','011.44.1644.429266'),(176,'Jonathon','Taylor','JTAYLOR','011.44.1644.429265'),(177,'Jack','Livingston','JLIVINGS','011.44.1644.429264'),(178,'Mary','Grant','KGRANT','011.44.1644.429263'),(179,'Mary','Johnson','CJOHNSON','011.44.1644.429262'),(180,'Mary','Taylor','WTAYLOR','650.507.9876'),(181,'Mary','Fleaur','JFLEAUR','650.507.9877'),(182,'Mary','Sullivan','MSULLIVA','650.507.9878'),(183,'Girard','Geoni','GGEONI','650.507.9879'),(184,'Mary','Sarchand','NSARCHAN','650.509.1876'),(185,'Mary','Bull','ABULL','650.509.2876'),(186,'Mary','Botman','JDELLING','650.509.3876'),(187,'Mary','Cabrio','ACABRIO','650.509.4876'),(188,'Kelly','Chung','KCHUNG','650.505.1876'),(189,'Jennifer','Dilly','JDILLY','650.505.2876'),(190,'Mary','Gates','TGATES','650.505.3876'),(191,'Randall','Perkins','RPERKINS','650.505.4876'),(192,'Sarah','Bell','SBELL','650.501.1876'),(193,'Mary','Everett','BEVERETT','650.501.2876'),(194,'Mary','McCain','SMCCAIN','650.501.3876'),(195,'Vance','Jones','VJONES','650.501.4876'),(196,'Alana','Walsh','AWALSH','650.507.9811'),(197,'Mary','Feeney','KFEENEY','650.507.9822'),(198,'Mary','OConnell','DOCONNEL','650.507.9833'),(199,'Mary','Grant','DGRANT','650.507.9844'),(200,'Mary','Whalen','JWHALEN','515.123.4444'),(201,'Mary','Hartstein','MHARTSTE','515.123.5555'),(202,'Pat','Fay','PFAY','603.123.6666'),(203,'Susan','Mavris','SMAVRIS','515.123.7777'),(204,'Mary','Baer','HBAER','515.123.8888'),(205,'Mary','Higgins','SHIGGINS','515.123.8080'),(206,'William','Gietz','WGIETZ','515.123.8181');
mysql> desc employees;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| employee_id | int | NO | PRI | NULL | auto_increment |
| first_name | varchar(20) | YES | MUL | NULL | |
| last_name | varchar(25) | YES | | NULL | |
| email | varchar(25) | YES | | NULL | |
| phone_number | varchar(20) | YES | | NULL | |
+--------------+-------------+------+-----+---------+----------------+
ICP 只能作用于二级索引,所以需要建立一个二级索引。执行下述命令建立 first_name 和 last_name 的联合索引:
mysql> alter table employees add index first_name_last_name (first_name, last_name);
打开 ICP 的性能测试
为了明确看到查询性能,我们启用 profiling:
mysql> set profiling = 1;
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> show profiles;
+----------+------------+---------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------------------------------------------+
| 1 | 0.00165975 | select * from employees where first_name='Mary' and last_name like '%man' |
| 2 | 0.00162125 | select * from employees where first_name='Mary' and last_name like '%man' |
| 3 | 0.00164050 | select * from employees where first_name='Mary' and last_name like '%man' |
+----------+------------+---------------------------------------------------------------------------+
注意:mysql 默认开启 ICP 机制
PS:MySQL 有一个叫 profile 的东东,可以用来监视 SQL 语句在各个阶段的执行情况,咱们可以使用这个工具来观察 SQL 语句在各个阶段的运行情况,关于 profile 的详细说明可以参考官方文档。
上述执行中查询语句中只有 first_name 采用索引,last_name 由于使用了模糊查询,没法使用索引进行匹配。
关闭 ICP 性能测试
mysql> set optimizer_switch='index_condition_pushdown=off';
mysql> set profiling = 1;
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> select * from employees where first_name='Mary' and last_name like '%man';
mysql> show profiles;
+----------+------------+---------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------------------------------------------+
| 1 | 0.00317075 | select * from employees where first_name='Mary' and last_name like '%man' |
| 2 | 0.00316175 | select * from employees where first_name='Mary' and last_name like '%man' |
| 3 | 0.00316075 | select * from employees where first_name='Mary' and last_name like '%man' |
+----------+------------+---------------------------------------------------------------------------+
结果对比
从上述结果可以看出,打开 ICP 机制时,随机执行三次查询,花费时间都在 0.0016...
而关闭 ICP 机制后,随机执行三次查询,花费时间都在 0.0031...,约是打开 ICP 机制时的 2 倍
此外,通过 explain 命令也可查看 ICP 是否打开,执行如下命令:
mysql> explain select * from employees where first_name='Mary' and last_name like '%man';
情况 1
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | employees | NULL | ref | first_name_last_name | first_name_last_name | 83 | const | 56 | 11.11 | Using index condition |
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
情况 2
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | employees | NULL | ref | first_name_last_name | first_name_last_name | 83 | const | 56 | 11.11 | Using where |
+----+-------------+-----------+------------+------+----------------------+----------------------+---------+-------+------+----------+-------------+
情况 1 中的 Extra 列显示 Using index condition 代表 ICP 机制已打开;
情况 2 中的 Extra 列显示 Using where 代表 ICP 机制已关闭;
测试结论
在二级索引是复合索引且前面的条件过滤性较低的情况下,打开 ICP 可以有效的降低 server 层和 engine 层之间交互的次数,从而有效的降低运行时间(从 0.0031s 降低到 0.0016s),但是,对于多个普通索引构成的 where 过滤条件,无论是否启用ICP,优化器都会将过滤性高的索引条件下推到 engine 层执行index range scan,因此,收益不大。
ICP原理
5.6 之前,在 SQL 语句的执行过程中,server 层通过 engine 的 api 获取数据,然后再进行 where_cond 的判断(具体判断逻辑在: evaluate_join_record),每一条数据都需要从 engine 层返回 server 层做判断。5.6 之后,在利用索引扫描的过程中,如果发现 where_cond 中含有这个 index 相关的条件,则将此条件记录在 handler 接口中,在索引扫描的过程中,只有满足索引与 handler 接口的条件时,才会返回到 server 层做进一步的处理,在前缀索引区分度不够,其它字段区分度高的情况下可以有效的减少 server & engine之间的开销,提升查询性能。
举例说明:
mysql> select * from employees where first_name='Mary';
+-------------+------------+------------+----------+--------------------+
| employee_id | first_name | last_name | email | phone_number |
+-------------+------------+------------+----------+--------------------+
| 166 | Mary | Ande | SANDE | 011.44.1346.629268 |
| 105 | Mary | Austin | DAUSTIN | 590.423.4569 |
| 204 | Mary | Baer | HBAER | 515.123.8888 |
| 167 | Mary | Banda | ABANDA | 011.44.1346.729268 |
| 172 | Mary | Bates | EBATES | 011.44.1343.529268 |
| 151 | Mary | Bernstein | DBERNSTE | 011.44.1344.345268 |
| 129 | Mary | Bissot | LBISSOT | 650.124.5234 |
| 169 | Mary | Bloom | HBLOOM | 011.44.1343.829268 |
| 186 | Mary | Botman | JDELLING | 650.509.3876 |
| 185 | Mary | Bull | ABULL | 650.509.2876 |
| 187 | Mary | Cabrio | ACABRIO | 650.509.4876 |
| 154 | Mary | Cambrault | NCAMBRAU | 011.44.1344.987668 |
| 110 | Mary | Chen | JCHEN | 515.124.4269 |
| 160 | Mary | Doran | LDORAN | 011.44.1345.629268 |
| 158 | Mary | Dymetman | AMCEWEN | 011.44.1345.829268 |
| 104 | Mary | Ernst | BERNST | 590.423.4568 |
| 147 | Mary | Errazuriz | AERRAZUR | 011.44.1344.429278 |
| 193 | Mary | Everett | BEVERETT | 650.501.2876 |
| 197 | Mary | Feeney | KFEENEY | 650.507.9822 |
| 181 | Mary | Fleaur | JFLEAUR | 650.507.9877 |
| 190 | Mary | Gates | TGATES | 650.505.3876 |
| 178 | Mary | Grant | KGRANT | 011.44.1644.429263 |
| 199 | Mary | Grant | DGRANT | 650.507.9844 |
| 163 | Mary | Greene | DGREENE | 011.44.1346.229268 |
| 201 | Mary | Hartstein | MHARTSTE | 515.123.5555 |
| 205 | Mary | Higgins | SHIGGINS | 515.123.8080 |
| 140 | Mary | Hofman | JPATEL | 650.121.1834 |
| 103 | Mary | Hunold | AHUNOLD | 590.423.4567 |
| 179 | Mary | Johnson | CJOHNSON | 011.44.1644.429262 |
| 173 | Mary | Kumar | SKUMAR | 011.44.1343.329268 |
| 156 | Mary | K_ing | JKING | 011.44.1345.429268 |
| 127 | Mary | Landry | JLANDRY | 650.124.1334 |
| 116 | Mary | Lichtman | SBAIDA | 515.127.4563 |
| 133 | Mary | Mallin | JMALLIN | 650.127.1934 |
| 128 | Mary | Markle | SMARKLE | 650.124.1434 |
| 131 | Mary | Marlow | JAMRLOW | 650.124.7234 |
| 171 | Mary | Mary | WSMITH | 011.44.1343.629268 |
| 143 | Mary | Matos | RMATOS | 650.121.2874 |
| 194 | Mary | McCain | SMCCAIN | 650.501.3876 |
| 118 | Mary | Oberman | GHIMURO | 515.127.4565 |
| 198 | Mary | OConnell | DOCONNEL | 650.507.9833 |
| 136 | Mary | Philtanker | HPHILTAN | 650.127.1634 |
| 113 | Mary | Popp | LPOPP | 515.124.4567 |
| 184 | Mary | Sarchand | NSARCHAN | 650.509.1876 |
| 139 | Mary | Seo | JSEO | 650.121.2019 |
| 161 | Mary | Sewall | SSEWALL | 011.44.1345.529268 |
| 159 | Mary | Smith | LSMITH | 011.44.1345.729268 |
| 182 | Mary | Sullivan | MSULLIVA | 650.507.9878 |
| 157 | Mary | Sully | PSULLY | 011.44.1345.929268 |
| 180 | Mary | Taylor | WTAYLOR | 650.507.9876 |
| 117 | Mary | Tobias | STOBIAS | 515.127.4564 |
| 144 | Mary | Vargas | PVARGAS | 650.121.2004 |
| 162 | Mary | Vishney | CVISHNEY | 011.44.1346.129268 |
| 123 | Mary | Vollman | SVOLLMAN | 650.123.4234 |
| 150 | Mary | Weedman | PTUCKER | 011.44.1344.129268 |
| 200 | Mary | Whalen | JWHALEN | 515.123.4444 |
+-------------+------------+------------+----------+--------------------+
56 rows in set (0.01 sec)
ICP的原理简单说来就是将可以利用索引筛选的 where 条件在存储引擎一侧进行筛选,而不是将所有 index access 的结果取出放在 server 端进行 where 筛选。
以上面的查询为例,在没有 ICP 时,首先通过索引前缀从存储引擎中读出 56 条 first_name 为 Mary 的记录,然后在 server 端用 where 筛选 last_name 的 like 条件(回表操作);而启用 ICP 后,会继续在 56 条数据上继续使用索引对 last_name 进行匹配,筛选掉不符合 where 条件的记录,这个过程不需要读出整条记录,同时只返回给 server 筛选后的 6 条记录,因此提高了查询性能。
ICP 使用场景
上述是关于 Index Condition Pushdown(ICP)优化的适用条件的说明。让我们逐一解释每个场景,并提供相应的示例:
1)当需要访问全表记录时,ICP 可用于 range(范围扫描)、ref(非唯一索引的"="操作)、eq_ref(唯一索引的"="操作) 和 ref_or_null(带有null值判断的操作,比如:WHERE col = ... OR col IS NULL) 访问方法。
- range(范围扫描)
SELECT * FROM Products WHERE Price BETWEEN 10 AND 20;
假设"Products"表中有一个"Price"列,并在该列上创建了索引。上述查询语句将返回价格在10到20之间的产品记录。ICP可以将范围扫描操作下推到存储引擎层,只读取和处理符合范围条件的数据。
- ref(非唯一索引的"="操作)
SELECT * FROM Users WHERE Country = 'USA';
假设"Users"表中有一个"Country"列,并在该列上创建了非唯一索引。上述查询语句将返回所在国家为"USA"的用户记录。ICP可以将等值操作下推到存储引擎层,只读取和处理与"USA"国家相关的数据。
- eq_ref(唯一索引的"="操作)
SELECT * FROM Orders o JOIN Users u ON o.UserID = u.UserID WHERE u.UserID = 123;
假设"Users"表中的"UserID"列是主键,并在该列上创建了唯一索引。"Orders"表中的"UserID"列是外键关联到"Users"表。上述查询语句将返回用户ID为123的订单信息。ICP可以将等值操作下推到存储引擎层,只读取和处理与特定用户相关的订单数据。
- ref_or_null(带有null值判断的操作)
SELECT * FROM Customers WHERE Address = '123 Main St' OR Address IS NULL;
如果在"Address"列上创建了索引,并且数据库支持空值(NULL),MySQL可以使用ICP将等值操作和空值判断下推到存储引擎层进行处理,只读取地址为'123 Main St'或地址为空的客户记录。
2)ICP 可以用于 InnoDB and MyISAM 表,包括分区的 InnoDB 和 MyISAM 表。
3)对于 InnoDB 表,ICP 仅适用于辅助索引。ICP 的目标是减少完整行读取的次数,从而减少 I/O 操作。对于 InnoDB 聚簇索引,完整的记录已经被读入 InnoDB 缓冲区。在这种情况下使用 ICP 不会减少 I/O。
假设有一个名为"Users"的InnoDB表,其中包含以下列:UserID(聚簇索引)、Name、Age、Country。
现在执行以下查询:
SELECT Name, Age FROM Users WHERE UserID = 123;
由于"UserID"是聚簇索引,InnoDB引擎会直接在内存中的InnoDB缓冲区中找到与UserID为123的记录,并返回Name和Age列的值,而不需要进行额外的I/O操作。
在这种情况下,ICP不会减少I/O操作的次数,因为完整的记录已经被读入内存。ICP主要用于辅助索引的情况,通过减少完整行读取的次数来减少I/O操作。对于InnoDB聚簇索引,由于数据已经在内存中,ICP并不适用。
需要注意的是,虽然ICP在这种情况下不会减少I/O操作,但它仍然可以提供其他优势,如减少CPU开销和网络传输量,因为ICP可以在存储引擎层面上过滤掉不符合条件的记录,减少了数据传输和处理的工作量。
4)ICP 不支持在虚拟生成列上创建的辅助索引。InnoDB 支持对虚拟生成列创建辅助索引。
虚拟生成列是一种在MySQL中引入的特性,它是通过计算表中其他列的表达式得到的列,而不是存储实际的数据。虚拟生成列在查询中可以像普通列一样使用,并且可以在其上创建索引。
然而,尽管InnoDB存储引擎支持对虚拟生成列创建辅助索引,但ICP无法在这些辅助索引上进行操作。这意味着在使用虚拟生成列的查询中,ICP无法将过滤条件下推到辅助索引层面,而是需要在存储引擎层面读取完整的记录,并在上层进行条件判断。
假设有一个名为"Products"的表,其中包含以下列:ProductID(主键)、ProductName、Price和Discount(虚拟生成列,计算出折扣后的价格)。
现在执行以下查询:
SELECT ProductName FROM Products WHERE Discount > 0.1;
如果在"Discount"列上创建了辅助索引,但是由于ICP的限制,存储引擎无法在辅助索引层面对查询进行过滤。因此,存储引擎将不得不读取完整的记录,并在上层进行条件判断。
5)无法下推涉及子查询的条件。
假设有两个表,"Orders"和"Customers",它们之间有一个关联关系。"Orders"表包含订单信息,"Customers"表包含客户信息。现在我们想要查询所有购买过特定产品的客户的姓名。
以下是一个使用子查询的查询示例:
SELECT CustomerName FROM Customers WHERE CustomerID IN (SELECT CustomerID FROM Orders WHERE ProductID = 123);
在这个查询中,子查询 (SELECT CustomerID FROM Orders WHERE ProductID = 123) 返回了购买过产品ID为123的订单的客户ID列表。然后,外部查询根据这个客户ID列表来获取客户的姓名。
然而,由于涉及到子查询,存储引擎无法将子查询的条件下推到索引层面进行处理。这意味着存储引擎需要在上层执行子查询,获取客户ID列表,并将结果返回给存储引擎进行进一步的条件判断。
6)无法下推涉及存储函数的条件。存储引擎无法调用存储函数。
MySQL的存储引擎无法将涉及存储函数的条件下推到索引层面进行处理。这意味着存储引擎无法直接调用存储函数来处理查询条件,而是需要在上层执行存储函数,并将结果返回给存储引擎进行进一步的条件判断。
让我们通过一个示例来说明这一点:
假设有一个名为"Products"的表,其中包含以下列:ProductID(主键)、ProductName和Price。
现在我们想要查询所有价格大于某个阈值的产品的名称。假设有一个存储函数calculate_discounted_price(),它接受产品ID作为参数,并返回产品的折扣后价格。
以下是一个使用存储函数的查询示例:
SELECT ProductName FROM Products WHERE calculate_discounted_price(ProductID) > 100;
在这个查询中,我们希望通过存储函数calculate_discounted_price()计算每个产品的折扣后价格,并将其与阈值进行比较。然而,由于涉及到存储函数,存储引擎无法直接调用存储函数来处理查询条件。
因此,存储引擎需要在上层执行存储函数calculate_discounted_price(),获取每个产品的折扣后价格,并将结果返回给存储引擎进行进一步的条件判断。这可能导致额外的计算和内存开销。
7)无法下推触发器中的条件。
MySQL的存储引擎无法将触发器中的条件下推到索引层面进行处理。这意味着存储引擎无法直接使用索引来处理触发器中的条件,而是需要在上层执行触发器,并将结果返回给存储引擎进行进一步的条件判断。
让我们通过一个示例来说明这一点:
假设有一个名为"Orders"的表,其中包含以下列:OrderID(主键)、CustomerID、OrderDate和TotalAmount。现在我们希望在插入新订单时,根据订单的总金额自动更新客户的信用额度。
为了实现这个功能,我们可以创建一个"Orders"表的BEFORE INSERT触发器,其中包含更新客户信用额度的逻辑。例如,以下是一个示例触发器:
CREATE TRIGGER update_credit_limit BEFORE INSERT ON Orders FOR EACH ROW BEGIN UPDATE Customers SET CreditLimit = CreditLimit - NEW.TotalAmount WHERE CustomerID = NEW.CustomerID; END;
在这个示例中,触发器中的条件是 CustomerID = NEW.CustomerID,用于匹配要更新的客户。然而,由于触发器中的条件无法下推,存储引擎无法直接使用索引来处理该条件。
因此,存储引擎需要在上层执行触发器,并将结果返回给存储引擎进行进一步的条件判断。
8)(适用于 MySQL 8.0.30 及更高版本)无法将条件下推到包含对系统变量引用的派生表。
在MySQL 8.0.30及更高版本中,当派生表(Derived Table)中包含对系统变量的引用时,条件下推优化无法进行。
派生表是指在查询中使用子查询(子查询作为临时表)的情况。当派生表中的查询条件涉及对系统变量的引用时,MySQL无法将该条件下推到派生表的查询层面进行处理。
让我们通过一个示例来说明这一点:
假设有一个名为"Orders"的表,其中包含以下列:OrderID(主键)、CustomerID和OrderDate。我们希望查询某个客户在过去一周内的订单数量。
以下是一个包含派生表的查询示例:
SELECT CustomerID, COUNT(*) AS OrderCount FROM ( SELECT CustomerID FROM Orders WHERE OrderDate >= CURDATE() - INTERVAL 1 WEEK ) AS DerivedTable GROUP BY CustomerID;
在这个查询中,派生表使用了子查询来获取过去一周内的订单的CustomerID。子查询中的条件是 OrderDate >= CURDATE() - INTERVAL 1 WEEK,其中 CURDATE() 是一个系统变量,用于获取当前日期。
然而,由于派生表中包含对系统变量的引用,MySQL无法将该条件下推到派生表的查询层面进行处理。因此,MySQL需要在上层执行派生表的查询,并将结果返回给存储引擎进行进一步的条件判断。
如果需要本文 WORD、PDF 相关文档请在评论区留言!!!
如果需要本文 WORD、PDF 相关文档请在评论区留言!!!
如果需要本文 WORD、PDF 相关文档请在评论区留言!!!