概述:

在生产环境中,当开启insert buffer时(参数innodb_change_buffering=all),部分实例偶尔会出现“UNABLE TO PURGE A RECORD”错误。这个其实是一个老bug,从官方bug系统来看,也有其它用户遇到过类似的问题,并且官方在今年2月份已经对该bug进行了修复。

insert buffer:

在描述问题的产生原因和解决方案之前,首先来简单说说insert buffer。简单来说,insert buffer是二级索引操作的一个缓存。在进行二级索引操作时,若请求的page不在buffer pool中,则将二级索引操作进行缓存,待下次读取该page时,将page与buffer中的操作合并,通过这种方式,可以减少在更新(INSERT,DELETE,UPDATE等)操作时磁盘的随机读,提高更新的响应时间。

最早insert buffer仅支持insert 操作的buffer,所以这种buffer叫insert buffer,到mysql5.6时,不仅支持insert操作的buffer操作,还可以支持update,delete,purge等操作的buffer功能,而insert buffer也改称changing buffer,后面我简称ibuf。通过参数innodb_change_buffering设置,用户可以灵活控制二级索引操作的buffer开启与否。ibuf仅支持普通二级索引,对于唯一索引和主键索引不起作用。我们知道,innodb表是索引组织表,每个表由一个聚簇索引和若干个二级索引组成。若使用ibuf,则对于每个二级索引的更新操作,都会对应产生一个ibuf操作符,下表列出了ibuf操作符与更新语句的对应关系:

操作

Insert buffer操作符

说明

Insert

IBUF_OP_INSERT

Delete

IBUF_OP_DELETE_MARK

Delete语句实际是对删除记录打标

Purge

IBUF_OP_DELETE

Purge真正物理上清理打删除标的记录

Update

IBUF_OP_DELETE_MARK

IBUF_OP_INSERT

二级索引的update由Delete+Insert组成。

purge:

上节提到的purge其实并非用户发起的更新语句,而是innodb存储引擎实现MVCC(多版本并发控制)时,需要的一个后台清理操作。Innodb的多版本实现机制(利用回滚段保存历史版本信息,并通过记录中的ROLLPTR与回滚段记录进行关联)在删除或更新记录时,并非真正物理删除,而仅仅是在记录上打上删除标记,通过后台线程进行清理,这个过程就是purge过程。当然,purge还有另外一个作用是清理回滚段,回收空间,以便空间可以重复利用。有关purge和mvcc的实现,有机会在单独整理一篇文章。

问题产生的原因:

回到问题本身,我们通过一个简单的场景来复现问题。假设存在表t(id int, c1 varchar(100),primary key(id),key(c1)),表中包含一条记录(1,a);并且假设表t的page都不在buffer pool中,以便可以使用ibuf。通过下表的操作序列,可以复现问题。

操作序列

更新语句

Ibuf操作

1【DELETE】

delete from t where id=1;

 

IBUF_OP_DELETE_MARK

2【INSERT】

insert into t values(1,’a’);

 

IBUF_OP_INSERT

3【PURGE】

IBUF_OP_DELETE

在读取page的过程中,会进行合并操作,合并操作的顺序是以对应page在ibuf中的操作顺序来进行的,在执行到第3步:IBUF_OP_DELETE时,发现待删除的记录并没有打删除标记(第2步插入了相同主键+二级索引的记录,将删除标记清理),认为异常,导致抛错。由于purge操作是由单独的线程在后台执行,因此执行更新语句的操作与purge操作并没有严格的先后顺序,如果上述操作的顺序变为1->3->2则不会复现问题。

重要流程:

从上节分析来看,产生问题的主要原因是purge操作和更新操作没有严格的同步,导致purge可能清理到未打删除标记的记录。

purge流程:

  1. 读取解析undo记录,TRX_UNDO_DEL_MARK_REC类型的回滚记录执行purge
  2. 调用函数row_purge_del_mark
  3. 若表含有二级索引,先purge二级索引(row_purge_remove_sec_if_poss)
  4. 判断purge二级索引是否可以缓存,确定是否执行删除物理记录动作
  5. purge主键索引(row_purge_remove_clust_if_poss)

函数调用关系:

srv_do_purge->trx_purge->que_thr_step->row_purge_step
->row_purge->row_purge_record_func->row_purge_del_mark

->row_purge_remove_sec_if_poss->
row_purge_remove_sec_if_poss_leaf

purge二级索引流程:

  1. 读取page,使用ibuf_should_try函数判断是否可以使用ibuf
  2. 根据返回结果确定读取page的方式,如果是purge,则方式设置为:BUF_GET_IF_IN_POOL_OR_WATCH
  3. 若page不在buffer-pool中,设置watch标记,表示有purge请求
  4. 调用ibuf_insert函数进行ibuf缓存操作,并设置标记为:BTR_CUR_DELETE_IBUF
  5. 根据返回结果,row_purge_del_mark确定是否真正执行删除动作
  6. 结束。

判断是否使用ibuf调用关系:

row_purge_remove_sec_if_poss_leaf->row_search_index_entry->btr_pcur_open_func->btr_cur_search_to_nth_level->ibuf_should_try

更新操作与purge操作在ibuf中的协同:

purge线程获取page时,若page不在buffer中,将page设置watch标记,然后执行ibuf_insert将purge操作缓存。更新操作(insert,delete,update等)也调用ibuf_insert操作进行buffer,首先会判断page是否有watch标记,若存在,则认为可能 与purge动作冲突,不能使用ibuf。此时,会去从磁盘读取page,在读取page过程中会将purge操作进行合并,后续进行更新操作则不会存在问题。通过watch标记来达到更新操作和purge操作协同使用ibuf的目的,避免上述提到的问题。

到这里,大家可能会有一个疑问,假设不使用ibuf,正常的更新和purge操作同样是在不同的线程,也有可能出现(DELETE,INSERT,PURGE)序列,为啥就没有问题呢?因为在purge二级索引时,还会调用row_purge_poss_sec函数,确认记录是否可以purge(二级索引记录对应的聚集索引没有delete mark或者trx_id比purge view还新时,不能purge),从而避免上述问题。

解决方法:

同样在purge二级索引过程中,btr_cur_search_to_nth_level,首先调用buf_page_get_gen函数进行watch设置,然后调用row_purge_poss_sec判断记录是否可以purge,若用户已经re-insert,则此时purge动作忽略;否则,表示还没有insert记录进来,继续执行调用ibuf_insert接口进行缓存操作。另一方面,更新操作,insert在使用ibuf时,会判断是否有watch标记,但程序逻辑在返回时,将标记丢了,导致出现问题。因此只要保证更新操作真正使用ibuf前,检查没有purge同时使用ibuf,则可以避免问题发生。

详细解法可以参考:

https://github.com/mysql/mysql-server/commit/ec369cb4f363161dfbbbd662b20763b54808b7d1

参考文档:

http://mysqllover.com/?p=1264

https://bugs.mysql.com/bug.php?id=73767

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

http://mysql.taobao.org/monthly/2015/04/01/

Bug #19528825 "UNABLE TO PURGE A RECORD"的更多相关文章

  1. UNABLE TO PURGE A RECORD(二)

    上一篇文章说明了bug出现的原因和原理分析,要修复bug似乎已经水到渠成了,但远没有这么简单,只因为“并发”.要修复问题,首先要做的第一件事情是稳定的复现问题.由于数据库系统是一个并发系统,并且这个b ...

  2. ASP.Net Core 2.2 InProcess托管的Bug:unable to open database file

    最近把项目更新到了ASP.Net Core 2.2,发布之后发现在IIS下使用SQLite数据库不行了,报异常说不能打开数据库."unable to open database file&q ...

  3. [java] bug经验 Unable to locate Spring NamespaceHandler for XML schema namespace解决办法

    报错关键字: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration probl ...

  4. 【bug】Unable to execute dex: Multiple dex files define

    This is a build path issue. Make sure your bin folder is not included in your build path. Right clic ...

  5. 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 ...

  6. FNDCPASS Troubleshooting Guide For Login and Changing Applications Passwords

    In this Document   Goal   Solution   1. Error Starting Application Services After Changing APPS Pass ...

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

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

  8. Excessive AWR Growth From Partitioned Objects Such as SYS.WRH$_EVENT_HISTOGRAM Causing Sysaux to Grow

    AWR数据增长较快,导致sysaux表空间使用较高 SQL> select f.tablespace_name, 2 a.total, 3 f.free, 4 round((f.free / a ...

  9. MySQL · 引擎特性 · InnoDB 事务子系统介绍

    http://mysql.taobao.org/monthly/2015/12/01/ 前言 在前面几期关于 InnoDB Redo 和 Undo 实现的铺垫后,本节我们从上层的角度来阐述 InnoD ...

随机推荐

  1. .net批量上傳Csv檔資料應用程序開發總結

    應用環境:visual studio 2010開發工具,Database為Sql2008以上版本 最近在生產環境中需要開發一款應用程式,上傳電子檔(.csv)資料至Database 最初方案: 以tx ...

  2. 疯狂Android讲义 - 学习笔记(一)

    常用开发工具的用法 android : 启动Android SDK管理器 android --help  查看具体用法 android create avd -n t10 -t 10 -b armea ...

  3. 第 15 章 CSS 文本样式[上]

    学习要点: 1.字体总汇 2.字体设置 3.Web 字体 主讲教师:李炎恢 本章主要探讨 HTML5 中 CSS 文本样式,通过文本样式的设置,更改字体的大小.样式以及文本的方位. 一.字体总汇 本节 ...

  4. HDU 5792---2016暑假多校联合---World is Exploding

    2016暑假多校联合---World is Exploding Problem Description Given a sequence A with length n,count how many ...

  5. Alfresco 4 项目介绍

    body{ font: 16px/1.5em 微软雅黑,arial,verdana,helvetica,sans-serif; }       Alfresco 是一个开源的企业内容管理系统(ECM) ...

  6. asp.net web常用控件FileUpload(文件上传控件)

    FileUpload控件的主要中能:向指定目录上传文件,该控件包括一个文本框和一个浏览按钮. 常用的属性:FileBytes,FileContent.FileName.HasFile.PostedFi ...

  7. jQuery Portamento 滑动定位

    版本: jQuery v1.3.2+ jQuery Portamento v1.1.1 注意事项: 在不支持position:fixed的浏览器里效果不是很好(例如 IE6.iOS4),可以在参数中设 ...

  8. Infinite Scroll - jQuery & WP 无限滚动插件

    无限滚动(Infinite Scroll)也称为自动分页.滚动分页和无限分页.常用在图片.文章或其它列表形式的网页中,用来在滚动网页的时候自动加载下一页的内容.Infinite Scroll  这款  ...

  9. 赞!带进度条的 jQuery 文件拖放上传插件

    jQuery File Uploader 是一个 jQuery 文件拖放上传插件,包括 Ajax 上传和进度条效果.作者编写这个插件的想法是要保持它非常简单,不像其他的插件,很多的标记,并提供一些 H ...

  10. Muzli – 所有你需要的设计灵感都在这

    曾几何时,三个家伙一起工作,他们分享各种令人振奋的资源链接.然后,其中一个人有一个想法.一个绝妙的主意,是否可以有一种方式来与他人分享这些链接?因此, Muzli 诞生了.所有你需要的设计灵感这里都有 ...