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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

本节摘取了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的page数为0,并没有检查purge线程的状态是否被设置为PURGE_STATE_STOP; 很显然,如果当前的history list非常长,那么可能需要等待purge完成后,才能退出循环,而在用户看来,就好像hang了很久一样。推长history list 很容易:开启一个打开read view的事务(例如RR级别下执行一个SELECT)不做提交,同时有并发的DML,跑一段时间history list就上去了。


解决

在函数srv_do_purge函数的while退出条件中加上purge线程状态判断,如果被设置为PURGE_STATE_STOP,就退出循环。

补丁


问题二

在执行InnoDB crash recovery阶段,如果发现不合法的大字段,就会去调用函数ib_warn_row_too_big 去打印一条warning,函数为push_warning_printf。然而这个函数的目的是给客户端返回一条warning,而这时候系统还在崩溃恢复阶段,并没有合法的thd对象,因此造成系统崩溃。

Tips:这个bug是在升级到新版本5.6出现的,最根本的原因是5.6新版本对大字段长度做的约束。早期版本5.6及之前的版本,我们可以定义非常大的blob字段,但如果字段太长,对这些字段的修改,可能导致redo log的checkpoint点被覆盖,因为计算redo log 空间是否足够,并没有依赖即将插入的redo 记录长度,而仅仅是保留一定的比例。因此在5.6.22版本中做了限制:如果blob的长度超过innodb_log_file_size * innodb_log_files_in_group的十分之一时,就会更新失败,给用户返回DB_TOO_BIG_RECORD的错误码。这个问题在5.7版本里被彻底解决:每写4个blob外部存储页,检查一次redo log空间是否足够,如果不够用,就推进checkpoint点。

解决

在函数ib_warn_row_too_big中判断当前线程thd是否被初始化,如果为NULL,直接返回,不调用push_warning_printf。

补丁


问题三

当我们通过alter语句修改一个被外键约束的列名时,由于没有从数据词典cache中将包含老列名的cache项驱逐掉,导致重载外键约束时失败。

举个简单的例子:

 
 
 
 
 
 

可以看到,尽管t1表的a列已经被rename成 id,但打印出来的信息也并没有更正。

解决

当被外键约束的列名被修改时,将对应的外键项从数据词典cache中驱逐,当其被随后重新加载时就会使用新的对象。

补丁


问题四

如上文所提到的,在新版本InnoDB中,对blob字段的数据操作需要保证其不超过总的redo log file大小的十分之一,但是返回的错误码DB_TOO_BIG_RECORD及打印的信息太容易让人误解,大概如下:

 

解决

输出更合适、更直观的错误信息,如下:

 

补丁


问题五

FLUSH TABLE操作在某些情况下可能导致实例crash。 例如如下执行序列:

 

当执行FLUSH TABLE时,在重载表cache时,InnoDB层会针对每个表设置其状态(ha_innobase::store_lock)。如果执行FLUSH 操作,并且加的是读锁时,就会调用函数row_quiesce_set_state将table->quiesce设置为QUIESCE_START。在上例中,表t1的两个表名表均加读锁,造成重复设置状态为QUIESCE_START,导致断言失败。

Tips:在5.6版本中,虽然有明确的FLUSH TABLE..FOR EXPORT命令来协助转储ibd文件。但实际上,简单的FLUSH TABLE操作默认就会产生一个tbname.cfg的配置文件,拷贝该文件和ibd,可以将数据转移到其他实例上。table->quiesce用于标识操作状态,例如,如果标识为QUIESCE_START,就会在函数ha_innobase::external_lock中调用row_quiesce_table_start来启动配置文件的生成。

解决

移除断言

补丁


问题六

线上实例错误日志中偶尔出现 “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这种类型的change buffer时,发现记录并没有被标记删除,就会导致错误日志报错。

显然上述的操作序列是不合理的,正确的序列应该是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中,而是直接去尝试读取物理页。

补丁


问题七

对于非windows系统的平台上,函数os_file_pwrite和os_file_pread在碰到io错误时返回-1,并错误的作为写入/读取的字节数写在错误日志中。

解决

单独记录失败的系统调用日志,打印更可读的日志信息。

补丁


问题八

在崩溃恢复后立刻执行一次slow shutdown (innodb_fast_shutdown = 0) 可能产生断言失败crash。原因是当完成crash recovery后,对于需要回滚的事务,会起单独的线程来执行,这时候如果shutdown实例,会触发触发purge线程内部断言失败:ut_a(n_pages_purged == 0 || srv_fast_shutdown != 0);

解决

等待trx_rollback_or_clean_all_recovered完成后,再进行slow shutdown

补丁


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
存储 关系型数据库 MySQL
MySQL InnoDB数据存储结构
MySQL InnoDB数据存储结构
|
1月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
803 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
2月前
|
存储 缓存 关系型数据库
MySQL - 存储引擎MyISAM和Innodb
MySQL - 存储引擎MyISAM和Innodb
|
5天前
|
存储 关系型数据库 MySQL
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
14 0
|
2月前
|
SQL 存储 关系型数据库
Mysql内核查询成本计算
Mysql内核查询成本计算
|
2月前
|
存储 SQL 关系型数据库
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
72 0
|
3月前
|
存储 算法 关系型数据库
MySQL相关(八)- innodb行级锁深入剖析
MySQL相关(八)- innodb行级锁深入剖析
42 0
|
3月前
|
存储 算法 关系型数据库
MySQL相关(七)- innodb 锁的介绍及使用
MySQL相关(七)- innodb 锁的介绍及使用
27 0
|
3月前
|
存储 关系型数据库 MySQL
MySQL相关(番外篇)- innodb 逻辑存储结构
MySQL相关(番外篇)- innodb 逻辑存储结构
32 0
|
3月前
|
存储 关系型数据库 MySQL
MySQL存储引擎 InnoDB、MyISAM、Memory存储引擎的特点与区别
MySQL存储引擎 InnoDB、MyISAM、Memory存储引擎的特点与区别
54 0

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多