This commit is contained in:
by931
2022-09-06 22:30:37 +08:00
parent 66970f3e38
commit 3d6528675a
796 changed files with 3382 additions and 3382 deletions

View File

@@ -195,29 +195,29 @@ function hide_canvas() {
<p>那么如何让从节点替代主节点呢?这就涉及数据一致性的问题了(只有在主从节点数据一致的情况下,才能进行主从替换)。</p>
<p>关于数据一致性通常要考虑一致性强弱即强一致性和最终一致性的问题。而要解决一致性的问题则要进行一系列的一致性协议如两阶段提交协议Two-Phrase Commit2PC、Paxos 协议选举、Raft 协议、Gossip 协议。</p>
<p>所以分布式数据存储的问题可以分成:<strong>数据分片</strong><strong>数据复制</strong>,以及<strong>数据一致性</strong>带来的相关问题。接下来,我会针对这些问题,提炼出面试中最为核心和高频的考察点。</p>
<p><img src="assets/CgqCHl_1fLCAU01mAAGC5GguKkM382.png" alt="Lark20210106-170227.png" /></p>
<p><img src="assets/CgqCHl_1fLCAU01mAAGC5GguKkM382.png" alt="png" /></p>
<h3>案例解答</h3>
<p>就如我开篇提到的,面试官往往会把“案例背景中”的四个问题串联到具体的场景中,以具体的场景设问,比如“假设你是一家电商网站的架构师,现在要将原有单点上百 G 的商品做数据重构,存储到多个节点上,你会如何设计存储策略 ?”</p>
<p>因为是商品存储扩容的设计问题,很容易想到做数据的分库分表,也就是重新设计数据的分片规则,常用的分片策略有两种,即 Hash哈希分片和 Range范围分片。<strong>从这一点出发会考察你Hash哈希分片的具体实现原理。</strong></p>
<p>商品表包括主键、商品 ID、商品名称、所属品类和上架时间等字段。如果以商品 ID 作为关键字进行分片,系统会通过一个 Hash 函数计算商品 ID 的 Hash 值,然后取模,就能得到对应的分片。模为 4 就表示系统一共有四个节点,每个节点作为一个分片。</p>
<p>假设Hash 函数为 “商品 ID % 节点个数 4”通过计算可以得到每个数据应该存入的节点计算结果为 0 的数据存入节点 A结果为 1 的数据存入节点 B结果为 2 的数据存入节点 C计算为 3 的数据存储节点 D。
<img src="assets/Cip5yF_-eayAB5wqAAJhp0sQN50761.png" alt="12.png" /></p>
<img src="assets/Cip5yF_-eayAB5wqAAJhp0sQN50761.png" alt="png" /></p>
<p>商品数据 Hash 存储</p>
<p>可以看出Hash 分片的优点在于可以保证数据非常均匀地分布到多个分片上,并且实现起来简单,但扩展性很差,因为分片的计算方式就是直接用节点取模,节点数量变动,就需要重新计算 Hash就会导致大规模数据迁移的工作。</p>
<p><strong>这时,就会延伸出第二个问题,如何解决 Hash 分片的缺点,既保证数据均匀分布,又保证扩展性?</strong></p>
<p>答案就是一致性 Hash :它是指将存储节点和数据都映射到一个首尾相连的哈希环上。存储节点一般可以根据 IP 地址进行 Hash 计算,数据的存储位置是从数据映射在环上的位置开始,依照顺时针方向所找到的第一个存储节点。</p>
<p>在具体操作过程中,通常会选择带有虚拟节点的一致性 Hash。假设在这个案例中将虚拟节点的数量设定为 10 个,就形成 10 个分片,而这 10 个分片构成了整个 Hash 空间。现在让 A 节点对应虚拟节点 0 ~ 3B 节点对应虚拟节点 4 ~ 6C 节点对应虚拟节点 7 ~ 8D 节点对应虚拟节点 9。</p>
<p>同样根据哈希函数为 “商品 ID % 节点个数 10”得到每一个商品在 Hash 环上的位置然后根据顺时针查找最近的存储节点即数据实际映射的位置。计算结果为0 ~ 3 的数据存入节点 A结果为 4 ~ 6 的数据存入节点 B结果为 7 ~ 8 的数据存入节点 C计算为 9 的数据存储节点 D。</p>
<p><img src="assets/Cip5yF_-ebmAAIvTAAOI5nij8GY653.png" alt="13.png" /></p>
<p><img src="assets/Cip5yF_-ebmAAIvTAAOI5nij8GY653.png" alt="png" /></p>
<p>商品一致性Hash存储</p>
<p>当我们新增一台服务器,即节点 E 时,受影响的数据仅仅是新服务器到所处环空间中前一台服务器(即沿着逆时针方向的第一台服务器)之间的数据。结合我们的示例,只有商品 100 和商品 101 从节点 A 被移动到节点 E其他节点的数据保持不变。此后节点 A 只存储 Hash 值为 2 和 3 的商品,节点 E 存储 Hash 值为 0 和 1 的商品。
<img src="assets/Cip5yF_-ecOAOYQuAAH1dFZCfd0612.png" alt="14.png" /></p>
<img src="assets/Cip5yF_-ecOAOYQuAAH1dFZCfd0612.png" alt="png" /></p>
<p>商品数据迁移</p>
<p>一致性 Hash 分片的优点是数据可以较为均匀地分配到各节点,其并发写入性能表现也不错。如果你应聘的是初级研发工程师,面试官通常不会追问下去,但是应聘中高级别研发的话,这样的回答还不够,你还要进一步阐述对分布式数据存储的理解。</p>
<p>要知道,虽然一致性 Hash 提升了稳定性,使节点的加入和退出不会造成大规模的数据迁移,但本质上 Hash 分片是一种静态的分片方式,必须要提前设定分片的最大规模,<strong>而且无法避免单一热点问题,</strong> 某一数据被海量并发请求后,不论如何进行 Hash数据也只能存在一个节点上这势必会带来热点请求问题。比如案例中的电商商品如果某些商品卖得非常火爆通过 Hash 分片的方式很难针对热点商品做单独的架构设计。</p>
<p>所以,如果面试官想深入考核你对分布式数据存储的架构设计,一般会追问你:<strong>如何解决单一热点问题?</strong></p>
<p><strong>答案是做 Range范围分片。</strong> 与 Hash 分片不同的是Range 分片能结合业务逻辑规则,例如,我们用 “Category商品类目” 作为关键字进行分片时,不是以统一的商品一级类目为标准,而是可以按照一、二、三级类目进行灵活分片。例如,对于京东强势的 3C 品类,可以按照 3C 的三级品类设置分片;对于弱势品类,可以先按照一级品类进行分片,这样会让分片间的数据更加平衡。</p>
<p><img src="assets/CgpVE1_-ed6AfUBMAAFtDc6PlH4881.png" alt="15.png" /></p>
<p><img src="assets/CgpVE1_-ed6AfUBMAAFtDc6PlH4881.png" alt="png" /></p>
<p>按业务品类分片</p>
<p>要达到这种灵活性,前提是要有能力控制数据流向哪个分区,一个简单的实现方式是:预先设定主键的生成规则,根据规则进行数据的分片路由,但这种方式会侵入商品各条线主数据的业务规则,<strong>更好的方式是基于分片元数据</strong>(不过架构设计没有好坏,只有适合与否,所以在面试场景中,我建议你用擅长的解决方案来回答问题)。</p>
<p>基于分片元数据的方式,就是调用端在操作数据的时候,先问一下分片元数据系统数据在哪,然后在根据得到的地址操作数据。元数据中存储的是数据分片信息,分片信息就是数据分布情况。在一个分布式存储系统中,承担数据调度功能的节点是分片元数据,当客户端收到请求后,会请求分片元数据服务,获取分片对应的实际节点地址,才能访问真正的数据。而请求分片元数据获取的信息也不仅仅只有数据分片信息,还包括数据量、读写 QPS 和分片副本的健康状态等。</p>
@@ -228,7 +228,7 @@ function hide_canvas() {
<li>每个数据存储实例节点定时向元数据服务集群同步心跳和分片信息。</li>
<li>当调用端的请求过来时,元数据服务节点只需要做好高可用和缓存即可。</li>
</ol>
<p><img src="assets/Cip5yF_-eeuAR6ptAAGr8tkm4aA309.png" alt="16.png" /></p>
<p><img src="assets/Cip5yF_-eeuAR6ptAAGr8tkm4aA309.png" alt="png" /></p>
<p>元数据分片</p>
<p>掌握了这些知识后,你基本可以应对大多数公司对于研发工程师在数据架构设计上考点了,但如果面试官想挖掘你的能力,还会深入聊到共识算法,在一致性共识算法和最终一致性共识算法方面提出类似的问题,<strong>比如, ETCD 是如何解决数据共识问题的</strong><strong>为什么要选择这种数据复制方式呢</strong></p>
<p>对于这类问题,你要从一致性算法原理层面解答,思路是:清楚 ETCD 的共识算法是什么,还有哪些常用的共识算法,以及为什么 ETCD 会做这样的选型。</p>
@@ -244,10 +244,10 @@ function hide_canvas() {
<p>如果把问题设计的极端一些,考察你对最终一致性算法的掌握,还可以有一种思路:分片元数据服务毕竟是一个中心化的设计思路,而且基于强一致性的共识机制还是可能存在性能的问题,有没有更好的架构思路呢?</p>
<p>既然要解决可用性的问题,根据 Base 理论,需要实现最终一致性,那么 Raft 算法就不适用了,因为 Raft 需要保证大多数节点正常运行后才能运行。这个时候,可以选择基于 Gossip 协议的实现方式。</p>
<p>Gossip 的协议原理有一种传播机制叫谣言传播,指的是当一个节点有了新数据后,这个节点就变成了活跃状态,并周期性地向其他节点发送新数据,直到所有的节点都存储了该条数据。这种方式达成的数据一致性是 “最终一致性”,即执行数据更新操作后,经过一定的时间,集群内各个节点所存储的数据最终会达成一致,很适合动态变化的分布式系统。</p>
<p><img src="assets/Ciqc1F_-ehOAYwr5AADJW3KSBTc125.png" alt="17.png" /></p>
<p><img src="assets/Ciqc1F_-ehOAYwr5AADJW3KSBTc125.png" alt="png" /></p>
<p>从图中你可以看到,节点 A 向节点 B、C 发送新数据,节点 B 收到新数据后,变成了活跃节点,然后节点 B 向节点 C、D 发送新数据。</p>
<p>到此,我们对一致性共识算法做个总结,共识算法的选择和数据副本数量的多少息息相关,如果副本少、参与共识的节点少,推荐采用广播方式,如 Paxos、Raft 等协议。如果副本多、参与共识的节点多,那就更适合采用 Gossip 这种最终一致性协议。</p>
<p><img src="assets/CgqCHl_-eiqASOXFAAD1S3fPmgw076.png" alt="Lark20210113-120243.png" /></p>
<p><img src="assets/CgqCHl_-eiqASOXFAAD1S3fPmgw076.png" alt="png" /></p>
<h3>总结</h3>
<p>总的来说,今天我通过电商场景下商品的存储设计,一步步延伸出了分布式系统的数据存储、分片,与数据一致性等分布式问题,它们包含了分布式系统知识体系中最基础的理论,也是最复杂的问题。今天这一讲,我强调这样几点:</p>
<ul>