上一篇文章说明了bug出现的原因和原理分析,要修复bug似乎已经水到渠成了,但远没有这么简单,只因为“并发”。要修复问题,首先要做的第一件事情是稳定的复现问题。由于数据库系统是一个并发系统,并且这个bug只有一定的概率出现,更说明了多个线程在一定的执行序列情况下才会出现这个bug。在没有用户请求的情况下,mysql自身的线程就很多,比如主线程,IO线程,监控线程,监听线程等;有用户请求的情况下,还会分配工作线程为用户服务。我们只找和bug相关的线程:purge线程,IO线程,工作线程。Purge线程主要工作是执行purge操作,产生IBUF_OP_DELETE操作;工作线程则主要执行DELETE,INSERT语句,产生IBUF_OP_INSERT和IBUF_OP_DELETE_MARK操作;IO线程则是IBUF的merge操作。为达到重现bug的目的,我们需要产生IBUF_OP_DELETE_MARK,IBUF_OP_INSERT和IBUF_OP_DELETE序列。由于这里面涉及到purge线程和用户线程,因此涉及一个协同问题。如何协同?

DEBUG_SYNC

DEBUG_SYNC是mysql源码中自带的宏,通过信号的方式实现多个线程间的同步。假设存在两个线程thread-1(A,B),thread-2(a,b),A,B和a,b分别是thread-1和thread-2的执行顺序。为了得到AaBb执行序列,可以通过DEBUG_SYNC实现。

1) 源代码中,设置同步点

Session-1(thread-1)

Session-2(thread-2)

A

a

DEBUG_SYNC(“after_A”)

DEBUG_SYNC(“after_a”)

B

b

DEBUG_SYNC(“after_B”)

2) Session设置

Session-1(thread-1)

Session-2(thread-2)

A

set debug_sync = "now wait_for stepA ";

set debug_sync="after_A signal stepA wait_for  stepa";

a

B

set debug_sync = "after_a signal stepa wait_for stepB";

set debug_sync=”after_B singal stepB”;

 b

通过在代码中设置同步点,以及在会话中设置同步关系即可以实现在并发环境下稳定地得到AaBb序列。注意到2)中的蓝色字体表示同步点,红色字体signal和 wait_for表示动作。比如“after_A signal stepA wait_for  stepa”表示执行到同步点after_A后,发出信号after_A,并且等待stepa信号的到来。另外,其中now是一个特殊的关键字,表示执行到指定位置立即wait for 或者signal。debug_sync似乎已经完美地解决了同步问题,但问题是,通过debug_sync同步,需要会话设置等待或发出信号动作,如果需要和后台线程同步怎么办?而这个bug恰好又需要和后台线程purge,以及IO线程同步。别着急,有需求,就一定有解,mysql还有另外一种协同方法,DBUG_EXECUTE_IF。

DBUG_EXECUTE_IF

DBUG_EXECUTE_IF原理就很简单了,就是在某一点执行一定的操作,至于是什么操作则没有限制。显然,我们可以在某一个点执行sleep函数,虽然无法做到严格的同步,但也基本能满足需求。DEBUG_EXECUTE_IF的基本格式为:DEBUG_EXECUTE_IF(key, code),比如为了得到AaBb序列,可以通过DBUG_EXECUTE_IF实现

1)  源代码中,设置操作点

Session-1(thread-1)

Session-2(thread-2)

DEBUG_EXECUTE_IF (“before_a”,{my_sleep(1)})

A

a

DEBUG_EXECUTE_IF (“after_A”,{my_sleep(3)})

DEBUG_EXECUTE_IF(“after_b”, {my_sleep(5)})

B

b

DEBUG_EXECUTE_IF (“after_B”, {my_sleep(7)})

2) Session设置

这里为了让后台线程也起作用,可以设置全局debug变量,若只想对本session起作用,可以设置会话debug变量。设置后,执行到对应的操作点时,则会额外执行DEBUG_EXECUTE_IF里面的代码。通过sleep方式来同步是一种取巧的方式,在实际情况中,需要不断调整sleep的时间,尽可能保证能按照预设的顺序执行。设置如下:红色部分是加的代码。

set global debug=”+d,before_a, after_A, after_B, after_b”;

实践

对于我们这个案例而言,为了得到指定的IBUF序列(IBUF_OP_DELETE_MARK,IBUF_OP_INSERT和IBUF_OP_DELETE),首先保证操作页面不出现在buffer_pool中;其次,执行INSERT操作产生的IBUF_OP_INSERT必需先于PURGE执行,最后,在整个过程中页面不能被MERGE,否则也将功亏一篑。对于第一点,可以通过将buffer_pool设置地比较小,然后扫描一个大表达到清理buffer_pool的目的;对于第二点,由于执行IBUF缓存的函数是IBUF_INSERT,针对类型为IBUF_OP_DELETE,强制其等待;对于第三点,在执行merge ibuf的地方,也强制其等待,并且比PURGE等待的时间要更长。具体设置点如下:

/*storage/innobase/ibuf/ibuf0ibuf.cc: ibuf_insert*/
ibuf_insert
{
DBUG_EXECUTE_IF("sleep_ibuf_insert",{
if (op == IBUF_OP_DELETE)
  my_sleep(3000000);
}); ......
 ibuf_insert_low();
} /* storage/innobase/ibuf/ibuf0ibuf.cc:ibuf_merge_pages*/
ibuf_merge_pages
{
......
  DBUG_EXECUTE_IF("sleep_ibuf_merge",{my_sleep(5000000);});
 
buf_read_ibuf_merge_pages(sync, space_ids, space_versions, page_nos, *n_pages);//merge操作
}

说明:为了使用debug_sync功能,编译mysqld时需要开启-DENABLE_DEBUG_SYNC=1 ,启动mysqld时,需要带启动选项--debug-sync-timeout

总结

在整个重现bug的过程中,其实还有很多细节,因为代码中总是会有很多if else的判断,以及特定标记位导致的特殊操作,因此打日志和debug很重要,通过打日志,了解大体执行流程;通过debug来了解细节,从而能掌握关键路径,在关键路径上设置合适的同步点,则能在并发环境下得到想要的执行序列。

参考文档

https://dev.mysql.com/doc/internals/en/debug-sync-facility.html

http://www.gpfeng.com/?p=461

http://hedengcheng.com/?p=238

http://www.cnblogs.com/cchust/p/4544518.html

UNABLE TO PURGE A RECORD(二)的更多相关文章

  1. Bug #19528825 "UNABLE TO PURGE A RECORD"

    概述: 在生产环境中,当开启insert buffer时(参数innodb_change_buffering=all),部分实例偶尔会出现“UNABLE TO PURGE A RECORD”错误.这个 ...

  2. mysql的TABLE_SCHEMA的sql和information_schema表, MySQL管理一些基础SQL语句, Changes in MySQL 5.7.2

    3.查看库表的最后mysql修改时间, 如果第一次新建的表可能还没有update_time,所以这里用了ifnull,当update_time为null时用create_time替代 select T ...

  3. oracle drop table and purge

    一.drop表 执行drop table xx 语句 drop后的表被放在回收站(user_recyclebin)里,而不是直接删除掉.这样,回收站里的表信息就可以被恢复,或彻底清除. 通过查询回收站 ...

  4. Python3 pip命令报错:Fatal error in launcher: Unable to create process using '"'

    Python3 pip命令报错:Fatal error in launcher: Unable to create process using '"' 一.问题 环境:win7 同时安装py ...

  5. unable to locate package

    一.问题 在ubuntu上安装npm时 sudo apt-get install npm 出现了错误: unable to lcoate package npm 二.解决办法 更新下apt就好了 su ...

  6. odoo10学习笔记二:继承(扩展)、模块数据

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11189252.html 一:继承 在不改变底层对象的时候添加新的功能——这是通过继承机制来实现的,作为在现有 ...

  7. MySQL5.6 GTID新特性实践

    MySQL5.6 GTID新特性实践 GTID简介 搭建 实验一:如果slave所需要事务对应的GTID在master上已经被purge了 实验二:忽略purged的部分,强行同步 本文将简单介绍基于 ...

  8. Ubuntu 中软件的安装、卸载以及查看的方法总结

    Ubuntu 中软件的安装.卸载以及查看的方法总结 博客分类: Linux UbuntuDebian配置管理CacheF#  说明:由于图形化界面方法(如Add/Remove... 和Synaptic ...

  9. [daily][device][bluetooth] 蓝牙怎么办!(archlinux下驱动蓝牙鼠标,以及三星手机)

    去年地摊买的破无线鼠标坏掉了.看上微软的Designer Mouse蓝牙鼠,但是买之前我要确认我能不能驱起来. 这款鼠标只支持蓝牙4.0.系统支持windows8以上,不支持xp和windows7. ...

随机推荐

  1. iOS阶段学习第15天笔记(NSDate操作)

    iOS学习(OC语言)知识点整理 一.OC关于NSDate类的操作实例代码 //默认直接显示对象,显示的是格林威治时间 //获取当前日期时间的实例方法 NSDate *date1=[[NSDate a ...

  2. CSRF 攻击介绍

    CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/ ...

  3. C#-INotifyPropertyChanged(解决数据绑定的界面刷新问题)

    最近做项目用到DataGridView,用它绑定数据源后,如果数据源中的数据修改无法及时刷新到控件上,必须切换单元格的焦点才能导致刷新显示新数值,通过查官方文档,用INotifyPropertyCha ...

  4. .net项目引用C++ 动态链接库.dll

    项目开发时可能需要调用其动态链接库,如C++动态链接库,实现相应功能,那么在C#里怎么调用C++库里的方法呢,如下提供两种方式. 方式一:建立一个C++项目,选择dll动态链接库如下. //stdaf ...

  5. 似乎都设置了utf-8,为什么出现乱码

    解决方法如下: ****************************************************************************************[来自我 ...

  6. mysql ,为什么一张表的varchar关联另一张表varchar执行失败的一个原因

    CONSTRAINT `orderdts_ibfk_2` FOREIGN KEY (`address`) REFERENCES `test列` (`address`) 很可能的原因是另一张表的test ...

  7. 展示 Popup 的使用方法

    源码下载:[原创]展示Popup的使用方法.zip

  8. ios源码-ios游戏源码-ios源码下载

    游戏源码   一款休闲类的音乐小游戏源码 该源码实现了一款休闲类的音乐小游戏源码,该游戏的源码很简单,而且游戏的玩法也很容易学会,只要我们点击视图中的grid,就可以 人气:2943运行环境:/Xco ...

  9. Hack语言特性之类型化

    Hack最基础的特性就是类型标注.PHP5已经开始支持对象的类型化,PHP7也提供了标量类型化声明.Hack提供了全面的类型标注支持,与其typecher配合使用,还可以实现快速.前置静态类型验证. ...

  10. 【Qt】2.3 使用Qt设计师来创建对话框

    安装完Qt OpenSource之后,在开始菜单目录下会有这几个东西. 其中[Designer]是用来设计窗口界面的程序.所以现在可以使用它来设计一个对话框.在[Qt Creator]中,[设计]这一 ...