Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

常见分布式基础设施系统设计图解(二):分布式数据库

Posted on 10/08/202009/24/2024 by 四火

从大致的非功能需求角度来说,作为一般的分布式持久化存储系统,这样三个需求从重要性依次排列:

Durability > Availability > Performance

即最重要的是,数据绝对不能丢失,其次是要一直提供服务,最后才是要保持一定的性能。当然,有了上述基础以后,我们还可以谈论任何分布式存储系统都涉及的重要特性,比如一致性。最后,作为特定的存储系统——“数据库”,我们还常常谈论一些特定的特性,比如权限管理和事务控制等等。

下面拿的是 Bigtable 来举例的,它建立在 GFS 这样的分布式文件系统上面,有一定代表性。

  • 图中展示的是一个简单的写数据的流程,虚线是控制流,实线是数据流。
  • 客户端先去 Chubby 获取 Root tablet 的地址,然后去 root tablet 获取相应的 metadata tablets 的地址,再去访问 metadata tablet 获取 user tables 的地址。
  • 上面的地址都是可以被 cache 的,所以实际的访问多数情况下远不需要那么多。
  • 我们在讨论 tablets 的时候,实际讨论的是逻辑层面的东西,而它们实际会被持久化到 SSTables 里面去,在 GFS 上就是一个个的文件,这是物理层面的东西。
  • Tablet Server 持有 GFS 的客户端来实现对文件系统的读写。客户端不和 GFS 直接打交道,而 GFS 并不关心 Bigtable 层面的概念,只关心文件 block 的读写。图中我省掉了 GFS 的 Master 结点。
  • Bigtable 的数据分成这样几种存放形式:
    • 一种是稳定的、有序的数据,sharding 后存放在 SSTable(Sorted String Table),每一个 SSTable 有自己的 index 和 bloom filter,前者用来加快数据定位,后者用来快速存在性判断(尤其是它判断得到不存在的结果的话,它的正确率是 100%)。Tablet Server 的内存中也会有部分 index 和全部 bloom filter 的拷贝,以提高数据定位的效率。
    • 第二种是由于数据增加、修改等原因,未和上述一起排序的 “额外” 的数据,这些数据一开始是存放在 Tablet Server 的内存中的,使用一个 Skip List 来维护,它们定期归并到上述的 SSTable 中去。为了防止这些数据丢失,需要使用 WAL(Write Ahead Log)持久化到 GFS 中,这个过程是很快的。这样这些后来的新增和修改操作既保证了效率,又不丢失持久性。
    • 由于 GFS 的特性,对数据追加的操作可以高效完成,但是数据修改却往往不是(比如某个值的修改导致其占用的空间增大,原地修改将导致整个文件的数据从该值的位置开始向后平移)。这个限制非常关键,Bigtable 的数据修改一般都是通过内存中的追加写操作来完成(使用一个 Skip List 来维护),内存中的追加操作达到一定大小阈值以后,它们将被磁盘序列化为 SSTable,而它的内部是有序的。于是,一条数据记录可能存在多个版本,在内存(无序)中和多于一个的 SSTable(有序)中都存在,但只有最新的一条才作数。
    • 因此对于某条数据的读取,是有可能从多个地方读取的,即 Tablet Server 的内存和多个 SSTable。在查询获取某一 key 的数据的时候,需要先访问内存中的 Skip List;然后对于硬盘中有序的 SSTable,过一遍 Bloom Filter(它可以长期待在内存里以减少磁盘读取),再加载它部分的 index 到内存中,从而定位到数据位置。
    • 每隔一定时间,所有这些 SSTable 可以做一次归并整理(有 k 个 SSTable 那就是 k 路归并),顺便剔除过期数据。
[Updated on 9/24/2024] 阅读 Bigtable 的论文后有更多的记录放在这里。

Bigtable 的数据模型符合这样的结构:

(row:string, column:string, time:int64) -> string

一个 row key,一个 column key,一个时间戳,combine 起来映射到一个 string 上面去。

Row key 是根据字典序排列的,并且因此自动划分范围并 partition。这种设计可以让相同前缀主键的行放在一起,处理起来就可以按照 range 来读取,很高效。事务控制上,Bigtable 也支持基于单行的事务。一个 range 的 rows 就被划分为一个 tablet,tablet 是 Bigtable 做 rebalance 最小的单位。

多个 column keys 可以组成一个 column family,这个概念的实现,基本上除了 Bigtable 和 HBase 比较少见到,一般一个 column family 的 columns 都符合同一种数据类型。Bigtable 是列数据库,若干个需要一起访问的 column family 的列数据可以放到一个 SSTable 上面去。一个 column key 的格式是:

family:qualifier

Access control 和 accounting 都是在 column family 这一层实现的。

对于时间戳,对于相同 row key 和 column key 的数据,默认是按照时间戳降序排列的。GC 机制可以回收客户指定的较新的保留版本以外的所有版本。

SSTable 包含若干个 blocks,对于从 SSTable 中读取数据,有两种常见的形式,一种是映射所有数据到内存中,这样读取就直接访问内存就可以;另一种是使用二分查找索引找到对应的那个 block,然后读取 block。SSTable 是一个持久化了的映射表,而 tablet 则是一个 range 的行,这是两个不同层面上的概念。一个 tablet 可能数据来源于多个 SSTable。

Bigtable 也是 singler master 的结构,和 GFS 设计类似,为了给 master 减负,多数 client 可以直接去和 tablet server(每一个 tablet server 可以管理很多 tablets)沟通,而不需要经过 master。

Metadata 也是存放在 tablets 里面的,但是它们设计得比较特殊,只有三层,并且 root 是不会分裂的,每一层都保存了下一层属于它的 tablets 的位置(root 的位置信息就保存在 Chubby file 中)。客户端会缓存 tablet 的位置信息,但是如果有了变化,它会到它的 parent 节点那里去找。

对于写数据,和很多追求高 throughput 的系统一样,使用 commit log 加上 batching write (group commit)。

Compaction 分为两种,

  • 一种是 Minor Compaction,就是 memtable 的大小到一定值了以后,系统就会写入新的 memtable,原来的 memtable 会被转化为 SSTable;
  • 另一种是 Major Compaction,就是把 SSTable merge 成为数量更少但是更大的 SSTable(之前在 SSTable 里被标记为已删除的数据行直接被丢弃),这也是异步进行的。

这是《常见分布式系统设计图解》系列文章中的一篇,如果你感兴趣,请参阅汇总(目录)寻找你其它感兴趣的内容。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

你可能也喜欢看:

  1. 常见分布式基础设施系统设计图解(一):分布式文件系统
  2. 常见分布式基础设施系统设计图解(三):分布式消息队列
  3. 常见分布式基础设施系统设计图解(五):分布式流控系统
  4. 常见分布式基础设施系统设计图解(六):分布式 MR 系统
  5. 常见分布式基础设施系统设计图解(七):分布式实时流处理系统

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Spark 互联网 亚马逊 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 眼界 程序员 管理 系统设计 缓存 编程范型 美股 英语 西雅图 设计 问题 面向对象 面试

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Machine Learning and Artificial Intelligence (6)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 聊一聊分布式系统中的时间
  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • panshenlian.com on 初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow
  • panzhixiang on 关于近期求职的近况和思考
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
  • Anonymous on 我裸辞了
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme
Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接