5.6 explain update一个疑似bug

本文介绍了 MySQL 5.6 版本中新加入的 DML Explain 特性,该特性允许用户对复杂的 UPDATE 和 DELETE 语句进行性能分析。文章通过实例演示了如何使用这一功能,并指出在某些情况下,该特性的实现存在 bug,导致结果不准确。此外,还对比了单表和多表场景下的不同表现。

5.6 的新增特性,允许对DML语句做explain。这下大家高兴了,碰到复杂更新语句(且还造成慢查询)要自己手动改成select语句的日子终于到头了。

    饶有兴致的试用了一把,总体感觉不错,不过发现一个bug。

 

复现    

mysql> create table tb(id int primary key , c int);
Query OK, 0 rows affected (0.01 sec)

 

mysql> insert into tb values(1,1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb values(2,2);
Query OK, 1 row affected (0.00 sec)

mysql> explain select * from tb where id=1;
+—-+————-+——-+——-+—————+———+———+——-+——+——-+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+—-+————-+——-+——-+—————+———+———+——-+——+——-+
|  1 | SIMPLE      | tb    | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL |
+—-+————-+——-+——-+—————+———+———+——-+——+——-+
1 row in set (0.00 sec) 

mysql> explain update tb   tb set c=2 where id=1;            
+—-+————-+——-+——-+—————+———+———+——+——+————-+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+—-+————-+——-+——-+—————+———+———+——+——+————-+
|  1 | SIMPLE      | tb    | range | PRIMARY       | PRIMARY | 4       | NULL |    1 | Using where |
+—-+————-+——-+

 这里我们看到,在explain update的结果中,type=range,并且Extra是Using where。

  这两个值的意思就是,MySQL做了一个range查询,即使只返回一行,也多做了一个next操作,而且Using where,表示从主键取出来的行还做了字段比较。

或者更直接一点,跟explain select的结果不一致。

 

 分析

         刚好趁机看一下explain dml的实现。发现explain update/deelte 不是沿用原来的逻辑,而是单独实现了一个Explain_table来处理。

         在这个Explain_table的explain_join_tab逻辑(也就是获取返回结果的type字段的值)中,返回值里没有返回JT_CONST的选项,只有merge和range。

         相同的,由于没有考虑const的情况,在得到Extra列的过程中,最少是Using Where

        实际上,如果对于唯一键上的等值查找,是不需要做range查询,并且返回结果也不需要做值判断

 

 实际执行流程

         Explain 多少是个锦上添花的动作,但是如果实际执行也是这么做,那就会影响性能(增加一次调用next和值比较),这就会对使用性能造成影响。

         所幸的是,5.6.10在执行期间的逻辑还是正确的: 对于unique index的等值查询,只返回一行,在get_next()时有判断,直接返回HA_ERR_END_OF_FILE。

 
多表的explain update   

mysql> explain update tb ,t1  set c=c+1,y=y+1 where tb.id=1 and t1.id=1;
+—-+————-+——-+——-+—————+———+———+——-+——+——-+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+—-+————-+——-+——-+—————+———+———+——-+——+——-+
|  1 | SIMPLE      | tb    | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
|  1 | SIMPLE      | t1    | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+—-+————-+——-+——-+—————+———+———+——-+——+——-+

 可以看到多表的explain update结果就是正确的,原因是单表的explain update/delete单独实现了Explain_table,而多表与explain select相同,走的是另外一个类Explain_join,这个过程的逻辑与以前版本(5.5-)是相同的。

 有兴趣的同学可以一起探讨下这个patch

`dict.update()` 是 Python 中用于**更新字典内容**的方法。它可以将一个字典或其他可迭代对象中的键值对合并到原字典中,如果键已存在则覆盖其值,如果不存在则新增。 --- ### ✅ 基本语法 ```python dict.update(other) ``` - `other` 可以是: - 另一个字典(`dict`) - 包含 `(key, value)` 元组的可迭代对象(如列表、元组) - 关键字参数(`update(key=value)`) --- ### ✅ 示例代码 #### 1. 使用另一个字典更新 ```python d = {'a': 1, 'b': 2} d.update({'b': 3, 'c': 4}) print(d) # 输出: {'a': 1, 'b': 3, 'c': 4} ``` > 注意:`'b'` 被更新为 `3`,`'c'` 是新增项。 --- #### 2. 使用关键字参数更新 ```python d = {'a': 1, 'b': 2} d.update(b=3, c=4) print(d) # 输出: {'a': 1, 'b': 3, 'c': 4} ``` > ⚠️ 这种方式中,关键字必须是合法的变量名(不能用数字或特殊字符作为键)。 --- #### 3. 使用包含元组的列表更新 ```python d = {'a': 1, 'b': 2} d.update([('b', 3), ('c', 4)]) print(d) # 输出: {'a': 1, 'b': 3, 'c': 4} ``` > 适用于从配置、数据库记录等结构化数据批量加载。 --- #### 4. 使用生成器表达式 ```python d = {'x': 10} d.update((chr(97 + i), i) for i in range(3)) # chr(97)=a, chr(98)=b... print(d) # 输出: {'x': 10, 'a': 0, 'b': 1, 'c': 2} ``` > 高效处理大量动态键值对。 --- ### 🔁 合并与创建新字典的区别 `update()` **就地修改原字典**,不返回新字典: ```python d1 = {'a': 1} d2 = {'b': 2} d1.update(d2) print(d1) # {'a': 1, 'b': 2} print(d2) # {'b': 2} (未改变) ``` 如果你不想修改原字典,应使用: ```python merged = {**d1, **d2} # Python 3.5+ # 或 merged = d1 | d2 # Python 3.9+ 的合并操作符 ``` --- ### 🧠 常见用途 | 场景 | 示例 | |------|------| | 默认配置覆盖 | `config.update(user_config)` | | 合并多个数据源 | `user_data.update(profile, preferences)` | | 动态构建字典 | `result.update([(k, func(v)) for k, v in items])` | --- ### ❗ 注意事项 1. **`update()` 返回 `None`** 它是就地操作,不要写成 `d = d.update(...)`,否则 `d` 会变成 `None`。 2. **键重复时后值覆盖前值** 更新顺序决定最终结果。 3. **支持任意可哈希类型作为键** 如数字、字符串、元组、布尔值等。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值