MySQL5.6.23: fix “UNABLE TO PURGE A RECORD”

本文简述下之前我们线上频繁碰到的“UNABLE TO PURGE A RECORD”的原因

###################################################

线上实例错误日志中偶尔出现 “UNABLE TO PURGE A RECORD”,从官方bug系统来看,很多用户都遇到了类似的问题。

当change buffer模块以如下序列来缓存索引操作时:

  1. 记录被标记删除(IBUF_OP_DELETE_MARK)
  2. 随后插入相同记录–IBUF_OP_INSERT
  3. Purge线程需要物理删除二级索引记录,操作被buffer–IBUF_OP_DELETE

 

当读入物理页时,需要进行ibuf merge,当执行到IBUF_OP_DELETE时,发现记录并没有被标记删除,导致错误日志报错。

 

显然上述的操作序列是不合理的,正确的序列应该是IBUF_OP_DELETE_MARK,IBUF_OP_DELETE,IBUF_OP_INSERT。

 

为了理清逻辑,我们简单的理一下相关代码

 

注意IBUF_OP_DELETE是由第一步的标记删除操作触发,Purge线程发起;在每个buffer pool的控制结构体中,有一个成员buf_pool->watch[BUF_POOL_WATCH_SIZE],BUF_POOL_WATCH_SIZE的值为purge线程个数,用于辅助Purge操作。

 

假定内存中没有对应的Page,Purge线程会做如下几件事儿:

  • 首先查询buffer pool,看看page是否已经读入内存;

如果不在内存中,则将page no等信息存储到watch数组中,并插入page hash(buf_pool_watch_set)。如果随后page被读入内存,就会删除watch标记。

  • 判断该二级索引记录是否可以被Purge(row_purge_poss_sec,当该二级索引记录对应的聚集索引记录没有delete mark并且其trx id比当前的purge view还旧时,不可以做Purge操作)

 

  • 随后,再插入IBUF_OP_DELETE类型的ibuf记录时,还会double check下该page是否被设为sentinel (ibuf_insert_low,buf_pool_watch_occurred),如果未被设置,表明已经page已经读入内存,就可以直接去做purge,而无需缓存了。

 

  • 对于普通的操作类型,例如IBUF_OP_INSERT和IBUF_OP_DELETE_MARK,同样也会double check page 是否读入了内存。在函数ibuf_insert中会调用buf_page_hash_get进行检查, 如果page被读入内存,则不缓存操作,如果请求的Page被设为sentinel,则从buf_page_hash_get返回NULL,因此随后判定需要缓存操作。这也正是问题的所在:
  1. 标记删除记录,写入IBUF_OP_DELETE_MARK
  2. Purge线程设置page对应的sentinel,完成合法性检查,准备调用ibuf_insert
  3. 插入相同记录,写入IBUF_OP_INSERT
  4. Purge线程写入IBUF_OP_DELETE

解决

如果记录所在的page被设置了一个sentinel,那么对该page的并发插入操作就不应该缓存到change buffer中,而是直接去尝试读取物理页。

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

 

时间: 2024-10-09 13:46:23

MySQL5.6.23: fix “UNABLE TO PURGE A RECORD”的相关文章

MySQL内核月报 2015.02-MySQL · 社区动态· 5.6.23 InnoDB相关Bugfix

本节摘取了MySQL5.6.23的几个和InnoDB相关的主要bugfix,简单阐述下问题及解决方案. 问题一 当执行FLUSH TABLE..FOR EXPORT命令时,会暂停purge线程的操作.这一步通过设置一个标记purge_sys->state的值为PURGE_STATE_STOP来告诉purge线程该停下来歇歇了. 然而如果Purge线程当前正在函数srv_do_purge中工作,该函数会执行一个while循环,退出条件是当前server shutdown,或者上次purge的pag

MySQL5.6基于GTID的主从复制_Mysql

MySQL 5.6 的新特性之一,是加入了全局事务 ID (GTID) 来强化数据库的主备一致性,故障恢复,以及容错能力. 什么是GTID? 官方文档:http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html在这篇文档里,我们可以知道全局事务 ID 的官方定义是:GTID = source_id:transaction_id MySQL 5.6 中,每一个 GTID 代表一个数据库事务.在上面的定义中,source_id 表示执行

MySQL: 并发replace into的死锁问题分析

测试版本:MySQL5.6.23测试表: create table t1 (a int auto_increment primary key, b int, c int, unique key (b));并发执行SQL: replace into t1(b,c) values (2,3)  //使用脚本,超过3个会话 背景 replace 操作可以算是比较常用的操作类型之一,我们先来理一下,对于上例,一条简单的replace into操作的主要流程包括哪些.   Step 1. 正常的插入逻辑

MySQL内核月报 2015.03-MySQL · 答疑释惑· 并发Replace into导致的死锁分析

测试版本:MySQL5.6.23 测试表: 背景 Replace into操作可以算是比较常用的操作类型之一,当我们不确定即将插入的记录是否存在唯一性冲突时,可以通过Replace into的方式让MySQL自动处理:当存在冲突时,会把旧记录替换成新的记录. 我们先来理一下一条简单的replace into操作(如上例所示)的主要流程包括哪些. Step 1. 正常的插入逻辑 首先插入聚集索引记录,在上例中a列为自增列,由于未显式指定自增值,每次Insert前都会生成一个不冲突的新值. 随后插入

Linux MySQL源码安装缺少ncurses-devel包

在Red Hat Enterprise Linux Server release 5.7 上用源码安装MySQL-5.6.23时,遇到了" remove CMakeCache.txt and rerun cmake.On Debian/Ubuntu, package name is libncurses5-dev, on Redhat and derivates it is ncurses-devel."错误,如下所示 [root@DB-Server mysql-5.6.23]# cm

MySQL中修改库名的操作教程_Mysql

MySQL在5.1引入了一个rename database操作,但在MySQL5.1.23后又不支持这个命令.可以说是一个实验性的功能,没有在生产中支持过(mysql-5.1 release在mysql-5.1.30),那么生产中我们有时为了追求完美需要改一下库名.怎么操作呢? 这里提供一个变通的方法. 1. 创建出新库名: mysql>create database db_v2; mysql>create database db_v2; 2.生成rename语句,从olddb里迁移,我这里o

MySQL 修改数据库名称的一个新奇方法_Mysql

MySQL在5.1引入了一个rename database操作,但在MySQL5.1.23后又不支持这个命令.可以说是一个实验性的功能,没有在生产中支持过(mysql-5.1 release在mysql-5.1.30),那么生产中我们有时为了追求完美需要改一下库名.怎么操作呢? 这里提供一个变通的方法. 1. 创建出新库名: 复制代码 代码如下: mysql>create database db_v2; 2.生成rename语句,从olddb里迁移,我这里olddb里sbtest; 复制代码 代

mysql sort buffer和join buffer学习笔记

相关参数查看命令 SELECT @@join_buffer_size; SELECT @@sort_buffer_size; ========================================================================================== join_buffer_size 当我们的join是ALL,index,rang或者Index_merge的时候使用的buffer. 实际上这种join被称为FULL JOIN. 实际上参与j

MySQL 多事务引擎XA

有那么一坨代码,他虽然在那里,我们却很少用到..那就是MySQL的多XA事务引擎特性支持..本周我们来探讨下TC LOG MMAP的代码实现.由于工作的关系,这块很少涉及,正好趁着周末补补漏.   本文分析的代码基于支持Tokudb的MySQL5.6.16 和MySQL 5.7.5:原因是官方MySQL还不包含多个事务引擎,因此代码压根走不到TC LOG MMAP. 但是MySQL5.7又Fix掉了相关的XA BUG (bug#47134),因此本文贴出的代码部分以MySQL5.7.5为主,调试