MySQL · 捉虫动态 · 备库1206错误问题说明

问题背景

一个用户自建MySQL,出现备库复制中断的问题,报错为slave sql thread 错误,The total number of locks exceeds the lock table size。

报错代码

这个报错在代码中的抛错逻辑为:

if UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 4

文字解释是:如果buffer pool中的空闲页面和LRU页面总和少于buffer pool 大小的1/4,则认为内存不够用,报错。

那么问题来了

  1. buffer pool 哪里去了
    buffer pool是InnoDB内部管理内存的统一结构。默认每个page 16k。初始化后,每个page都是空闲状态,放在free中。
    当读取数据等需要用到页面数据的操作时,将数据从磁盘读取到内存中,用的就是buffer pool的page。为了支持淘汰机制,InnoDB内部维护了一个淘汰链表,就是LRU list。装了数据的page被从free list移到LRU list。
    但是,除了正常的读取数据,还有其他的逻辑需要从buffer pool中“抢”资源。比如本例中是因为undo page。
    事务越大,需要的undo page越多,在整个事务未提交前,undo page是必须强占内存的。这就可能导致一种情况:事务过大,导致buffer pool全部被用光,无法提供正常服务。
    因此InnoDB有了上面的保护机制。触发这个上限后报错后,事务会回滚,释放undo page。
  2. 为什么主库执行成功备库失败了
    从上面的分析和代码中可以看到,判断内存是否占用过多,设置的上限是buffer_pool size的1/4.
    另外,5.6以后支持了设置多个 innodb_buffer_pool_instances,也就是分成多个pool, 在现在的逻辑中,认为只要“任意一个pool满足上述超过1/4的条件”,都判定为内存消耗过限。
    因此主要排查参数:

    • 备库的 innodb_buffer_pool_size 是否小于主库值
    • 若主备的innodb_buffer_pool_size值相同,备库的 innodb_buffer_pool_instances 值是否更大。
  3. 作为验证
    DBA在发现备库apply error的时候第一步往往是用 mysqlbinlog 工具去看导致错误的event是什么。这时候会发现其实是一个批量的load数据,或者update/delete大事务导致。

小结

  1. buffer pool不仅用于缓存page,会有其他数据结构争抢;
  2. 主备的参数尽量保持一致;
  3. 尽量避免超大事务,即使不考虑备库apply error。这种超大事务在主库执行,由于undo page占用buffer pool,可能会导致buffer pool命中率突然下降,影响业务。
时间: 2024-12-31 21:00:48

MySQL · 捉虫动态 · 备库1206错误问题说明的相关文章

MySQL · 捉虫动态 · ALTER IGNORE TABLE导致主备不一致

背景 我们知道当一张表的某个字段存在重复值时,这个字段没办法直接加UNIQUE KEY,但是MySQL提供了一个 ALTER IGNORE TABLE的方式,可以忽略修改表结构过程中出现的错误,但是要忽略UNIQUE重复值,就需要打开old_alter_table,也就是拷贝表的方式来ALTER TABLE. 例如这样: CREATE TABLE t1(c1 int) ENGINE = InnoDB; INSERT INTO t1 VALUES (1), (1); SET old_alter_t

MySQL内核月报 2014.10-MySQL· 捉虫动态·从库OOM

bug背景 官方最近发布的版本(5.7.5)修复了这样一个bug,主备复制场景下,如果主库和备库对应的表结构中有数据类型不一致,并且主库的 binlog 是 row 格式的,这时候如果主库对不一致的表做了一个大事务更新,备库在应用 relay-log 的时候报OOM(Out of Memory).bug地址在这里,主备数据类型不一致主要发生在这2种情况下: 主备库版本不一致,不同版本之间的数据类型可能存在不一致.用户在报这个bug时,就是在5.5到5.6的复制场景下,用到了时间类型,时间类型在5

MySQL · 捉虫动态·DROP DATABASE外键约束的GTID BUG

背景 MySQL的DDL没有被设计成事务操作,因此DDL操作是无法回滚的(像PgSQL把DDL也设计成事务操作,DDL就可以在执行成功后被回滚操作取消).这就会导致如果某个DDL语句内部被拆分为多个原子的DDL调用,那么这个DDL语句就不具备中途执行失败后回滚整个DDL语句的能力,也就是说,即使语句逻辑内的某个原子DDL调用失败了,也无法回滚已经完成的那些原子DDL调用. 问题描述 DROP DATABASE 就是一个例子,对于MySQL而言,DROP DATABASE 并非是一个原子DDL操作

MySQL · 捉虫动态 · 并行复制外键约束问题二

背景 并行复制可以大大提高备库的 binlog 应用速度,内核月报也多次对并行复制特性进行介绍,感兴趣的朋友可以回顾下:5.6 并行复制实现分析.5.6 并行复制恢复实现 和 5.6并行复制事件分发机制. 在早期的内核月报,有一篇 并行复制外建约束问题,介绍阿里在 5.5 版本中自己实现并行复制时遇到的外键约束问题,本文接着前作继续介绍并行复制外键约束问题,这次场景不一样,并且目前官方 5.6 最新版本(5.6.30)中也有这个问题. 问题描述 一般情况的复制是 A->B 这样一主一备,本文要描

MySQL · 捉虫动态 · MySQL 外键异常分析

外键约束异常现象 如下测例中,没有违反引用约束的插入失败. create database `a-b`; use `a-b`; SET FOREIGN_KEY_CHECKS=0; create table t1(c1 int primary key, c2 int) engine=innodb; create table t2(c1 int primary key, c2 int) engine=innodb; alter table t2 add foreign key(c2) referen

MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题

背景 官方 5.6 最新版本 5.6.24 有这样一个bugfix,当使用 GTID 协议进行复制,并且备库的 slave_net_timeout 值设置太小的话,备库的 slave io 线程会卡住,同时主库上的 binlog dump 线程数一直在涨,官方的bug地址 . bug分析 首先说明下几个概念: 1. slave_net_timeout,这个变量控制备库 IO 线程的连接超时,如果IO线程在指定时间内没有从主库收到数据的话,就断开重连,默认值是3600秒: 2. heartbeat

MySQL · 捉虫动态 · Relay log 中 GTID group 完整性检测

bug背景 官方 5.7.6 版本对 gtid 有非常多的改进和bugfix,其中有一个 bugfix 是针对 relay log 中没有接收完整的 gtid 事务的.正常的relay log 中的 gtid 事务应该是像下面这样: 1. gtid event 2. query event (begin) 3. row event (write/update/delete) 4. query event (commit) 上面这 4 个 event 序列构成一个 group.因为 IO 线程从主

MySQL · 捉虫动态 · order by limit 造成优化器选择索引错误

问题描述 bug 触发条件如下: 优化器先选择了 where 条件中字段的索引,该索引过滤性较好: SQL 中必须有 order by limit 从而引导优化器尝试使用 order by 字段上的索引进行优化,最终因代价问题没有成功. 复现case 表结构 create table t1( id int auto_increment primary key, a int, b int, c int, key iabc (a, b, c), key ic (c) ) engine = innod

MySQL · 捉虫动态 · ORDER/GROUP BY 导致 mysqld crash

问题描述 表结构如下所示: show create table test\G Table: test Create Table: CREATE TABLE `test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id2` varchar(50) DEFAULT NULL `id3` varchar(100) DEFAULT NULL `some_text` varchar(200) DEFAULT NULL `name` varchar(