{{ item.name }}
{{ item.name }}

{{ it.name }}

{{ it.text }}

{{ it.name }}

{{ innerIt.name }}

{{ innerIt.text }}

{{news.time}}
{{news.title}}
技术分享 | HASH_SCAN BUG 之迷惑行为大赏
2021-01-22发布 1,004浏览

关键字:MySQL数据库优化MySQL性能调优MySQL性能调优与架构设计

前言 

我们知道,MySQL 有一个老问题,当表上无主键时,那么对于在该表上做的DML,如果是以ROW模式复制,则每一个行记录前镜像在备库都可能产生一次全表扫描(或者二级索引扫描),大多数情况下,这种开销都是非常不可接受的,并且产生大量的延迟。

在MySQL 5.6 中提供了一个新的参数:slave_rows_search_algorithms, 可以部分解决无主键表导致的复制延迟问题,其基本思路是对于在一个ROWS EVENT中的所有前镜像收集起来,然后在一次扫描全表时,判断HASH中的每一条记录进行更新。

以上摘自印风大神的文章,具体效果和使用方式可以查看文章:https://yq.aliyun.com/articles/41058。


HASH_SCAN BUG 的迷惑行为

本文不是要继续讨论 slave_rows_search_algorithms 的原理,而是在使用 slave_rows_search_algorithms 参数时遇到一个坑,扑朔迷离的地方在于这不像一个bug,这又是一个bug,最后官方的操作更是让人看不懂。

问题描述

row-based replication,主从数据一致情况下,slave sql 线程报错 Can't find record。

如何复现

配置:

slave_rows_search_algorithms = 'INDEX_SCAN,HASH_SCAN'

binlog_format = ROW

在主库上:

1. CREATE TABLE t1 ( A INT UNIQUE KEY, B INT ); insert into t1 values (1,2);

2. replace into t1 values (1,3),(1,4);

3. 然后从库就会出现报错了。

4. 在从库上:set global slave_rows_search_algorithms='INDEX_SCAN,TABLE_SCAN'; start slave; 问题解决

或者用如下方法避免:

1. 将 UNIQUE KEY 调整为 PRIMARY KEY;

2. 将 replace into tt.t1 values (1,3),(1,4); 调整为 replace into tt.t1 values (1,3);replace into tt.t1 values (1,4);

分析过程: 

查看 slaverowssearch_algorithms 参数定义:

The default value is INDEX_SCAN,TABLE_SCAN, which means that all searches that can use indexes do use them, and searches without any indexes use table scans.

To use hashing for any searches that do not use a primary or unique key, set INDEX_SCAN,HASH_SCAN. Specifying INDEX_SCAN,HASH_SCAN has the same effect as specifying INDEX_SCAN,TABLE_SCAN,HASH_SCAN, which is allowed.

Do not use the combination TABLE_SCAN,HASH_SCAN. This setting forces hashing for all searches. It has no advantage over INDEX_SCAN,HASH_SCAN, and it can lead to “record not found” errors or duplicate key errors in the case of a single event containing multiple updates to the same row, or updates that are order-dependent.

(1)根据手册描述,可以理解为:

从库定位数据可选项有 INDEX_SCAN、HASH_SCAN、TABLE_SCAN,优先级是依次递减的。也就是说如果有主键,走 INDEX_SCAN,没主键则根据设置走 HASH_SCAN 还是 TABLE_SCAN(而且如果两者都配了,则优先 HASH_SCAN);无主键设置 INDEX_SCAN,HASH_SCAN,可以提升从库回访效率,降低延迟;如果走了 HASH_SCAN,当对同一行数据同时更新多次时,会导致无法找到行记录。看到这里,手册已经说明原因了。但是矛盾的地方在于手册有推荐使用 INDEX_SCAN,HASH_SCAN 的意思,并且 8.0.2 版本开始的默认值已经修改为:INDEX_SCAN,HASH_SCAN

(2)针对上面的疑问提交SR

Oracle 工程师回应这是 HASH_SCAN 的 bug,修复到了 MySQL8.0.17(还没有正式发布):

It happens when one row is updated twice within an Update_rows_log_event. HASH_SCAN will put both rows in a hash map. Then it iterates over the rows of the table, looks up each row in the hash. If any row is found, it applies the update. Since it only makes one lookup per row, it will miss the second update. In the end, it checks that all rows in the hash were applied and generates an error otherwise. This is what is seen in the bug report.

等等,为什么只修复到 8.0 而没在 5.7 修复?再根据之前就有的疑问,我来脑补一波:

1. 文档写了在某些情况 HASH_SCAN 有这个问题(吐槽:HASH_SCAN 就是优化无主键情况从库复制效率的,但是无主键且对同一行数据同时更新多次时 HASH_SCAN 又会导致从库无法找到记录,那我用还是不用呢?黑人问号.gif),所以暂时我先假设“这不是个 bug”;

2. Oracle 工程师反手甩给我一个 bug 报告,啪啪打脸,官方承认这就是一个 bug;

3. 官方好像也认为有点不对劲,一拍脑袋,咱们只在 8.0 修复,把锅甩给 “8.0.2 的默认值修改成了 HASH_SCAN”。

这个迷惑行为可以用一句经典总结:it's not a bug,it's a feature!

解决方案

1. 给表添加主键(规范必须有主键才是王道);

2. 修改参数 slave_rows_search_algorithms='INDEX_SCAN,TABLE_SCAN';

3. 最符合初衷的做法是升级到 8.0.17,可惜这步对于绝大多数生产环境来说都太大了。

关键字:MySQL数据库优化MySQL性能调优MySQL性能调优与架构设计

上一篇
新特性解读 | MySQL 8.0 窗口函数框架用法
400-820-6580 13916131869
marketing@actionsky.com
上海市闵行区万源路2138号泓茂中心2号楼
产品详情
关系型数据库
AI数据库
数据库智能管理平台
数据库生态产品
行业案例
金融行业
新零售行业
制造业
通信行业
更多
公司动态
最新新闻
国产化信息
技术分享
关于我们
公司简介
公司分布
国家专利
资质认证
扫码关注公众号
© Copyright 2017, All rights reserved by: 上海爱可生信息技术股份有限公司 沪ICP备12003970号-1 | 法律声明 | 网站地图
沪公网安备 31010402003331号