带着问题读tidb源码:hive元数据使用tidb启动报错

TiDB_PingCAP TiDB_PingCAP     2023-02-16     486

关键词:

《带着问题读源码系列》- 开篇

在 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 乃至更多业务场景起到积极作用。

tidb在大数据架构下的应用

作者:胡梦宇一年前,TiDB首次遇上知乎的大数据架构,那时我们将HiveMetaStore的元数据库迁移到了TiDB,得到了超过单机数据库一个量级的性能提升。在见识过分布式NewSQL数据库TiDB的威力后,我们对TiDB寄予厚望... 查看详情

tidb初级课程体验8(文件和日志与监控)

...的数据库是有不同的。一般我们的数据库包含有如下几类问题1 数据文件2 配置文件3 数据日志文件4 告警错误文件从图中看,TIDB分布式数据库的文件的分布和其他的数据库是不同的,TIDB作为客户端来说他不具有数... 查看详情

猿创征文|国产数据库实战之使用docker部署tidb集群(代码片段)

猿创征文|国产数据库实战之使用Docker部署TiDB集群一、TiDB介绍1.TiDB简介2.TiDB特性3.TiDB集群整体架构4.TiDB集群各部分介绍5.本次TiDB集群组件二、检查本地环境1.检查docker状态2.检查docker版本3.检查docker-compose版本三、下载tidb-docker-comp... 查看详情

猿创征文|国产数据库实战之使用docker部署tidb集群(代码片段)

猿创征文|国产数据库实战之使用Docker部署TiDB集群一、TiDB介绍1.TiDB简介2.TiDB特性3.TiDB集群整体架构4.TiDB集群各部分介绍5.本次TiDB集群组件二、检查本地环境1.检查docker状态2.检查docker版本3.检查docker-compose版本三、下载tidb-docker-comp... 查看详情

猿创征文|国产数据库实战之使用docker部署tidb集群(代码片段)

猿创征文|国产数据库实战之使用Docker部署TiDB集群一、TiDB介绍1.TiDB简介2.TiDB特性3.TiDB集群整体架构4.TiDB集群各部分介绍5.本次TiDB集群组件二、检查本地环境1.检查docker状态2.检查docker版本3.检查docker-compose版本三、下载tidb-docker-comp... 查看详情

跟随一条insert语句,进入tidb的源码世界(上)

TiDB是googleF1的开源实现;TiDB实现了基于mvcc的乐观锁,在线表结构变更,基于时间戳的数据线性一致性,等等;为了可靠性,TiDB和Oracle一样,维护了百万级别的自动化测试用例,跑在k8s搭建的集群之上;TiDB里面用到的算法,都... 查看详情

tidb-使用tidblightning迁移mysql数据至tidb中(代码片段)

一、TiDBLightning介绍TiDBLightning是一个将全量数据高速导入到TiDB集群的工具,目前支持Mydumper或CSV输出格式的数据源。你可以在以下两种场景下使用Lightning:迅速导入大量新数据。备份恢复所有数据。TiDBLightning主要包含两... 查看详情

tidb一个大数据实时计算的存储利器(代码片段)

...是由中国PingCAP公司开发的,是一个开源的分布式NewSQL数据库。它最初的设计目标是解决传统关系型数据库的瓶颈和限制,实现高可用、可扩展和高性能的数据存储和处理。TiDB架构详解TiDB是一个分布式的NewSQL数据库,... 查看详情

tidb数据库压力测试工具安装(代码片段)

... 如果自己使用sysbench来测试mysql性能的话,建议使用源码编译比较好,版本为0.4版本。步骤是:1、yuminstallm4autoconfautomakelibtool2、./configure--prefix=/usr/local/sysbench--with-mysql-includes=/usr/l 查看详情

当大数据架构遇上tidb(代码片段)

...:胡梦宇,知乎核心架构平台开发工程师,大数据基础架构方向,主要工作内容是负责知乎内部大数据组件的二次开发和数据平台建设。前言一年前,知乎的大数据架构与TiDB首次相遇,那时我们将HiveMetaSto... 查看详情

大数据大数据组件tidb原理+实战篇(代码片段)

文章目录1.TiDB引入1.1.数据库技术发展简史1.2.从MySQL到TiDB1.3.TiDB概述1.4.数据库种类简介2.TiDB架构特性2.1.TiDB整体架构2.2.TiDB核心特性2.3.存储和计算能力3.TiDB安装部署3.1.TiDB-Local单机版3.2.TiDB-Docker集群版4.TiDB实践案例4.1.TiDB-SQL操作4.2... 查看详情

tidb监控框架概述

TiDB使用开源时序数据库 Prometheus 作为监控和性能指标信息存储方案,使用 Grafana 作为可视化组件进行展示。Prometheus在TiDB中的应用Prometheus是一个拥有多维度数据模型的、灵活的查询语句的时序数据库。Prometheus作为热门的... 查看详情

tidb的数据类型查询问题

参考技术Atidb对于类型要求比较严格,例如varchar存储的字段内容,查询时用整形查,tidb会先转化类型后再查询,速度超级慢idint(11)primarykeyadvertiser_idvarchar(32)notnulladvertiser_id存储的都是123,445,666之类select*fromtablewherenamein(445,666)... 查看详情

tidb4业界使用情况

...不断降低、大表DDL无法执行等,不得不面临数据库扩展的问题。对于MySQL来讲,最直接的方案就是采用分库分表的水平扩展方式 查看详情

使用dumpling从tidb/mysql导出数据

安装tiupinstalldumpling所需要权限shellSELECTRELOADLOCKTABLESREPLICATIONCLIENTPROCESS基本参数-h|--host:连接的数据库主机的地址,默认127.0.0.1-p|--password:连接的数 查看详情

tidb之mac上搭建及调试技巧

...行实时的通讯。我今天仔细分析了一下,最终总结出两个问题需要解决:PD和TiKV的端口需要暴露给宿主机;宿主机上的TiDB服务需要解决docker-compose服务内部域名调用的问题;解决方案第一个问题比较容易解决,docker本身就提供了... 查看详情

tidb-使用br工具进行数据热备份与恢复(代码片段)

...iDB分布式备份恢复的命令行工具,用于对TiDB集群进行数据备份和恢复。BR只支持在TiDBv3.1及以上版本使用。在前面的章节中,我们介绍了dumpling将数据导出的方式,也可以作为一种备份的方式,并且导出的数据是极... 查看详情

tidb-使用tidbbinlog实现数据复制(代码片段)

...解为Mysql的Binlog主从服务模式,并且TiDBBinlog还支持将数据发送到Kafka中,这又类似与Canal中间件。目前TIDBBinlog集群主要分为Pump和Drainer两个组件,以及binlogctl工具。TiDBBinlog整体架构注意:TiDBBinlog与TiDBv5.0版本开始... 查看详情