前言
为什么要加锁
当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有专门的团队维护)。