服务器分布式加锁的设计

前言


为什么要加锁

当KV读写无法保持一致性,当一段操作需要保证互斥的时候,我们要怎么做呢?答案就是加锁!

基本原理

记录一个标志位,如果标志位存在则锁已存在,如果标志位不存在则设置标志位。

基于KV的设计(KV读写使用版本号保持一致性)


设计1

  • 加锁:检查 key 的标志位,如果不存在则写入
  • 解锁:将 key 中的标志位清除
  • 问题:万一解锁失败,将会导致死锁,用户再也无法进行相关的操作,维护困难

设计2

  • 原理:在设计1的基础上,再设置一个超时时间(默认5秒)。如果超时,则自动解锁
  • 死锁优化:如果解锁失败,最多对用户造成5秒的影响
  • 效率:KV的读写平均耗时3-5毫秒,加、解锁总共需要操作(读+写)4次KV,总共增加耗时10-20毫秒
  • 问题:我们使用的KV,数据会落地,将会导致很多的垃圾数据(目前影响不大)
  • 使用:
WG_KV_LOCK_AUTO("xxxxx"/*, 5 */);
  • 加锁、解锁伪代码:
int KVLock::TryLock(const std::string &key, uint64_t expire_time) {
    wg::kvlock::KVLockData lock_data;
    int ret = KStrMod(key, 1, [&](std::string &val) {
        uint64_t now = NowStamp();
        do {
            if (val.empty())
                break;

            if (!lock_data.ParseFromString(val))
                break;

            if (!IsExpired(now, lock_data.lock_time() + lock_data.expire_time()))
                return (int)ERR_KVLOCK_LOCKED;
        } while (0);

        lock_data.set_lock_time(now);
        lock_data.set_expire_time(expire_time);

        if (!lock_data.SerializeToString(&val))
            return (int)ERR_KVLOCK_PB_ENCODE;
        return 0;
    });
    return ret;
}
int KVLock::Unlock(const std::string &key) {
    wg::kvlock::KVLockData lock_data;
    int ret = KStrMod(key, 1, [&](std::string &val) {
        if (val.empty())
            return (int)ERR_KVLOCK_UNLOCKED;

        if (!lock_data.ParseFromString(val))
            return (int)ERR_KVLOCK_PB_DECODE;

        uint64_t now = NowStamp();
        if (IsExpired(now, lock_data.lock_time() + lock_data.expire_time()))
            return (int)ERR_KVLOCK_UNLOCKED;

        lock_data.set_lock_time(0);
        lock_data.set_expire_time(0);

        if (!lock_data.SerializeToString(&val))
            return (int)ERR_KVLOCK_PB_ENCODE;
        return 0;
    });
    return ret;
}

独立服务器的设计

设计

此设计是根据QT的locksvr逆推而成,对于locksvr我了解 这几点:1、可以加锁;2、单点系统。在此要感谢一下wardlei,帮我了解到locksvr的

  • 首先,这是一个纯内存的服务,在内存里记录标志位和超时时间
  • 还可以使用共享内存,工作进程
  • 内存结构使用多阶hash
  • 机器之间的路由规则使用一致性hash,进程之间使用取模

承载能力(以下并不严谨,欢迎指正)

  • 以100W容量,5秒超时为例(平均每秒20W);并假设key的长度限制为256B,消息的大小限制为1KB
  • 内存:(256B * 100w + 1KB * 20W * 2 =)不超过700MB;再加上进程二进制文件大小、页表的大小、socket系统缓冲区(socket复用,不会有太多)、每个key还有些控制信息等;2GB绰绰有余了
  • 网络:吞吐量(1KB * 20W * 2 =)400M
  • CPU:假设机器20核,每个核平均每秒处理1W

维护

  • 服务器足够简单,也会足够稳定
  • 机器间路由可以使用一致性hash,在机器挂掉时,踢掉挂掉的机器对用户造成的影响很小
  • 时间允许可以考虑加入主备或分布式一致性算法

最终选择

看到这里,相信大家已经知道了,最后采用的方案是基于KV的设计2。缺点不明显,开发简单,完全不需要维护(KV有专门的团队维护)。

转载于:https://my.oschina.net/distroy/blog/666073

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值