听小董谝存储 四

我爱glt

目录

序章

第一章 与master交互获取路由

第二章 转发请求

第三章 确定读写顺序

3.1 正常情况下

3.2 搬迁合并分裂情况下

3.2.1 三种情况的解释

3.2.1 三种情况的具体方案

后续阅读资料


序章

前面几章介绍了存储系统整体的架构和master层的一些功能设计。这一节主要聊聊portal这一层的设计。

如下图一,portal是直接对client提供服务的,它的主要功能就是将用户的请求导入特定的particle上。

这一个大功能其实也可以进行拆解,具体为如下三部分:

1 与master交互,获取准确的路由

2 根据key找到对应的particle上的data shard

3在多副本的情况下确定读写顺序

                                              

                                                                   图一 存储系统整体架构

第一章 与master交互获取路由

小菜:老师,前面各种数据结构看得我头疼,你能不能在不提具体实现的情况下,把问题讲清楚。毕竟,我只是想了解存储系统大概是怎么设计的,而不是要真正地去设计一个系统。

老鸟:我想想哈。Master与protal的交互流程参见《听小董谝存储三》4.5节。Master发给portal的就是一个list,每个元素都是一个路由,数据结构就是你之前提到的【slotStart, slotEnd, particleIp1, particlePort1, particleIp2, particlePort2, particleIp3, particlePort3, volumeId, shardId】。然后以跳表的形式存储。(百度安全验证)跳表这块建议你看一下,这是一个很优秀的数据结构。在路由表里面,跳表里面元素的排序key就是单个路由的slotStart。当发生路由更新,就是单纯的跳表更新操作了,大家自己看看跳表就OK。

第二章 转发请求

  1. 先把请求的key进行hash,然后对100w取余,获取存放的槽位。
  2. 在跳表里面找到符合槽位的路由(第一个大于上面计算的槽位号的路由的前一个元素)。
  3. 取出那个路由对应的机器信息,然后发请求就完了。

小菜:那这个会不会和前面获取路由的时候发生读写冲突?

老鸟:会。

小菜:那咋办?

老鸟:你说呢?

小菜:加锁?

老鸟:加锁。

第三章 确定读写顺序

老鸟:小菜,现在每个数据都存储了3份,你觉得应该怎么读写呢?

小菜:那就给每个data shard都分配一个编号,例如1,2,3。写(包括删除)的时候就先写1号副本,如果成功再写2号,如果还成功就写3号副本。在三次写的过程中,任意一次失败都告知客户端写失败;只有三次都成功才告诉客户端写成功。读的时候就读3就OK了(为什么是3?而不是1)。

其实副本是分主从的,写的时候先并发写两个从,都成功之后,再写主。

读的时候就只读主。

3.1 正常情况下

老鸟:你的设计没有什么大问题。图二是我根据你的思路画的图,精简一下,你刚才的编号就换成主副的说法,1号就是主;2,3就是副。你觉得下面的图有什么问题么?

                                         

                                                                             图二 data shard的主从逻辑       

小菜:副只提供写服务(大部分情况),主既有写也有读。从上面来看,Particle A的压力有点高呀。我想想,不如把主副本分散到各个机器上,这样负载能均衡一些,如下图三。

                                 

                                                       图三 data shard的主从逻辑

老鸟:不错。           

小菜:我还有一个问题,假如张大爷使用咱们的系统。他先写入鸡蛋的价格是5块钱,写成功了。之后他又更新(更新与写入是一个接口)鸡蛋的价格是6块钱,但是写失败了,此后他再去读,那么读的价格到底是多少钱呢?

老鸟:你提了一个很好的问题。就你上面的案例,他有可能读到5块钱,也有可能读到6块钱。因为,如果他之前写的时候,写主副本就失败了(例如网络超时,请求压根就没有到particle),那么他读到的数据就是5块钱。如果他是写从副本的时候失败了(之前写主已经成功了),但是读的时候因为读的是主副本,那么他就读到六块钱。

小菜:那就是说在不区分错误码的情况下,如果写失败了,那么落地的数据到底是什么样的就是个未知状态了?

老鸟:是的,如果写失败,那么底层到底是个什么状态谁也不知道。就上面你说的例子,就算是网络超时,还分两种,一是发送请求的时候超时,二是写成功了,但是particle返回结果给portal的时候超时了。这种情况下,前者存储的数据还是5,后者存储的数据已经是6。

小菜:还有一个问题,如果遇到读写并发怎么办?

老鸟:所有的client都是通过一个sdk包来连接portal的。SDK会把key经过hash,绑定到某个portal上。这样保证了同一个key都是由一个portal来处理的。在同一个portal内部,请求是串行走的。这样也就规避了读写并发问题。

老鸟:另外咱们前面说的都只是正常情况下的读写流程,一旦发生搬迁分裂合并,情况会比较复杂。

3.2 搬迁合并分裂情况下

小菜:在前面几篇文章里,你就已经提到了搬迁分裂合并这3个词,具体是什么意思呢?能解释一下么。

3.2.1 三种情况的解释

老鸟:好的。先说搬迁吧。

  • 1 搬迁           

         我们知道数据是存放在3份particle上面的。而particle都是廉价的商用机,很有可能出现各种硬件故障,就算不是硬件故障,单纯断电断网,也会导致不可访问。那此时就只有两份副本可用了,2份副本肯定没有3份副本安全。所以就很有必要把数据从原来的2份副本里搬迁到新的3份副本里。如果做个类比就是张大爷卖鸡蛋,他就有一个账本,原本放在

         15号厂房的3号货架的15号货篮里,

         为了保证数据安全,咱们还把账本复制了两份分别放在

         16号厂房的3号货架的15号货篮里,

         17号厂房的3号货架的15号货篮里。

         某天15号厂房整体都失火了,那咱们就得从16号厂房(或者17号厂房)3号货架的15号货篮里把账本再复制三份,分别存放到安全的

         21号厂房的4号货架的8号货篮,

         22号厂房的4号货架的8号货篮,

         23号厂房的4号货架的8号货篮里。

         这个过程就叫搬迁。

  • 2 分裂

         依然用张大爷记账来说。张大爷的业务做得很广,每天都有业务往来。现在是2个账本:

         一个放在25号厂房的4号货架的9号货篮,

         一个放在30号厂房的7号货架的10号货篮(为了叙述方便,暂时不考虑三副本的问题)。

         同时咱们制定了保存规则,每个月前半月的流水记录到9号货篮里,后半个月的流水记录到10号货篮里。

         后来张大爷的业务狂飙式发展,两个账本压根就不够用,我们就制定了新的规则:

         每个月的第一周的流水存放到22号厂房的4号货架的11号货篮

         每个月的第二周的流水存放到22号厂房的4号货架的12号货篮

         每个月的第三周的流水存放到22号厂房的4号货架的13号货篮

         每个月的第四周的流水存放到22号厂房的4号货架的14号货篮

         这个过程就叫分裂。

  • 3 合并

         首先一个大前提:咱们每个货架上的货篮数量是有限的。

         张大爷后面遭遇禽流感,生意不行了,生意流水大大减少,依然占据了4个货篮,这自然是不合适的。所以我们又把4个货篮减少到3个,记账规则就是:

         每个月的第一周放到22号厂房的4号货架的15号货篮

         每个月的第二周放到22号厂房的4号货架的16号货篮

         每个月的第三第四周放到22号厂房的4号货架的17号货篮

         这就是合并

数据发生搬迁分裂合并的概率很高,但是我们得有一个前提,不能因为我们要对数据进行上述操作就要求客户停止读写!所以这里我们要讨论的就是,如何在用户无感知地进行读写的情况下对数据搬迁分裂合并。另外就如上面举的例子,我们进行操作的最小单元就是一个货篮,也就是我们存储系统里面的一个data shard。OK,小菜,说说你的思路吧。

3.2.1 三种情况的具体方案

  • 1 搬迁    

        小菜:一个一个来,先说搬迁。从阶段来说,应该有搬迁中,搬迁完成后两个状态。当进入搬迁过程中,portal会设置一个标志位,此时新来的数据应该是同时写到两组particle(一共6台机器)一个叫新机器,一个叫旧机器。读的时候,只能读旧数据(具体来说就是读旧数据里面的主那一份),因为数据还没有搬迁完,所以有些数据新机器那里还没有。等到搬迁完成,portal的读写操作就都导向新机器。就机器上的数据就等待回收。简略的说就是搬迁中:双写读旧;搬迁完成:读新写新。整体来说这个阶段可以理解为把所有数据分为两部分,历史数据与新增数据。历史数据由mover进行搬迁,新增数据由portal进行双写。不过portal怎么知道开始搬迁了,什么时候搬迁完成呢?谁来执行搬迁呢?具体怎么搬迁呢?

                                          

                                                                                    图四 data shard的搬迁

              

老鸟:什么时候搬迁,什么时候搬迁完成这两个问题都是由dispatcher告知master的,master收到通知后,会修改自己的路由,然后推给所有的portal。这样portal就知道这阶段变化了。这部分在后面讲dispatcher和mover的时候咱们再详细说。

  • 2 分裂

                                            

                                                                                图五 data shard的分裂          

小菜:和搬迁对着看,分裂也就不复杂了。如上图五,分裂之前,槽位1-9的数据都写入某一个data shard A;开始分裂后,槽位1-4新收到的数据写入data shard B和原来的data shard A;槽位5-9的数据写入data shard C 和原来的data shard A。而只要分裂没有结束,所有的读都走到原来的data shard A。

  • 3 合并

                              

                                                                                图六 data shard的合并

小菜:看完分裂,合并我就不说了。大家看看图六就懂了。嗯不过,就以搬迁为例,我们原来的数据在data shard A,搬迁完成后,数据在data shard B。那data shard B到底在哪?

老鸟:这个由master决定。既然你你问到这里,那我就把dispatcher和mover的功能也简单说说吧。一个典型的搬迁流程如下:

  1. 某些组件(至于都是哪些组件,请参考听小董谝存储三,4.3怎么知道路由变更了)发现某个particle出现问题了,就上报给master了。          
  2. master讲这个机器上所有的data shard的状态都改为Dead。
  3. dispatcher发现有dead的data shard了就开始制定搬迁计划,并发给mover
  4. mover在开始搬迁前,先通知master,请求搬迁了。
  5. master收到mover的通知后就修改本地的路由,然后告诉mover该把数据搬迁到哪里,并推送路由给所有的portal。
  6. portal收到master新的路由消息后,设置一个特殊的标志位,此后收到的请求都会按照前文说的读写策略进行。
  7. mover一直进行数据搬迁,等数据搬迁完成后再通知master和dispatcher。
  8. master收到搬迁完成的消息后,修改自己的路由,再推送给protal。
  9. portal收到新的路由后,删除之前那个标志位,搬迁结束,数据的读写都由新的data shard承接。

小菜:和我想的差不多,但是mover搬迁的时候,怎么做到不覆盖已经写入的数据呢,还有他是怎么知道历史数据搬迁完了呢?

老鸟:别着急,后面讲mover的时候再说。我乏了。下课

后续阅读资料

跳表相关:

百度安全验证

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值