带着问题读 TiDB 源码:Hive 元数据使用 TiDB 启动报错
《带着问题读源码系列》- 开篇
在 TiDB 社区活跃较久的伙伴们应该知道,过去我们有被称为 24 章经的《TiDB 源码阅读系列文章》,也有面向 TiKV 的《TiKV 源码解析系列文章》以及 《Deep Dive TiKV 系列文章》。这些系列文章的内容非常深入,能够帮助大家从非常细节的原理入手了解 TiDB 以及 TiKV 的实现方式和基础原理。
然而在 TiDB 社区中活跃的许多伙伴还需要更简单,并且同自己每天工作中使用 TiDB 时遇到的问题更相关的源码阅读文章。本文是《带着问题读源码系列》的第一次尝试,在定位并解决用户所遇到的一个简单问题的过程中,对相关的代码一并进行介绍。希望能够从不同的视角,以不同的问题颗粒度来帮助大家更好的学习 TiDB 和 TiKV 的源码。
AskTUG 上有许多用户日常使用 TiDB 过程中遇到的问题反馈,这些问题都能够成为同本文类似的源码解析素材。如果本文能够为大家创造价值,那么我们一定努力将《带着问题读源码系列》持续建设成同前辈们一样受大家欢迎的源码阅读系列。
问题
近期在 AskTUG 论坛接到用户反馈使用 TiDB 作为 Hive metastore 数据库时设置 SERIALIZABLE 事务隔离级别失败。并且用户根据文档建议进行 SET GLOBAL tidb_skip_isolation_level_check=1 操作后仍然无法按照预期解决问题。考虑到知乎在一年前就已正式上线并一直使用着 4.0.x 系列的 TiDB 作为 Hive metastore 的数据库,而用户按照说明文档操作仍然无法顺利在 TiDB 上部署 Hive metastore 意味着很可能 TiDB 在不同的版本间发生了不兼容的行为改变。接下来就让我们一起从问题的排查入手,学习了解相应功能背后的源代码。
验证流程
在 tiup 的帮助下我们能够非常轻松的启动多个不同版本的 TiDB 对事务隔离级别的行为进行测试和验证。
首先我们先启动 5.0.0 版本的 TiDB 集群准备测试
接下来我们使用 tiup 提示的连接命令使用 mysql client 连接上测试集群,在设置完 SET GLOBAL tidb_skip_isolation_level_check=1 之后使用 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 验证行为符合预期。说明 TiDB 5.0 系列的行为同 4.0 一致,能够支撑 Hive metastore 的运转。
接下来我们启动 5.1.0 版本的 TiDB 集群准备测试
同样我们使用 mysql client 连接上测试集群,在设置完 SET GLOBAL tidb_skip_isolation_level_check=1 并重建链接确保设置生效后,使用 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 仍然会收到错误报告。说明从 TiDB 5.1 系列开始行为同以往版本不一致,无法满足 Hive metastore 的要求。
问题分析
首先我们需要 checkout 一份最新 TiDB 代码(git hash: 649ed6abc9790cfdd2a17065118379d8abcc7595)查看事务隔离级别校验相关逻辑。为了快速定位到相关逻辑所在的代码,我们可以在 TiDB 代码的根目录下对字符串 SERIALIZABLE 进行文本检索,快速定位到可能与此有关的代码文件。
我们发现实际上包含字符串 SERIALIZABLE 的文件有两个,而其中对隔离级别进行判断进行处理的文件只有 sessionctx/variable/varsutil.go 这一个文件。打开文件后我们发现这里正是对隔离级别进行判断并根据 tidb_skip_isolation_level_check 设置决定是否通过的逻辑。
我们可以同行为符合预期的 5.0 版本 TiDB 代码(git hash: 53251a9731da02ad9ee5abed9f27a14c7dea33a4)进行对比来快速定位两者间行为不同是由那些变化引起的。同样我们通过字符串匹配快速定位到 sessionctx/variable/sysvar.go 和 sessionctx/variable/session.go 两个文件都存在对隔离级别进行条件处理的情况。
这两个不同的检查逻辑非常类似,都是试图获取 TiDBSkipIsolationLevelCheck 变量的设置,根据设定值决定是否予以放行。当我们将这里的逻辑同 master 代码中的逻辑进行对比时我们发现他们本质上的区别非常小。5.0 中使用了一个内置工具函数 GetSessionSystemVar 来获取变量值,而 master 代码则直接访问 SessionVars 的 systems 变量表进行访问来获取 TiDBSkipIsolationLevelCheck 变量的当前值。进一步查看 5.0 中 GetSessionSystemVar 的实现我们发现这个工具函数负责在 session 变量未设置时进一步到全局变量表中进行查找并将查找到的结果放置在 SessionVars 的 systems 变量表中供后续查找使用。
根据目前的线索猜测,在 5.1 某次代码重构试图将两个相似的重复隔离级别检查逻辑合并成一个通用逻辑的时候绕过了工具函数直接访问 systems 变量表。这种方式访问变量表不具备从前工具函数自动回退全局变量设定的能力。了解到这里修复非常简单,只需使用当前 TiDB 中类似工具函数 GetSessionOrGlobalSystemVar 来读取 TiDBSkipIsolationLevelCheck 的变量值就能恢复预期行为。
修复并完成构建后再次测试 TiDB 的行为已符合预期。
提交修复
根据 TiDB 社区标准的代码贡献流程,我们首先创建一个新的 Issue 对发现的问题、复现方式以及期望的行为做清晰的描述。
创建完 issue 后我们就可以将修复逻辑提交到自己 fork 的仓库并创建 PR,创建过程中需要根据实际情况填充 PR 信息模版。
创建完成后 CI 系统会对提交的 PR 进行一系列的负责检查并执行必要的测试,除了这些系统自动化的验证之外。其他社区贡献者会对 PR 进行 code review,在有足够来自于 TiDB Reviewer 及以上权限的贡献者对 PR 点赞后变更才能够被合并到项目主干中。
在 PR 提交后不久就得到了 @morgo 的 review 反馈,反馈一针见血的指出了问题背后的真正原因是 PR #24836 中对 TiDBSkipIsolationLevelCheck 变量初始化行为的错误变更,去掉 TiDBSkipIsolationLevelCheck 变量定义中的 skipInit: true 初始化字段即可确保 session 初始化时正确的将 global 变量值复制到 session 中,让前面的隔离级别检查逻辑行为恢复正常。根据这个线索进行代码修改并实际测试证明表现符合预期,接下来让我们继续分析 skipInit 相关的源码探个究竟。
代码中所有对 skipInit 变量的读取操作都封装在上图的 SkipInit 函数中,从下图中我们可以看到 SkipInit 方法用于在初始化新的 session 变量 cache 的过程跳过部分变量。
接下来 newSessionCache 被更新到 session 变量中并通过下图中的 GetSessionCache 方法对外提供访问。
而 GetSessionCache 方法只有一个调用方 loadCommonGlobalVariablesIfNeeded,到这里 skipInit 对系统变量初始化流程的影响就非常清晰了。
当 session 创建完成后,没有标记为 skipInit 的变量都会以变量的初始值的形式更新到会话变量表中,也就是前面提到的 systems 变量表中。当我们将 TiDBSkipIsolationLevelCheck 的 skipInit 恢复为 false 之后,全局变量 tidb_skip_isolation_level_check 能够在这个初始化的过程中被正确的复制到用户会话,使得调整会话事务隔离级别的行为符合用户预期。
在问题得到解决后,大家可能还会问在什么样的情况下 skipInit 需要被设置成 true。在引入这个功能的 PR #24836 中我们可以得知部分不适合在初始化过程中复制到会话中的变量会利用这个标记实现黑名单功能。而在这次重构过程中 TiDBSkipIsolationLevelCheck 被错误的设置在黑名单中导致了 5.1 开始版本行为的异常。
What problem does this PR solve?
Problem Summary:Currently the builtinGlobalVariable feature is a source of bugs because even though a sysvar is added, it is not automatically copied to new sessions. This behavior is also not MySQL compatible, where it is expected a sysvar of session scope should be copied on session init.Fixing the full incompatibility is a little bit more complicated, but this takes the initial step of inverting from an allow list to a deny list, but is otherwise functionally compatible.This also includes the fix from #24835 should this PR supercede it.
What is changed and how it works?
What's Changed:A variable on the SysVar struct can now be set to skipInit. By default it will not skip for session-scope variables, which is why it is now a deny list.However, it will always skip for noop variables, which helps keep the memory footprint of new sessions slightly lower.
Related changes
- None
- There will need to be followup PRs to handle the specific skipInit variables; some probably don't need to be on this list. The global-only variables that are hard-coded into the SkipInit function will also need removing.
后记
感谢向社区报告 TiDB 行为异常的热心用户,非常遗憾没能在故障发生的第一时间定位并解决问题。但我们仍然希望在新版本发布修复这个问题后 TiDB 能够为你支撑 Hive metastore 乃至更多业务场景起到积极作用。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
红象云腾加入龙蜥社区,构建兼容龙蜥生态的大数据基础平台
近日,北京红象云腾系统技术有限公司经理事成员单位综合评估,一致决定正式接受北京红象云腾系统技术有限公司加入龙蜥社区,助力社区开源生态建设。 红象云腾作为中国大数据基础软件厂商,为政府和企事业提供基于 Apache Hadoop 生态架构的分布式存储和分布式计算的大数据解决方案。红象云腾致力于 Apache Hadoop 架构在中国的普及和推广,专注于打造中国自主可控的大数据基础存储和处理软件平台。红象云腾已经和国内、国际多家芯片、操作系统、服务器厂商建立广泛的合作关系,秉承“从开源中来,到开源中去〞的思想,积极参与国际技术开源社区的建设。 在未来,红象云腾(REDOOP)基于 Apache Hadoop 大数据平台软件领域的行业积累,将持续完善 Redoop Enterprise V9.0 基础软件,构建支持兼容 OpenAnolis 和阿里云生态的大数据基础平台软件和解决方案,与社区合作伙伴一起共同构建面向数字基础设施的开源操作系统。 关于龙蜥社区 龙蜥社区(OpenAnolis)是由企事业单位、高等院校、科研单位、非营利性组织、个人等按照自愿、平等、开源、协作的基础上组成的非盈利...
- 下一篇
花了30天才肝出来,史上最全面Java设计模式总结,看完再也不会忘
本文所有内容均节选自《设计模式就该这样学》 序言 Design Patterns: Elements of Reusable Object-Oriented Software(以下简称《设计模式》),一书由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides合著(Addison-Wesley,1995)。这四位作者常被称为“四人组(Gang of Four)”,而这本书也就被称为“四人组(或 GoF)”书。他们首次给我们总结出一套软件开发可以反复使用的经验,帮助我们提高代码的可重用性、系统的可维护性等,解决软件开发中的复杂问题。 设计模式已诞生20多年,其间相继出版的关于设计模式的经典著作不计其数。如果说GoF的《设计模式》是设计模式领域的“圣经”,那么之后出版的各种关于设计模式的书籍可称为“圣经”的“批注版”或者“白话版”。本书正是基于GoF的《设计模式》来编写的。 《设计模式》总结的是经验之谈,千万不要死记硬背,生搬硬套。下面来总体预览一下设计模式的分类和总结,如下表所示。 分 类 解 释 举 例 创建型设计模式(Creatio...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS8编译安装MySQL8.0.19