数据库内核月报 - 2016 / 07

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命中率突然下降,影响业务。