C++ 周刊(C++ Weekly)第9期开始了
周刊目标:
•
不是要让你成为 C++ 专家,
•
而是让你成为 C++ 面试专家。
本期主题:
•
数据均衡拆分--hash函数
小思考(导读):
大家都知道,普通的哈希函数有个致命缺点:
节点数一变,几乎所有数据都要迁移,
一致性希函解决了这个问题
你可能不知道
•
为什么分布式数据库 TiDB不用一致性哈希?
•
为什么内存数据库 Redis不用一致性哈希?
•
为什么文件系统Ceph,也不用一致性哈希?
这一期从,
•
数据查询
•
数据副本
•
数据规模
•
自动化运维角度
帮你彻底搞懂这些面试高频问题背后逻辑
•
用户请求量暴增,订单积压,响应很慢,请立刻解决!”
我来!我充满热情,积极响应!
解决办法如下:
•
内存吃满 → 升级:32GB 不够?直接加到 128GB
•
CPU 打满 → 升级:核心不够?上更高规格 CPU
•
线程不足 → 升级:3 个线程不够?直接开到 10 个、20 个
> 画外音:操作系统提供可插拔接口 提供这能保证
•
水平扩缩容指通过调整服务节点的数量来实现服务能力的扩缩容
•
单台服务器顶不住?→ 10 台不够,再加 20 台!
画外音 系统架构提前设计了可扩展性
•
Nginx:Master + worker 模型
•
支持Raft 协议的Tidb集群
单线程改多线程,看似提升了并发,但负载均衡考虑了吗?
初次之外 你考虑什么因素 ,不均衡怎么办?
不会上来什么都没考虑吧,上来直接使用程咬金三板斧
你有没有想过,传说中的一致性哈希,
为什么像Ceph、TiDB这样的存储系统不怎么用?
一致性哈希最早确实很火。
它把每台机器映射到哈希环上,
新增或删除节点时,数据迁移量很小。
问题也很明显:
① 不支持拓扑感知
一致性哈希只知道节点编号,不知道物理位置。
就像你搬家,房东只给钥匙,不告诉你新房在哪栋楼。
结果可能三个副本全落在同一机架。
一旦机房出问题,数据直接掉线。
② 只支持 key→节点 单个查询, 不支持范围查询
•
TiDB 集群由 PD + TiKV + TiDB Server 构成。
•
策略:范围分片(Range-based Sharding)
•
数据按 Key Range 划分成 Region(默认 96MB 左右)。
•
每个 Region 有多个副本,通过 Raft 保证一致性。
•
PD(Placement Driver)负责 Region 调度和均衡。
•
特点:
1
支持范围查询(Range Scan):连续 Key 可以在一个 Region 内完成查询。
2
动态均衡:Region 热点会拆分(Split),PD 自动迁移副本。
3
副本管理原生:Raft 保证多副本一致性。
•
为什么不使用一致性哈希:
- 一致性哈希天然只适合 单 Key 映射节点,不支持顺序范围查询。
- 一致性哈希本身没有多副本拓扑感知逻辑,需要额外设计。
假设 TiDB 集群有 6 个 TiKV 节点:
数据中心 DC1
├─机架 R1: Node1, Node2
└─机架 R2: Node3
数据中心 DC2
├─机架 R3: Node4, Node5
└─机架 R4: Node6
•
Region 默认 3 副本:
•
PD 会自动选择不同机架节点,例如:
•
Region 副本:Node1(R1)、Node3(R2)、Node4(R3)
•
优势:
•
即使某个机架宕机,Region 副本仍可在其他机架继续提供服务。
•
支持自定义拓扑策略(跨机房、副本数可调)。
•
举例说明:范围查询
CRUSH 全称:Controlled Replication Under Scalable Hashing
1
层级拓扑感知
•
CRUSH Map 里有 节点层级:数据中心 → 机房 → 机架 → 服务器 → OSD
•
可以定义规则,副本要分布在不同机架或机房,保证故障域隔离。
假设 Ceph 集群有:
数据中心A
├─机架1: OSD 1,2
└─机架2: OSD 3,4
数据中心B
└─机架3: OSD 5,6
CRUSH 规则:每个对象 3 副本,要求 不同机架
•
对象 obj123 → PG → CRUSH 计算
•
CRUSH 输出三个 OSD: OSD2, OSD4, OSD5(分别在不同机架)
特点:
•
保证多副本分布均衡
•
考虑拓扑结构避免单点故障
•
自动均衡,无需中心调度器全局维护
你知道吗?
做分布式存储,最重要的一件事就是——拆得均匀!
如果拆不均匀,负载就可能集中在几台机器上,成为瓶颈
拆分均匀需要什么条件,代价是什么,如果不均衡怎办?
Redis Cluster 有一套“老派”的做法:固定槽 + 手工调整。
(1 )所有 Key 映射到 16384 个槽:
slot = CRC16(key) % 16384
(2)然后把槽分配到节点,比如有 3 个主节点:
•
节点A:0-5460
•
节点B:5461-10922
•
节点C:10923-16383
槽分布不均怎么办?
只能手工调整槽范围,重新分配槽。
为了高可用,Redis 提供 主从复制:
•
每个主节点挂多个从节点
•
主节点挂了,从节点自动顶上
生产环境通常用:
•
3主3从(6节点) 或 10主10从(20节点)
•
Redis 集群最多支持 1000 个主节点(官方建议 ≤400)
扩容成本高,但对中小规模系统够用了
DynamoDB 的思路完全不同:
自适应一致性哈希 + 虚拟节点 + 多副本
1️⃣ 哈希环
•
所有 Key 映射到一个 超大哈希环[0, 2^128-1]
•
哈希函数保证 Key 随机分布,负载天然均衡
•
理论上支持 千到万节点,没有上限
2️⃣ 虚拟节点
•
每台机器分配几百个虚拟节点
•
新增节点 → 只接管一小部分虚拟节点
•
上万节点 → 一样平滑扩容
好处:
•
拆分天然更均衡
•
哈希函数保证 Key 随机分布,负载天然均衡
•
超大哈希环, 2128-1 reids 216]
•
新增节点 → 只接管一小部分虚拟节点
•
上万节点 → 一样平滑扩容
DynamoDB 默认 3 副本,且跨可用区。
写入时,顺时针找 连续 3 个虚拟节点 放在不同物理节点:
•
单节点挂了 → 数据可用
•
整个机房挂了 → 还有副本顶上
小系统用 Redis,足够用了。
大系统靠 DynamoDB。
产品愿景
DynamoDB 的核心目标是三件事:
•
理论上支持 上万节点,没有上限
•
一致性哈希 + 虚拟节点 → 自动均衡
一句话:用户量翻 100 倍,系统不需要重构。
•
3 副本默认配置,且 跨可用区存储
•
写入数据 → 顺时针找 连续 3 个虚拟节点
•
单节点挂了 → 副本自动接管
•
整个机房挂了 → 还能继续对外服务
一句话:节点会挂、机房会挂,但数据不会丢。
•
DynamoDB 自己做 负载均衡
•
自己做 数据迁移
•
自己做 副本重建
•
节点故障靠 心跳包检测,发现异常立即重路由
•
运维几乎“零人工”
一句话:你不需要雇一队 DBA。
•
课程编号:15-445 / 645(本科生/研究生)
•
课程名:Database Systems
•
学校:Carnegie Mellon University(CMU)
•
主讲:Andy Pavlo(数据库内核大神,Peloton & NoisePage 项目负责人)
•
课程主页
https://15445.courses.cs.cmu.edu/fall2024/
•
PostgreSQL 的哈希索引早期是可扩展哈希,支持桶分裂和目录扩展
•
现代 PostgreSQL 中,默认索引用 B+Tree,但可扩展哈希仍然是重要的存储组件
英文原文:
•
Extendible hashing is a type of hash system which treats a hash as a bit string and uses a trie for bucket lookup.
•
Because of the hierarchical nature of the system, re-hashing is an incremental operation (done one bucket at a time, as needed).
•
This means that time-sensitive applications are less affected by table growth than by standard full-table rehashes.
•
Extendible hashing was described by Ronald Fagin in 1979. Practically all modern filesystems use either extendible hashing or B-trees. In particular, the Global File System, ZFS, and the SpadFS filesystem use extendible hashing.
中文翻译:
•
可扩展哈希(Extendible Hashing)是一种哈希系统,它将哈希值视为比特串,并使用 Trie 树 来进行桶(bucket)查找。
•
由于该系统具有分层结构的特点,重新哈希(re-hashing)是一个增量操作(按需一次处理一个桶)。这意味着,对于对时间敏感的应用,表增长对性能的影响比标准的全表重哈希要小得多。
•
可扩展哈希由 Ronald Fagin 在 1979 年提出。几乎所有现代文件系统都使用 可扩展哈希 或 B 树。特别地,Global File System、ZFS 和 SpadFS 文件系统都使用了可扩展哈希
•
文件系统的目录或元数据大小是动态变化的,不可预知。
•
可扩展哈希是一种 增量扩展 的哈希结构:
•
可扩展哈希相比 B 树或者其他平衡树
•
虽然 B 树也能处理动态扩展,但对于哈希索引的 快速随机访问,可扩展哈希更直接。
•
这种特性对 大规模文件系统 特别重要,尤其是数百万甚至数亿个文件的目录
想象你是一个邮局管理员,需要把信件按收件人邮编放到邮筒里:
1
一开始只有 2 个邮筒:
•
邮筒 0:放邮编以 0 开头的信
•
邮筒 1:放邮编以 1 开头的信
1
每个邮筒最多放 2 封信(容量限制)。
•
插入 4 封信,刚好分配到 2 个邮筒里,不超过容量。
•
插入第 5 封信时,目标邮筒已满 → 需要拆分邮筒:
•
拆成两个小邮筒,每个只看邮编的前 2 位
•
这样就能容纳更多信件
•
插入第 6 封信,如果邮筒再次满了,又继续拆分
•
全局深度类似“你需要看邮编几位数来决定放哪个邮筒”,随着拆分增加。
1
增量扩展:只拆分满的桶(邮筒),不重建整个表
2
层次结构:桶号由哈希值的前几位决定(类似逐层看邮编)
3
高效查找:找某个 key,只需要根据哈希前几位找到对应桶
如果遇到 数据不均衡 的情况,有两种思路:
为什么要等到不均衡了再调整?
不过,这往往涉及 复杂的数学公式和概率模型。 作为开发人员,我们很难自己设计出来, 只能借助 别人已经发明的概念和算法。
这种方式会结合 分片的物理位置、副本情况 等因素, 通过 重新分片、迁移副本 等手段, 实现 更灵活、低成本的动态扩展。