TiDB(7):技术内幕之存储

news2024/11/26 16:51:47

1 引言

数据库、操作系统和编译器并称为三大系统,可以说是整个计算机软件的基石。其中数据库更靠近应用层,是很多业务的支撑。这一领域经过了几十年的发展,不断的有新的进展。

很多人用过数据库,但是很少有人实现过一个数据库,特别是实现一个分布式数据库。了解数据库的实现原理和细节,一方面可以提高个人技术,对构建其他系统有帮助,另一方面也有利于用好数据库。

研究一门技术最好的方法是研究其中一个开源项目,数据库也不例外。单机数据库领域有很多很好的开源项目,其中 MySQL 和 PostgreSQL 是其中知名度最高的两个,不少同学都看过这两个项目的代码。但是分布式数据库方面,好的开源项目并不多。 TiDB 目前获得了广泛的关注,特别是一些技术爱好者,希望能够参与这个项目。由于分布式数据库自身的复杂性,很多人并不能很好的理解整个项目,所以我希望能写一些文章,自顶向下,由浅入深,讲述 TiDB 的一些技术原理,包括用户可见的技术以及大量隐藏在 SQL 界面后用户不可见的技术点。

2 保存数据

数据库最根本的功能是能把数据存下来,所以我们从这里开始。

保存数据的方法很多,最简单的方法是直接在内存中建一个数据结构,保存用户发来的数据。比如用一个数组,每当收到一条数据就向数组中追加一条记录。这个方案十分简单,能满足最基本,并且性能肯定会很好,但是除此之外却是漏洞百出,其中最大的问题是数据完全在内存中,一旦停机或者是服务重启,数据就会永久丢失。

为了解决数据丢失问题,我们可以把数据放在非易失存储介质(比如硬盘)中。改进的方案是在磁盘上创建一个文件,收到一条数据,就在文件中 Append 一行。OK,我们现在有了一个能持久化存储数据的方案。但是还不够好,假设这块磁盘出现了坏道呢?我们可以做 RAID (Redundant Array of Independent Disks),提供单机冗余存储。如果整台机器都挂了呢?比如出现了火灾,RAID 也保不住这些数据。我们还可以将存储改用网络存储,或者是通过硬件或者软件进行存储复制。到这里似乎我们已经解决了数据安全问题,可以松一口气了。But,做复制过程中是否能保证副本之间的一致性?也就是在保证数据不丢的前提下,还要保证数据不错。保证数据不丢不错只是一项最基本的要求,还有更多令人头疼的问题等待解决:

  • 能否支持跨数据中心的容灾?
  • 写入速度是否够快?
  • 数据保存下来后,是否方便读取?
  • 保存的数据如何修改?如何支持并发的修改?
  • 如何原子地修改多条记录?

这些问题每一项都非常难,但是要做一个优秀的数据存储系统,必须要解决上述的每一个难题。 为了解决数据存储问题,我们开发了 TiKV 这个项目。接下来我向大家介绍一下 TiKV 的一些设计思想和基本概念。

3 Key-Value

作为保存数据的系统,首先要决定的是数据的存储模型,也就是数据以什么样的形式保存下来。TiKV 的选择是 Key-Value 模型,并且提供有序遍历方法。简单来讲,可以将 TiKV 看做一个巨大的 Map,其中 Key 和 Value 都是原始的 Byte 数组,在这个 Map 中,Key 按照 Byte 数组总的原始二进制比特位比较顺序排列。 大家这里需要对 TiKV 记住两点:

  1. 这是一个巨大的 Map,也就是存储的是 Key-Value pair
  2. 这个 Map 中的 Key-Value pair 按照 Key 的二进制顺序有序,也就是我们可以 Seek 到某一个 Key 的位置,然后不断的调用 Next 方法以递增的顺序获取比这个 Key 大的 Key-Value

讲了这么多,有人可能会问了,这里讲的存储模型和 SQL 中表是什么关系?在这里有一件重要的事情要说四遍:

这里的存储模型和 SQL 中的 Table 无关! 这里的存储模型和 SQL 中的 Table 无关! 这里的存储模型和 SQL 中的 Table 无关! 这里的存储模型和 SQL 中的 Table 无关!

现在让我们忘记 SQL 中的任何概念,专注于讨论如何实现 TiKV 这样一个高性能高可靠性的巨大的(分布式的) Map。

4 RocksDB

任何持久化的存储引擎,数据终归要保存在磁盘上,TiKV 也不例外。但是 TiKV 没有选择直接向磁盘上写数据,而是把数据保存在 RocksDB 中,具体的数据落地由 RocksDB 负责。这个选择的原因是开发一个单机存储引擎工作量很大,特别是要做一个高性能的单机引擎,需要做各种细致的优化,而 RocksDB 是一个非常优秀的开源的单机存储引擎,可以满足我们对单机引擎的各种要求,而且还有 Facebook 的团队在做持续的优化,这样我们只投入很少的精力,就能享受到一个十分强大且在不断进步的单机引擎。当然,我们也为 RocksDB 贡献了一些代码,希望这个项目能越做越好。这里可以简单的认为 RocksDB 是一个单机的 Key-Value Map。

底层LSM树将对数据的修改增量保存在内存中,达到指定大小限制之后批量把数据flush到磁盘中,磁盘中树定期可以做merge操作,合并成一棵大树,以优化性能。

5 Raft

好了,万里长征第一步已经迈出去了,我们已经为数据找到一个高效可靠的本地存储方案。俗话说,万事开头难,然后中间难,最后结尾难。接下来我们面临一件更难的事情:如何保证单机失效的情况下,数据不丢失,不出错?简单来说,我们需要想办法把数据复制到多台机器上,这样一台机器挂了,我们还有其他的机器上的副本;复杂来说,我们还需要这个复制方案是可靠、高效并且能处理副本失效的情况。听上去比较难,但是好在我们有 Raft 协议。Raft 是一个一致性算法,它和 Paxos 等价,但是更加易于理解。Raft 的论文,感兴趣的可以看一下。本文只会对 Raft 做一个简要的介绍,细节问题可以参考论文。另外提一点,Raft 论文只是一个基本方案,严格按照论文实现,性能会很差,我们对 Raft 协议的实现做了大量的优化。

Raft 是一个一致性协议,提供几个重要的功能:

  • Leader 选举
  • 成员变更
  • 日志复制

TiKV 利用 Raft 来做数据复制,每个数据变更都会落地为一条 Raft 日志,通过 Raft 的日志复制功能,将数据安全可靠地同步到 Group 的多数节点中。

到这里我们总结一下,通过单机的 RocksDB,我们可以将数据快速地存储在磁盘上;通过 Raft,我们可以将数据复制到多台机器上,以防单机失效。数据的写入是通过 Raft 这一层的接口写入,而不是直接写 RocksDB。通过实现 Raft,我们拥有了一个分布式的 KV,现在再也不用担心某台机器挂掉了。

6 Region

讲到这里,我们可以提到一个 非常重要的概念:Region。这个概念是理解后续一系列机制的基础,请仔细阅读这一节。

前面提到,我们将 TiKV 看做一个巨大的有序的 KV Map,那么为了实现存储的水平扩展,我们需要将数据分散在多台机器上。这里提到的数据分散在多台机器上和 Raft 的数据复制不是一个概念,在这一节我们先忘记 Raft,假设所有的数据都只有一个副本,这样更容易理解。

对于一个 KV 系统,将数据分散在多台机器上有两种比较典型的方案:一种是按照 Key 做 Hash,根据 Hash 值选择对应的存储节点;另一种是分 Range,某一段连续的 Key 都保存在一个存储节点上。TiKV 选择了第二种方式,将整个 Key-Value 空间分成很多段,每一段是一系列连续的 Key,我们将每一段叫做一个 Region,并且我们会尽量保持每个 Region 中保存的数据不超过一定的大小(这个大小可以配置,目前默认是 64mb)。每一个 Region 都可以用 StartKey 到 EndKey 这样一个左闭右开区间来描述。

注意,这里的 Region 还是和 SQL 中的表没什么关系! 请各位继续忘记 SQL,只谈 KV。 将数据划分成 Region 后,我们将会做 两件重要的事情:

  • 以 Region 为单位,将数据分散在集群中所有的节点上,并且尽量保证每个节点上服务的 Region 数量差不多
  • 以 Region 为单位做 Raft 的复制和成员管理

这两点非常重要,我们一点一点来说。

先看第一点,数据按照 Key 切分成很多 Region,每个 Region 的数据只会保存在一个节点上面。我们的系统会有一个组件来负责将 Region 尽可能均匀的散布在集群中所有的节点上,这样一方面实现了存储容量的水平扩展(增加新的结点后,会自动将其他节点上的 Region 调度过来),另一方面也实现了负载均衡(不会出现某个节点有很多数据,其他节点上没什么数据的情况)。同时为了保证上层客户端能够访问所需要的数据,我们的系统中也会有一个组件记录 Region 在节点上面的分布情况,也就是通过任意一个 Key 就能查询到这个 Key 在哪个 Region 中,以及这个 Region 目前在哪个节点上。

对于第二点,TiKV 是以 Region 为单位做数据的复制,也就是一个 Region 的数据会保存多个副本,我们将每一个副本叫做一个 Replica。Replica 之间是通过 Raft 来保持数据的一致,一个 Region 的多个 Replica 会保存在不同的节点上,构成一个 Raft Group。其中一个 Replica 会作为这个 Group 的 Leader,其他的 Replica 作为 Follower。所有的读和写都是通过 Leader 进行,再由 Leader 复制给 Follower。

 

我们以 Region 为单位做数据的分散和复制,就有了一个分布式的具备一定容灾能力的 KeyValue 系统,不用再担心数据存不下,或者是磁盘故障丢失数据的问题。这已经很 Cool,但是还不够完美,我们需要更多的功能。

7 MVCC

很多数据库都会实现多版本控制(MVCC),TiKV 也不例外。设想这样的场景,两个 Client 同时去修改一个 Key 的 Value,如果没有 MVCC,就需要对数据上锁,在分布式场景下,可能会带来性能以及死锁问题。 TiKV 的 MVCC 实现是通过在 Key 后面添加 Version 来实现,简单来说,没有 MVCC 之前,可以把 TiKV 看做这样的:

Key1 -> Value
Key2 -> Value
……
KeyN -> Value

有了 MVCC 之后,TiKV 的 Key 排列是这样的:

Key1-Version3 -> Value
Key1-Version2 -> Value
Key1-Version1 -> Value
……
Key2-Version4 -> Value
Key2-Version3 -> Value
Key2-Version2 -> Value
Key2-Version1 -> Value
……
KeyN-Version2 -> Value
KeyN-Version1 -> Value
……

注意,对于同一个 Key 的多个版本,我们把版本号较大的放在前面,版本号小的放在后面(回忆一下 Key-Value 一节我们介绍过的 Key 是有序的排列),这样当用户通过一个 Key + Version 来获取 Value 的时候,可以将 Key 和 Version 构造出 MVCC 的 Key,也就是 Key-Version。然后可以直接 Seek(Key-Version),定位到第一个大于等于这个 Key-Version 的位置。

8 事务

TiKV 的事务采用的是 Percolator 模型,并且做了大量的优化。TiKV 的事务采用乐观锁,事务的执行过程中,不会检测写写冲突,只有在提交过程中,才会做冲突检测,冲突的双方中比较早完成提交的会写入成功,另一方会尝试重新执行整个事务。当业务的写入冲突不严重的情况下,这种模型性能会很好,比如随机更新表中某一行的数据,并且表很大。但是如果业务的写入冲突严重,性能就会很差,举一个极端的例子,就是计数器,多个客户端同时修改少量行,导致冲突严重的,造成大量的无效重试。

9 其他

到这里,我们已经了解了 TiKV 的基本概念和一些细节,理解了这个分布式带事务的 KV 引擎的分层结构以及如何实现多副本容错。下一节会介绍如何在 KV 的存储模型之上,构建 SQL 层。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于FreeRTOS的嵌入式设备管理关键技术研究及实现(学习三)

设备节点功能模块设计 在本文节点功能设计中,拥有用于检测温度的温度传感器、监测湿度的湿度传感器以及 调节空调温度的IR红外发射器,另留有部分外接扩展串口,因此可能会在实际使用中扩展更多的设备模块与功能。 可见,如何高效地…

2023年03月份青少年软件编程Python等级考试试卷三级真题(含答案)

2023-03 Python三级真题 分数:100 题数:38 测试时长:60min 一、单选题(共25题,共50分) 1.十进制数111转换成二进制数是?( )(2分) A.111 B.1111011 C.101111 D…

青岛大学_王卓老师【数据结构与算法】Week04_10_线性表的应用3_学习笔记

本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享,另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础–…

Spring Boot中的度量指标及使用方法

Spring Boot中的度量指标及使用方法 简介 Spring Boot是目前流行的Java后端框架之一,它提供了许多有用的功能,其中包括度量指标。度量指标可以帮助我们监测应用程序的性能、稳定性和可靠性,以便及时发现并解决问题。本文将介绍Spring Boot中…

使用Jetpack Compose集成WebView

在Android开发中,WebView是一个非常重要的组件,它可以用来显示网页或加载在线内容。然而,在Jetpack Compose(Google推出的新的UI工具包)中,目前没有内置的WebView Composable。但不必担心,你可以…

【数据算法与结构】栈和队列课后习题

题目(共两道) 题目1 Qestion: 根据下面代码片段写出运行下列程序段的输出结果(元素类型为char) 题目代码片段 void main() {Stack S; char x,y; InitStack(S); // 初始化栈x ‘e ‘; y ‘c’; Push(S, ‘h‘); Push(S, ‘r‘); Push(S,y);Pop(S,x);…

了解Java可见性的本质

作者:早恒 前一段时间重温了伪共享(false sharing)问题,了解到深处有几个问题一直想不明白,加上开发过程中遇到volatile时总觉得理解不够透彻,借着这次脑子里这几个问题,探究下Java可见性的本质…

【C语言】深入学习函数(万字)

👦个人主页:Weraphael ✍🏻作者简介:目前正在回炉重造C语言(2023暑假) ✈️专栏:【C语言航路】 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你…

文件上传+文件包含组合式getshell

实验目的 通过本实验,了解文件包含的特点,掌握图片马的执行方法,文件上传文件包含组合式getshell。 实验环境 操作机:kali 靶机:Windows 2007 用户名/密码:college/360College 实验地址:http…

jenkins部署springboot项目

jenkins部署springboot项目 1、创建一个项目 上传到gitee 1、创建项目 2、上传到git 2、jenkins创建一个pipeline项目 Pipeline简介 1)概念 Pipeline,简单来说,一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者…

优化chatGPT提示词的Prompts

你扮演一个专业的chatGPT提示词工程师,我将为您提供我的提示词,它用三个反引号分隔,请根据openai发布的提示词标准和优化技巧,改进和优化我的提示词,让chatGPT能够更好的理解。 我的第一个提示词是:“”“……

【UI设计】新拟态风格

新拟态风格 1.有且只有一个光源照射 那作者在追波上按钮也好还是卡片处理也好,仔细观察不难发现,它定了一个光源,是从左上向右下照射的,所以,越靠近光源的部分,越亮,远离光源的越暗(…

卡尔曼滤波实例分析(一)

1 现实问题 假设一个物体位于1000米处以自由落体运动,地面有一台具有特殊功能的雷达,对其进行观察,现需要对其下落的高度进行测量; (1)建模 速度:V gt 位置:Y -Vt Y0 &#xff0…

react笔记_06类组件

目录 复习展开运算符 组件什么叫做组件?分类类组件es6新增构造函数语法类组件渲染类组件的this指向类组件的三大属性state作用语法-初始化数据语法-修改state数据语法-获取state中的数据案例 propspropTypes属性(prop-types库)defaultProps属性 refs[1] 字符串形式的…

基于UFLD-v2的改进:UFLD-v2-plus

1 待解决问题 UFLD-v2是一个非常优秀的车道线检测模型。也有一些问题。 1 参数量太大,一个模型600M。 2 不能区分车道线。 2 工作内容 改进方法如下 2.1 降低参数量 对模型网络逐层参数量,可以发现,86%(很久之前分析的&…

基于STM32+华为云IOT设计的智能浇花系统

一、前言 随着社会的不断发展和人们生活水平的逐渐提高,人们逐渐追求高质量的生活,很多人都会选择在家里或办公室种植一些花卉以净化家庭空气,陶冶情操,但是很多人忙于工作、学习、出差、旅游或者一些其他的原因,不能及时地对花卉进行照料,短时间内导致很多花卉因缺水分…

100天精通Golang(基础入门篇)——第13天:深入解析Go语言中的字符串(string)及常用函数应用

🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《I…

怎么用爱问转换工具在线拆分PDF文件

我们平时在工作中会用到很多PDF文件,有的PDF文件内容很多,里面也有许多分类类别,其实对于这种内容多的PDF文件,可以使用拆分的方式,来获取自己需要的材料,那么,如何快速拆分PDF文件呢&#xff1…

VSCode离线安装Remote-SSH插件

最近想要在VSCode上安装Remote-SSH插件,以便可以远程登录云服务器进行开发或者文件编辑、文件传输等功能;但是VSCode中在线安装一直失败,所以记录一下离线安装的方法; 文章目录 一、下载VSCode Remote-SSH的离线插件,即…

python: PyCharm 2023.1打包项目成执行程序

IDE 最底部: pyinstaller -i heart.ico -D main.py 生成成功