惊呆:where 1=1 可能严重影响性能,差了10多倍,快去排查你的 sql
文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
惊呆:where 1=1 可能严重影响性能,差了10多倍,快去排查你的 sql
尼恩特别说明: 尼恩的文章,都会在 《技术自由圈》 公号 发布, 并且维护最新版本。 如果发现图片 不可见, 请去 《技术自由圈》 公号 查找
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团、蚂蚁、得物的面试资格,遇到很多很重要的相关面试题:
MySQL中“where 1=1” 条件 影响性能么,为什么?
听说 MySQL中“where 1=1” 条件,部分场景会严重 影响性能,是哪些场景呢, 该怎么解决?
最近有小伙伴面试网易,都问到了这个面试题。小伙伴没有系统的去梳理和总结,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V175版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取
MySQL中“where 1=1”的性能影响深究
“where 1=1” 这个条件, 非常常见,也非常好用。
但是, 隐藏在背后一个巨大的 性能问题。 通过此文, 老架构师尼恩给大家来一一揭秘, 帮助大家内力猛增。
“where 1=1”在动态 SQL 中的应用
WHERE 1 = 1
在动态 SQL 场景中经常被使用,很多开发者喜欢在构建动态SQL查询时使用它,因为它可以方便地添加额外的查询条件。
在动态构建 SQL 查询语句时,添加WHERE 1 = 1
可以方便地在其后添加其他条件,“1=1” 常被用作where 的 一个条件占位符。
有了 1 = 1,当我们可以动态地添加多个查询条件,不同担心“AND”或“OR”关键字的拼接问题。
例如,在一个 Java 程序中动态构建 SQL 查询:
String sql = "SELECT * FROM table_name WHERE 1 = 1";
if (condition1) {
sql += " AND column1 = value1";
}
if (condition2) {
sql += " AND column2 > value2";
}
“where 1=1”会导致严重的性能问题
虽然 “where 1=1” 使用的时候很方便,但是 存在严重的 性能隐患。
然而,这种做法存在风险,在部分罕见场景,就可能导致意外的全表扫描,影响查询性能。
什么罕见场景呢? 就是where没有一个真正的条件,只剩下1=1,就可能导致意外的全表扫描。
所以在使用动态 SQL 时,需要谨慎处理这种情况,确保在必要的时候添加合适的筛选条件来避免不必要的全表扫描。
性能影响测试
where 1=1
到底会不会影响性能?我们可以先看一个具体的例子:
尼恩使用的mysql 版本比较老 是5.7,可以使用如下指令查看 MySQL版本:
SELECT VERSION();
场景:基于一张拥有 50W 条数据的test_user 表,根据height (带索引的列)进行查询,
使用存储过程,插入50W条测试数据, 具体请参见尼恩的前面文章
美团面试:mysql 索引失效?怎么解决?(重点知识,建议收藏,读10遍+)
所创建的test_user 中包含:id
、id_card
、age
、user_name
和height
、address字段。
test_user 的结构如下:
CREATE TABLE `test_user` (
`id` int NOT NULL AUTO_INCREMENT,
`id_card` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`age` int DEFAULT '0',
`user_name` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
`height` int DEFAULT '0',
`address` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_id_card_age_user_name` (`id_card`,`age`,`user_name`),
KEY `idx_height` (`height`))
ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
查看表结构和表的总数据,如下图:
下面,通过执行两条 SQL查询语句(一条带有 1=1):
explain format=json SELECT * FROM test_user WHERE 1 = 1 LIMIT 10,10
explain format=json SELECT * FROM test_user WHERE 1 = 1 and height=120 LIMIT 10,10
场景1:后面不带真实查询条件的where 1=1的执行成本
对 不带 真实查询条件 的where 1=1的sql,通过explain 查看执行计划 和 执行成本。
代码如下:
explain format=json SELECT * FROM test_user WHERE 1 = 1 LIMIT 10,10
执行结果如下:
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "100134.80"
},
"table": {
"table_name": "test_user",
"access_type": "ALL",
"rows_examined_per_scan": 489294,
"rows_produced_per_join": 489294,
"filtered": "100.00",
"cost_info": {
"read_cost": "2276.00",
"eval_cost": "97858.80",
"prefix_cost": "100134.80",
"data_read_per_join": "160M"
},
"used_columns": [
"id",
"id_card",
"age",
"user_name",
"height",
"address"
]
}
}
}
以下是对这个 JSON 格式的执行计划输出的解释:
一、整体结构
"query_block"
: 代表整个查询的执行计划块,包含了关于这个查询的各种信息。
二、查询标识符和 执行成本
-
"select_id": 1
:查询的唯一标识符为 1。 -
"cost_info"
:
"query_cost": "100134.80"
: 查询的预估总成本为 100134.80。
这个成本是数据库优化器根据多种因素估算出来的,包括读取数据的成本、评估条件的成本等。
一般来说,成本越低,查询的执行效率可能越高。
关于 sql 语句执行成本的详细介绍, 请参考尼恩的前面的深度文章:
三、表相关信息
-
"table_name": "test_user"
: 查询涉及的表名为test_user
。 -
"access_type": "ALL"
:
访问表的方式为全表扫描(ALL
)。
这意味着数据库在执行这个查询时没有使用索引,而是扫描了整个表来获取数据。
全表扫描通常在没有合适的索引可用或者优化器认为全表扫描比使用索引更高效的情况下发生。
"rows_examined_per_scan": 489294
:
每次扫描检查的行数为 489294。
这表示在执行查询过程中,数据库预计需要检查这么多行的数据。
"rows_produced_per_join": 489294
: 每次连接产生的行数为 489294。
如果查询涉及多个表的连接,这个值表示从这个表中产生的行数,用于连接操作。在这个例子中,可能没有连接操作,所以这个值与 rows_examined_per_scan
相同。
"filtered": "100.00"
: 过滤后的行数比例为 100%。
过滤后,所有满足条件的行数占总检查行数的比例为 100%。这意味着经过筛选条件(在这个例子中,WHERE 1 = 1
实际上没有进行有效的筛选)
-
"cost_info"
:"read_cost": "2276.00"
: 读取数据的成本为 2276.00。"eval_cost": "97858.80"
: 评估条件的成本为 97858.80。在这个例子中,由于WHERE 1 = 1
没有实际的评估工作,这个成本可能主要来自其他方面,如读取数据后的处理。"prefix_cost": "100134.80"
: 前缀成本为 100134.80,通常是指查询的总成本减去某些特定操作的成本。"data_read_per_join": "160M"
: 每次连接读取的数据量为 160M(可能是 160 兆字节)。
-
"used_columns": ["id", "id_card", "age", "user_name", "height", "address"]
:
查询使用的列包括 id
、id_card
、age
、user_name
、height
和 address
。
这意味着查询将从表中读取这些列的数据。
综上所述,这个执行计划表明查询使用了全表扫描的方式来获取数据,成本较高。
可能需要考虑创建合适的索引或者优化查询语句来提高查询性能。
read_cost、 eval_cost、prefix_cost 三个成本的关系
-
read_cost(读取数据成本)
定义:read_cost 主要是指从存储介质(如磁盘或内存)中读取数据所产生的成本。在数据库操作中,数据存储在磁盘上的文件或者内存中的缓存区域。当执行查询时,需要将相关的数据块读取到内存中进行处理,这个过程就会产生 read_cost。
影响因素:
-
数据量:要读取的数据量越大,read_cost 就越高。例如,对于一个全表扫描的查询,需要读取整个表的数据,read_cost 会比只读取少量满足条件的数据行高很多。
-
存储介质性能:磁盘的 I/O 速度会直接影响 read_cost。如果是机械硬盘,其 I/O 速度相对较慢,read_cost 会比较高;而固态硬盘(SSD)的 I/O 速度快,read_cost 会相应降低。另外,内存读取速度比磁盘快很多,所以如果数据已经在内存缓存中,read_cost 会比从磁盘读取低。
-
数据分布和存储格式:数据在磁盘上的分布情况以及存储格式也会影响 read_cost。例如,数据是否按照某种顺序存储(如按照索引顺序),如果是,可能会减少磁盘寻道时间,从而降低 read_cost。
-
示例:在一个查询中,假设需要读取一个包含 100 万行数据的表,每行数据大小为 1KB,从磁盘读取数据块的单位成本为 1(这是一个抽象的成本单位),如果是全表扫描,read_cost 可能就会很高,约为 100 万(行数)* 1KB(每行大小)* 1(单位成本)。
-
-
eval_cost(评估条件成本)
定义:eval_cost 是指对查询条件进行评估所产生的成本。在 SQL 查询中,WHERE 子句用于筛选数据,数据库需要对每一行数据的条件进行判断,这个判断操作就会产生 eval_cost。
影响因素:
-
条件复杂度:如果查询条件很复杂,包含多个逻辑运算符(如 AND、OR)、函数(如 SUM、AVG)或者嵌套的子查询,eval_cost 会增加。例如,一个 WHERE 子句中有多个复杂的函数调用和嵌套子查询,数据库需要执行更多的计算和比较操作来评估条件,从而增加 eval_cost。
-
数据类型和索引使用:数据类型也会影响 eval_cost。例如,比较整数类型的条件可能比比较文本类型的条件更快。如果能够利用索引来帮助评估条件,eval_cost 可能会降低。例如,对于一个有索引的列,数据库可以通过索引快速定位满足条件的行,减少了对每一行数据的详细评估,从而降低 eval_cost。
-
示例:对于一个查询条件为 WHERE age > 30 AND salary < 5000 的查询,数据库需要对每一行数据的 age 和 salary 列进行比较操作。如果表中有 10 万行数据,每次比较操作的成本假设为 0.01(抽象成本单位),那么 eval_cost 大约为 10 万(行数)* 2(两个条件)* 0.01(单位成本)。
-
-
prefix_cost(前缀成本)
定义:prefix_cost 是查询的一种累积成本估算,它通常是查询总成本减去某些特定操作的成本。具体来说,它考虑了在执行查询过程中的多个步骤的成本累积,包括读取数据、评估条件以及其他可能的操作(如排序、分组等),但可能会排除一些后续的特定操作成本,以便于在执行计划比较等场景中进行更灵活的成本分析。
与其他成本的关系:prefix_cost 是 read_cost 和 eval_cost 等成本的综合体现,并且还可能包含其他相关操作的成本。它的计算方式和具体包含的内容可能因数据库系统的优化器实现而有所不同。一般来说,它可以帮助用户和优化器快速判断一个查询执行计划在主要操作阶段的成本情况。
示例:假设一个查询的总成本为 100,其中排序操作成本为 10,那么 prefix_cost 可能就是 90,它反映了在不考虑排序操作的情况下,读取数据和评估条件等主要操作的成本。在执行计划选择过程中,数据库优化器可能会比较不同执行计划的 prefix_cost,选择成本较低的计划作为主要的候选方案,然后再考虑其他操作(如排序)的成本来最终确定执行计划。
场景2:后面 带有真实查询条件 的where 1=1的执行成本
对 带有真实查询条件 的where 1=1的sql,通过explain 查看执行计划 和 执行成本。
代码如下:
explain format=json SELECT * FROM test_user WHERE 1 = 1 and height=120 LIMIT 10,10
执行结果如下:
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "7252.80"
},
"table": {
"table_name": "test_user",
"access_type": "ref",
"possible_keys": [
"idx_height"
],
"key": "idx_height",
"used_key_parts": [
"height"
],
"key_length": "5",
"ref": [
"const"
],
"rows_examined_per_scan": 6044,
"rows_produced_per_join": 6044,
"filtered": "100.00",
"cost_info": {
"read_cost": "6044.00",
"eval_cost": "1208.80",
"prefix_cost": "7252.80",
"data_read_per_join": "1M"
},
"used_columns": [
"id",
"id_card",
"age",
"user_name",
"height",
"address"
]
}
}
}
以下是对这个 JSON 格式执行计划输出的详细解释:
一、整体结构
"query_block"
: 代表整个查询的执行计划块,包含了关于这个查询的各种关键信息。
二、查询标识符和成本信息
-
"select_id": 1
:查询的唯一标识符为 1。 -
"cost_info"
:"query_cost": "7252.80"
: 查询的预估总成本为 7252.80。这个成本是数据库优化器根据多种因素估算出来的,包括读取数据的成本、评估条件的成本等。相对较低的成本通常意味着执行计划较为高效。
三、表相关信息
"table_name": "test_user"
: 查询涉及的表名为test_user
。"access_type": "ref"
: 访问表的方式为ref
,表示通过非唯一索引进行查找。
在这个例子中,说明查询使用了索引进行查找,并且不是唯一索引。
"possible_keys": ["idx_height"]
:
可能使用的索引列表为 idx_height
,这表明优化器考虑了这个索引,并最终选择了它。
"key": "idx_height"
:
实际使用的索引是 idx_height
,说明优化器认为使用这个索引可以提高查询性能。
"used_key_parts": ["height"]
: 使用的索引部分是height
列,表明索引idx_height
是基于height
列创建的,并且在查询中只使用了这个索引的height
部分。"key_length": "5"
: 索引的长度为 5,可能与存储height
列的值所需的空间有关。"ref": ["const"]
: 索引引用的值是常量,这里可能是查询条件中的某个具体值。"rows_examined_per_scan": 6044
: 每次扫描检查的行数为 6044。这表示在执行查询过程中,需要检查的行数估计为 6044 行。相比全表扫描,这个行数较少,说明索引的使用有效地减少了需要检查的数据量。"rows_produced_per_join": 6044
: 每次连接产生的行数为 6044。如果查询涉及多个表的连接,这个值表示从这个表中产生的行数,用于连接操作。在这个例子中,可能没有连接操作,所以这个值与rows_examined_per_scan
相同。"filtered": "100.00"
: 过滤后的行数比例为 100%。这意味着经过筛选条件(可能与height
列相关)过滤后,所有满足条件的行数占总检查行数的比例为 100%。"cost_info"
:
"read_cost": "6044.00"
: 读取数据的成本为 6044.00。"eval_cost": "1208.80"
: 评估条件的成本为 1208.80。"prefix_cost": "7252.80"
: 前缀成本为 7252.80,通常是指查询的总成本减去某些特定操作的成本。"data_read_per_join": "1M"
: 每次连接读取的数据量为 1M(可能是 1 兆字节)。
"used_columns": ["id", "id_card", "age", "user_name", "height", "address"]
: 查询使用的列包括id
、id_card
、age
、user_name
、height
和address
。这意味着查询将从表中读取这些列的数据。
综上所述,这个执行计划表明查询使用了名为 idx_height
的索引进行查找,有效地减少了需要检查的行数和读取的数据量,从而降低了查询成本。
两条执行计划的对比
可以生成两条执行计划,一个是走索引(idx_height)的执行计划,另一个是全表扫描的执行计划,并且通过sql分析我们发现两者的cost值不一样的,cost的值如下所示:
执行方式 | cost |
---|---|
where 1=1` 后面 有带真实条件 , 走索引 | 7252.80 |
where 1=1` 后面 没有带真实条件 , 没 走索引,全表扫描 | 100134.80 |
对比两条 SQL执行的结果,可以发现它们消耗的时间几乎相同,因此,看起来where 1=1
后面有没有带真实条件,对整体的性能 影响非常大 。
1=1 本质上被mysql 优化掉了
实际上: where 后面的1=1 条件,本质上被mysql 优化掉了
回顾一下:查询优化器、执行器的工作原理
MySQL 的查询优化器在生成执行计划之前会对查询语句进行分析。
- 上图中 优化器 + 执行器 是哼哈二将:
- 根据sql的结构生成不同的执行计划,然后选择一个最优的 plan 计划 , 是MySQL优化器 的主要任务。
- 执行这个最优的执行计划 plan ,是 MySQL执行器 的主要任务。
执行器最终 执行的,是mysql 认为的,效率最高的执行计划, 执行sql并返回数据。
MySQL 优化器职责
-
语法和语义解析
当接收到一个 SQL 查询语句时,优化器首先会对语句进行语法和语义解析。
它会识别出查询中的关键字(如 SELECT、FROM、WHERE、JOIN 等)、表名、列名、函数调用、操作符以及各种条件表达式等元素。这是理解查询意图的基础步骤。
例如,对于查询语句 “SELECT * FROM users WHERE age> 18 AND gender = 'male'”,优化器会解析出这是一个从 “users” 表中选择所有列,并且筛选出年龄大于 18 岁且性别为男性的记录的查询。
-
查询重写
优化器会尝试对查询进行重写,以使其更高效或符合优化规则。
这可能包括简化复杂的表达式、消除冗余的子句等操作。
例如,对于一些包含嵌套子查询的复杂查询,优化器可能会将其转换为等价的连接操作,以提高查询性能。
另外,如果查询中存在重复的条件或者可以通过逻辑推导简化的条件,优化器也会进行相应的处理。
-
生成执行计划
这是优化器的核心职责之一。
它会根据解析后的查询和数据库的元数据(如表结构、索引信息、统计信息等)生成多个可能的执行计划。
执行计划描述了数据库如何执行查询,包括访问哪些表、以何种顺序访问、是否使用索引、如何进行连接操作等细节。
例如,对于一个涉及多表连接的查询,优化器可能会生成多种连接顺序不同的执行计划,如先连接表 A 和表 B,再连接表 C,或者先连接表 B 和表 C,再连接表 A 等不同的方案。
-
成本估算和计划选择
对于每个生成的执行计划,优化器会估算其执行成本。
成本估算考虑多种因素,主要包括 I/O 成本(从磁盘读取数据的成本)和 CPU 成本(对数据进行处理的成本,如比较、排序、函数计算等)。
优化器会根据表和索引的统计信息(如表的行数、索引的键值分布、列的基数等)来计算这些成本。
然后,它会选择成本最低的执行计划作为最终的执行计划。
例如,如果一个执行计划需要进行大量的全表扫描,I/O 成本较高,而另一个执行计划可以通过使用索引有效减少数据读取量,优化器会倾向于选择后者。
MySQL 执行器职责
-
执行计划的执行
一旦优化器确定了最终的执行计划,执行器就负责按照这个计划执行查询。
它会根据执行计划中规定的步骤,逐个操作地处理查询。
例如,如果执行计划要求先从某个表中读取数据,执行器就会向存储引擎发出相应的读取请求,获取数据行。
如果执行计划包括连接操作,执行器会按照指定的连接算法(如嵌套循环连接、哈希连接等)对数据进行连接处理。
-
数据读取和操作
执行器负责从存储引擎读取数据,并根据查询的要求进行相应的操作。
这包括读取表中的行数据、获取索引中的键值信息等。在读取数据的过程中,执行器会遵循存储引擎的接口和规则。
例如,对于 InnoDB 存储引擎,执行器会通过 InnoDB 提供的接口来读取表空间文件(.ibd 文件)中的数据。同时,执行器会对读取的数据进行操作,如根据 WHERE 子句中的条件进行筛选,对选定的列进行投影操作(即选择查询中指定的列)等。
-
事务管理(如果涉及)
如果查询是在事务环境中进行的,执行器需要参与事务的管理。
它会负责开启事务、提交事务或者回滚事务等操作,以确保数据的一致性和完整性。
例如,在一个包含多个更新操作的事务查询中,执行器会按照事务的要求,先将更新操作记录到事务日志(如 InnoDB 的 redo 日志)中,在事务提交时确保所有的更新操作都持久化到磁盘,或者在事务回滚时撤销已经执行的部分更新操作。
-
结果集生成和返回
执行器会根据查询的要求生成最终的结果集。
这可能包括对数据进行排序、分组、聚合等操作。
例如,对于一个带有 ORDER BY 子句的查询,执行器会对读取的数据进行排序,然后将排序后的结果返回给客户端。
在生成结果集的过程中,执行器会遵循 SQL 标准和数据库的特定规则,确保结果的准确性和完整性。
最后,执行器将结果集返回给客户端应用程序,完成查询的执行过程。
MySQL 优化器对于1=1条件的优化工作
优化器最终选择最优或者执行效率最高的执行计划, 交给执行器去执行sql并返回数据。
MySQL 优化器的语义解析阶段
优化器的主要目标是找到一个高效的执行计划,以最小化查询的成本(包括 I/O 成本和 CPU 成本)。
优化器的语义解析阶段, 会解析 SQL 语句的语法和语义,提取表名、列名、条件表达式等信息。
对于WHERE 1 = 1
这样的条件,优化器会判断它是一个恒为真的表达式。
当它发现WHERE 1 = 1
这个条件不会对结果集进行实质性的筛选,并且不会影响索引的使用或其他优化策略时,就会尝试忽略这个条件。
MySQL 有一系列内置的优化规则。其中一条规则可能是识别并忽略没有实际筛选效果的常量条件。对于WHERE 1 = 1
,它符合这种没有实际筛选作用的情况,因此可以根据规则被优化掉。
MySQL 优化器的成本估算和计划选择
在评估成本时,它会考虑多种因素,如是否使用索引、表的连接方式、数据量等。
在生成多个可能的执行计划时,优化器会计算每个计划的成本。
对于包含WHERE 1 = 1
的查询,优化器会在计算成本的过程中发现这个条件不会对数据的读取量、比较操作的复杂度等成本因素产生实质性的增加。
例如,在计算 I/O 成本时,因为WHERE 1 = 1
不会减少或增加需要从磁盘读取的数据页数量,所以在选择执行计划时,会倾向于选择那些不含有WHERE 1 = 1
这个条件的更高效的计划。
所以,无论在 优化器的语义解析阶段,还是在 优化器的成本估算和计划选择, 都会把 1=1 这个条件忽略。
where 1=1的被优化后的结果
如果查询语句中除了WHERE 1 = 1
之外,没有其他真正有筛选作用的条件,并且没有指定`Order by等使用索引的子句,数据库优化器可能会认为没有足够的信息来使用索引进行高效的查询,从而选择全表扫描的方式来获取数据。
例如,对于查询SELECT * FROM test_user WHERE 1 = 1
,如果没有其他限制条件,数据库可能会扫描整个表来获取所有行。
但是,如果 后面指定`Order by ,就会命中索引,而不走全表扫描。
SELECT * FROM test_user WHERE 1 = 1 order by height LIMIT 10,10
where 1=1 与索引使用的关系
如果查询中有其他条件、或者其他子句(如order)可以使用索引,并且WHERE 1 = 1
不会干扰索引的使用,优化器会优先考虑索引相关的优化策略。
例如,对于查询SELECT * FROM table WHERE 1 = 1 AND column_name = 'value'
,如果column_name
列有索引,优化器会重点关注如何利用这个索引来高效地找到满足column_name = 'value'
的行,而WHERE 1 = 1
则被视为不影响索引使用的额外条件,在生成执行计划时可能被忽略。
如果没有其他的条件、其他的字句 命中索引,那么对不起, 就只能是性能退化, 退化到 全表扫描。
如何避免 where 1=1 导致的性能严重退化?
where 1=1 条件 本质是被优化了的,所以 如果没有其他条件命中索引、或者其他sql子句命中索引, 那么sql就会退化到 全表扫描。
如何防止 where 1=1 导致的性能退化?
方法一:通过其他的查询条件命中索引
比如,能够确保其他条件中,至少有一个条件 命中索引:
eg
SELECT * FROM test_user WHERE 1 = 1 and height=120
方法二:如果没有通过其他的查询条件命中索引
如果没有通过其他的查询条件命中索引 , 通过其他的sql 子句命中索引, 比如order子句。
比如下面的用id 排序
explain SELECT * FROM test_user WHERE 1 = 1 ORDER BY id LIMIT 10,10;
方法三:使用<where>
标签,去掉不必要的1=1
使用<where>
标签,去掉不必要的1=1, 比如使用 Mybatis提供的<where>
标签
<where>
标签只有在至少一个 if条件有值的情况下才去生成 where子句,若 AND或 OR前没有有效语句,where元素会将它们去除,
也就是说,如果 Mybatis通过<where>
标签动态生成的语句为 where AND name = '111'
,最终会被优化为where name = '111'
。
<where>
标签使用示例如下:
<select id="" parameterType = "">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
<where>
标签是在 MyBatis中引入的,所以,很多一开始就使用 MyBatis的用户对这个标签使用的比较多。
尼恩提示:方式三 只是去掉 不必要的 1=1 条件而已, 并没有解决根本问题。
根本问题是要通过 sql 的其他子句,比如 select 子句、order by 子句 命中索引, 从而避免全表扫描。
如果遇到 mysql 这块的很难的面试题,可以找尼恩 来交流。
尼恩架构团队的塔尖 sql 面试题
- sql查询语句的执行流程:
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
- 索引
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
滴滴面试:单表可以存200亿数据吗?单表真的只能存2000W,为什么?
- 索引下推 ?
- 索引失效
美团面试:mysql 索引失效?怎么解决?(重点知识,建议收藏,读10遍+)
- MVCC
- binlog、redolog、undo log
美团面试:binlog、redolog、undo log底层原理是啥?分别实现ACID哪个特性?(尼恩图解,史上最全)
- mysql 事务
京东面试:RR隔离mysql如何实现?什么情况RR不能解决幻读?
- 分布式事务
分布式事务圣经:从入门到精通,架构师尼恩最新、最全详解 (50+图文4万字全面总结 )
- mysql 调优
说在最后:有问题找老架构取经
回到面试题:
-
MySQL中“where 1=1” 条件 影响性能么,为什么?
-
听说 MySQL中“where 1=1” 条件,部分场景会严重 影响性能,是哪些场景呢, 该怎么解决?
只要按照上面的 尼恩团队梳理的 方案去作答, 你的答案不是 100分,而是 120分。 面试官一定是 心满意足, 五体投地。
按照尼恩的梳理,进行 深度回答,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。
很多小伙伴刷完后, 吊打面试官, 大厂横着走。
在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
另外,如果没有面试机会, 可以找尼恩来改简历、做帮扶。前段时间,刚指导一个小伙 暴涨200%(涨2倍),29岁/7年/双非一本 , 从13K一次涨到 37K ,逆天改命。
狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。
技术自由的实现路径:
实现你的 架构自由:
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
… 更多架构文章,正在添加中
实现你的 响应式 自由:
这是老版本 《Flux、Mono、Reactor 实战(史上最全)》
实现你的 spring cloud 自由:
《Spring cloud Alibaba 学习圣经》 PDF
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
实现你的 linux 自由:
实现你的 网络 自由:
《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》
实现你的 分布式锁 自由:
实现你的 王者组件 自由:
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》