zoukankan      html  css  js  c++  java
  • 悲剧啊!Mysql的上古BUG!!!

    导读 这是MySQL8.0修复的上古bug之一,在2003年由Percona的CEO(当时应该还没Percona吧)提出的bug#199,光看这bug号就扑面而来一股上古时代的沧桑气息。

    悲剧啊!Mysql的上古BUG!!!悲剧啊!Mysql的上古BUG!!!
    问题的本质在于InnoDB初始化AUTO_INCREMENT的方式,在每次重启时,总是算出表上最大的自增值作为最大值,下一次分配从该值开始。这意味着如果在btree右侧叶节点大量删除记录,重启后,自增值可能被重用。这在很多场景下可能导致问题,包括但不限于:主备切换、历史数据迁移等场景。在bug#199下面一大堆的回复里,可以看到大量的同行抱怨。

    官方的修复就比较优雅了,不改变任何现有的存储,而是通过redo log来进行恢复。该补丁基于WL#7816的框架实现的,要想搞懂这个补丁,得先看看WL#7816做了哪些改动,为了解决这个问题,InnoDB使用一个引擎私有的系统表+特殊redo log的方式,在引擎内部自己解决corruption标记持久化的问题。其大概思路为:

    1. 当发现索引损坏时,写入一条redo log,但不更新数据词典
    2. 引入一个innodb引擎私有的系统表,称为DD Buffer Table,每次checkpoint之前会将索引corruption bit存入其中。
    3. 在崩溃恢复时,同时从redo log和DD Buffer Table中读取索引 corruption bit, 合并结果,并标记内存中的表和索引对象。
    初始化Persister

    目前Persister的类型仅有两种,一个用于corruption bit的持久化,一个用于自增列的持久化,对应的类为:

    Persister:
        |-- CorruptedIndexPersister
        |-- AutoIncPersister
    

    Persister对应全局对象dict_persist_t::persisters,可以通过类型persistent_type_t来找到对应的Persister,目前仅有PM_INDEX_CORRUPTED及PM_TABLE_AUTO_INC,但从注释来看,未来肯定会做更多的扩展,Persister在启动时调用函数dict_persist_init进行初始化。

    新的系统表

    新的系统表名为SYS_TABLE_INFO_BUFFER,对应管理类为DDTableBuffer,指针存储在dict_persist->table_buffer中。

    系统表包含两个列:TABLE_ID及BLOB类型的METADATA(ref DDTableBuffer::init),METADATA列包含了所有需要持久化的元数据。

    回写DDTableBuffer

    有几种情况会将内存修改回写到DDTableBuffer中:

    1. 在做checkpoint(log_checkpoint)之前,所有在dirty_dict_tables链表上的表对象,对应persist metadata都需要回写到DDTableBuffer中(dict_persist_to_dd_table_buffer)
    2. 从内存中驱逐一个表对象时(dict_table_remove_from_cache_low),如果需要的话也会去尝试回写。
    3. 在对包含自增列的表做DDL后,需要持久化counter,在如下函数中,会调用dict_table_set_and_persist_autoinc:
    ha_innobase::commit_inplace_alter_table
    create_table_info_t::initialize_autoinc()
    // for example: alter table..auto_increment = ??
    row_rename_table_for_mysql;
    // rename from temporary table to normal table
    

    回写的过程也比较简单(dict_table_persist_to_dd_table_buffer_low):

    1. 通过表对象初始化需要回写的Metadata数据: corrupt index及autoinc值(dict_init_dynamic_metadata)
    2. 构建记录值,插入DDTableBuffer系统表(DDTableBuffer::replace(), 如果记录存在的话,则进行悲观更新操作
    3. 表对象的diry_status修改成 METADATA_BUFFERED,表示有buffer的元数据
    Recovery and Startup

    在崩溃恢复时,当解析到日志MLOG_TABLE_DYNAMIC_META时(MetadataRecover::parseMetadataLog),会进行解析并将解析得到的数据存储到集合中(MetadataRecover::m_tables),如果存在相同table-id的项,就进行替换,确保总是最新的。

    在完成recovery后,搜集到的meta信息暂时存储到srv_dict_metadata中, 随后进行apply(srv_dict_recover_on_restart), apply的过程也比较简单,载入表对象,然后对表对象进行更新(MetadataRecover::apply),例如对于autoinc列,就总是选择更大的那个值。

    最后

    这个bug已经挂了相当长的时间,不排除把这个bug当作InnoDB的“特性”的同学,一定要注意到这个改动...

    本文转载自:http://www.linuxprobe.com/mysql-bug.html

    免费提供最新Linux技术教程书籍,为开源技术爱好者努力做得更多更好:http://www.linuxprobe.com/

  • 相关阅读:
    vue-router路由
    前端路由与后端路由
    getsupportfragmentmanager 没有这个方法
    Glide使用
    Android使用Glide加载Gif.解决Glide加载Gif非常慢问题
    电脑卡,eclipse Android stadio 卡,什么都卡解决方法
    Service IntentService区别 (面试)
    枚举
    Android stadio litepal
    Android 单元测试
  • 原文地址:https://www.cnblogs.com/Hdf-fight/p/5946085.html
Copyright © 2011-2022 走看看