电脑坏了mysql怎么办 电脑数据库损坏了怎么修复( 三 )


全局变量,只读变量,默认为OFF 。
三、问题修复
这类MySQL用户表损耗的问题解决方式也是有多种,我这里列举其中一种:
(1)my.cnf中的[mysqld]标签下添加skip_grant_tables,启动时跳过加载系统字典 。
(2)重启mysqld , 然后修复mysql schema下的所有表 。
(3)在[mysqld]标签下注释或删除掉skip_grant_tables,然后重启mysqld 。
此时mysqld是可以正常启动的,无异常 。
四、深入排查
在产品化中,以上修复方式很不优雅,只是作为临时的解决方案;并且也存在一些令人疑惑的点:
带着这些疑问 , 我们继续排查出现该现象的原因;此时Google也没有找到一些有效的信息,那么只能通过MySQL源代码来寻找一些答案 。
首先需要下载mysql 5.7.31版本的源代码,并搭建mysql debug环境;具体步骤可以自动Google搜索一下,本文就不再赘述了 。
在源代码中搜索一下关键词,用于打断点的位置 , 然后进行调试:
定位到相关代码,大概是sql/mysqld.cc的4958行 , 且存在if条件判断 , 此时我们开始调试:
通过以上调试信息,可以判断出acl_init函数返回的值为真;此时我们查看该函数的代码 (sql/auth/sql_auth_cache.cc:1365):
根据该函数的注释发现:该函数是初始化负责用户/数据库级特权检查的结构,并从mysql schema中的表中为其加载特权信息;且return值为1代表的是初始化权限失败 。
此后开始逐步调试,观察return相关信息,当调试到lock_table_names函数时,我们发现在Phase 3时return值为true,且根据代码注释发现true代表是Failure;具体代码如下(sql/sql_base.cc:5549):
调试信息如下:
可以看到flags的值为0,而MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK为宏定义值0x1000,与flags的值 做按位与操作 , 结果自然也是0,当然MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY也是如此;need_global_read_lock_protection是bool类型值,代表是否需要全局读锁的保护,这个值是在table- mdl_request.type不为MDL_SHARED_READ_ONLY发生改变;check_readonly函数相关信息 下面概述 。
此时也查看了下MySQL 5.7.26版本代码作为对比 , 发现lock_table_names函数下的Phase 3后的部分代 码是在5.7.29版本后新增的 。如果是git clone的MySQL代码可以用git blame命令查询文件变化的信息:
上述展示的信息中,最左侧的列值为commit id为05824063和0405ebee,有兴趣的同学可以详细看下 。
此功能解决的问题是BUG#28438114: SET READ_ONLY=1 SOMETIMES DOESN'T BLOCK CONCURRENTDDL.;当然这个代码的变更功能也在5.7 Release Notes中有所体现,如下所示(m/doc/relnotes/mysql/5.7/en/news-5-7-29.html):
最后我们再查看下check_readonly函数,该函数是基于read_only和super_read_only状态执行标准化检查,是禁止(TRUE)还是允许(FALSE)操作 。代码如下(sql/auth/sql_authorization.cc:489):
此时第一反应就是去检查my.cnf中是否包含read_only相关参数,检查之后发现确实是使用了该参数 ,  如下:
此时注释掉该参数,然后再次启动mysqld,发现MyISAM表可以自动修复 , 且正常启动;error log信息如下:
由于docker一些限制,我们在mysqld启动会涉及两次;所以解决该问题的方式为:第一次mysqld的启动时先关闭read_only参数,第二次启动时开启read_only参数 。之所以选择默认开启read_only参数,是为了避免在mysqld启动后,选主逻辑未完成时的保护措施;当然选主完成后,会自动对master执行 set global read_only=0 操作 。
五、总结
六、附录
调试的栈帧信息如下,有兴趣的小伙伴可以研究下:
熟悉MySQL体系结构和innodb存储引擎工作原理;以及MySQL备份恢复、复制、数据迁移等技术;专注于MySQL、MariaDB开源数据库,喜好开源技术 。

推荐阅读