mit6.824lab4-4A

实验简介

这个实验我感觉比前三个,更难理解。最后是根据下面的这段话,才懂得。

Sworduo(http://sworduo.net/2019/08/16/MIT6-824-lab4-shardKV/#more):

Lab2和Lab3构成基础分布式数据库的框架,实现多节点间的数据一致性,支持增删查改,数据同步和快照保存。然而,在实际应用中,当数据增长到一定程度时,若仍然使用单一集群服务所有数据,将会造成大量访问挤压到leader上,增加集群压力,延长请求响应时间。这是由于lab2和lab3所构成的分布式数据库的核心在于分布式缓存数据,确保在leader宕机后,集群仍然可用,并没有考虑集群负载问题,每时每刻,所有数据请求都集中在一台机器上,显而易见的,在数据访问高峰期,请求队列将会无比漫长,客户等待时间也会变长。一个非常直接的解决方法,就是将数据按照某种方式分开存储到不同的集群上,将不同的请求引流到不同的集群,降低单一集群的压力,提供更为高效、更为健壮的服务。

Lab4就是要实现分库分表,将不同的数据划分到不同的集群上,保证相应数据请求引流到对应的集群。这里,将互不相交并且合力组成完整数据库的每一个数据库子集称为shard。在同一阶段中,shard与集群的对应关系称为配置,随着时间的推移,新集群的加入或者现有集群的离去,shard需要在不同集群之中进行迁移,如何处理好配置更新时shard的移动,是lab4的主要挑战。

一个集群只有Leader才能服务,系统的性能与集群的数量成正比。lab3是一个集群,lab4A要实现的是多个集群之间的配合。

参考:

  • https://www.jianshu.com/p/6e8d33c3c799

4A

First you’ll implement the shard master, in shardmaster/server.go and client.go.

// 数据库子集和集群之间的关系
type Config struct {
    // config的版本号
	Num    int              
    // shards[0] = g0:代表数据库子集[0]由集群g0负责
	Shards [NShards]int     // shard -> gid
	Groups map[int][]string // gid -> servers[]
}

CLient

Client的实现跟3A一样。

Server

逻辑和3A也是一样。每一次变化,都要新增一个Config。

  • Join:把这些GID 组,加到MASTER的管理范围里来。
  • Leave:移除几个集群。
  • Query:查询具体的Config
  • Move:指定某个数据库子集归哪个集群管。

注意

如果你的Op中包含自定义字段,请在lagob中注册。因为Raft需要把我们Start的命令追加到日志中,然后Leader通过RPC发送追加日志请求给Follower。需添加如下代码:

labgob.Register(Op{})
labgob.Register(JoinArgs{})
labgob.Register(LeaveArgs{})
labgob.Register(MoveArgs{})
labgob.Register(QueryArgs{})

Rebalance

测试是通过查看Config.Shards来判断。

因为有集群的加入和离去,就需要重新调整数据库子集和集群之间的对应关系。一个Shared只能有一个Group管理,而一个Group可以管理多个Shared。我的思路是使用最大堆和最小堆。

type ShardMaster struct {
	mu      sync.Mutex
	me      int
	rf      *raft.Raft
	applyCh chan raft.ApplyMsg
	// Your data here.
	configs     []Config // indexed by config num
	...
	shardCount []Relation
}

type Relation struct {
	Gid     int
	Shards []int // one group 管理 哪几个shard
}

有新集群加入

从多的几个组中,分一点过来。因为懒得实现最大堆和最小堆,就直接用了暴力排序

avg := NShards / len(cfg.Groups)
relation := Relation{
    Gid:     gid,
    Shards: make([]int, 0),
}
i := 0
for ; i < avg && len(sm.shardCount) != 0; i++ {
    // 从大到小排序,每次选出最大的
    sort.Sort(MoreByCount(sm.shardCount))
    sharedNo := sm.shardCount[0].Shards[0]
	// update `sm.shardCount`:从最多的中,移除一个Shard
    if len(sm.shardCount[0].Shards) == 1 {
        sm.shardCount[0].Shards = sm.shardCount[0].Shards[:0]
    } else {
        sm.shardCount[0].Shards = sm.shardCount[0].Shards[1:]
    }
    // 把这个shard分给新的group
    relation.Shards = append(relation.Shards, sharedNo)
    // update `Config.Shards`
    cfg.Shards[sharedNo] = gid
}
if len(sm.shardCount) == 0 {
    for i := 0; i<NShards; i++ {
        relation.Shards = append(relation.Shards, i)
        cfg.Shards[i] = gid
    }
}
sm.shardCount = append(sm.shardCount, relation)

有集群离去

划分给Shared少的几个组

// 这个gid拥有哪几个shared
var shards []int
for i, _ := range sm.shardCount {
    if sm.shardCount[i].Gid == gid {
        shards = sm.shardCount[i].Shards
        // update sm.shardCount
        if i == len(sm.shardCount) -1 {
            sm.shardCount = sm.shardCount[:i]
        } else {
            sm.shardCount = append(sm.shardCount[:i], sm.shardCount[i+1:]...)
        }
        break
    }
}
// 一个集群也没有了
if len(sm.shardCount) == 0 {
    cfg.Shards = [NShards]int{}
    return
}
// 分配这几个shards
for _, s := range shards {
    // 从小到大排序,每次选出最大的
    sort.Sort(LessByCount(sm.shardCount))
    // 分区数最小的集群 add a shard
    sm.shardCount[0].Shards = append(sm.shardCount[0].Shards, s)
    // update `Config.Shards`
    cfg.Shards[s] = sm.shardCount[0].Gid
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值