技术

《拒绝最终一致性的脏读陷阱: Umem-cache 强一致性实现原理》

原文:consistency.rst
缓存一致性是 Umem-cache 最关键的特性,缓存协议和集群的设计方案都是围绕着它展开的。缓存一致性能减少软件开发中的复杂性,因为你不需要担心过时的数据,这是最终一致性做不到的。
选择缓存一致性是有代价的:我们牺牲了可用性。也就是一旦 Umem-cache 挂掉,即使你的其他服务依旧完好,整个系统也无法工作。但是不必担心,我们有基于 raft 共识算法的集群解决方案,它可以提供很高的容错能力。
缓存协议

我们的缓存协议提供了 GetOrSet 和 Delete 两个命令,是的, 我们把 Get 和 Set 合并了。
缓存一致性的实现是靠在数据库中的 Key 更新完成的瞬间,以下事件确保发生:

  • 没有任何连接正在读取 Key
  • 所有在此之前读到 Key 且未完成 Set 的连接都已被关闭
  • Umem-cache 中不存在该 Key
下面我将详细说明:
缓存命中

缓存命中时,Umem-cache 直接返回数据。
但是如果 key 因为正在 set 而被锁住了,该 get 操作会给该锁设置上过期时间,并排队等待 value 。另外,如果锁到期了,持有锁的连接会被关闭,并把锁转交给排队中的下一个连接。
::
客户端                  Umem-cache                  数据库           get key|------------------------->|                           | 1. 等待锁释放           返回 value        ||<-------------------------|缓存未命中

当缓存未命中时,我们会锁住 key ,然后尝试 set ,其他 get 请求会排队等待,这种机制是为了防止缓存击穿。
你可能发现了,Umem-cache 不响应 Set ,这让我们省掉了一次网络传输。因为它大概率会成功,就算失败也不是什么大事。
::
客户端                  Umem-cache                  数据库           get key|------------------------->|                           | 1. 锁住 key           返回 miss         ||<-------------------------|                       get key|--------------------------------------------------->|                                                     | 2. 等待读锁                       返回 value                      ||<---------------------------------------------------||------------------------->| 3. set value                             4. 释放 key 的锁更新或删除

事务和锁能保证数据库在更新完成时,该 key 未被缓存,且不在缓存的路上。
将第 4 步放在事务的最后是最佳实践,因为如果该 key 已被缓存,那么在此之前,所有的 Get 都会命中,而不是在队列中等待。
::
客户端                Umem-cache               数据库|-------------------------------------------------->| 1. 开启事务                                                    | 2. 获取 key 的写锁|<--------------------------------------------------| 3. 更新或删除 key      删除 key|-------------------->|        4. 删除 key 或关闭持有 key 锁的连接|<--------------------||-------------------------------------------------->| 5. 提交事务集群

集群的负载均衡靠的是客户端散列,在集群模式下,每个 key 都携带了 8 字节的版本号前缀,这个版本号与它要散列到的成员的版本号一致。一旦 key 散列到的成员发生变化,对应成员的版本号会增长,这个版本号是由 Raft 共识协议来维护的,并且我们保证了所有成员的版本号都不同。所以一旦 key 散列到的成员发生变化,旧成员上的 key 就会因为版本号的变化自动失效,这个机制就保证了同一时间不可能有两个成员上存在相同的有效的 key 。
集群也有一个版本号,这个版本号可以用来快速判断集群是否发生变化,这个版本号也是由 Raft 共识协议来维护的。每次集群成员变更或者成员可用状态变更都会导致集群版本号增长。所以集群版本号的增长也代表着集群中 Key 的散列分布发生了变化。
我们的集群协议提供了 Leader ,Authority 和 Cluster 三个命令,每一个缓存命令都会向集群获取执行授权,这个授权通过 Authority 命令来执行,Authority 命令会返回当前集群的版本号。
下面是客户端实现流程:
::
                            -----                            |开始|                            -----                              |                              v                  -------------------------                  |随机选择服务器查询领导者信息|<----------                  -------------------------          |                              |                      |                              v                      |             ----------------------------------      |             |向领导者查询集群信息,得到 version V1|      |             ----------------------------------      |                              |                      |                 ---------------------------         |                 |           并行           |         |                 v                         v         | ----------------------------------      -----       | |向领导者请求认证权威,得到 version V2|      |缓存|       | ----------------------------------      -----       |                 |                         |         |                 ---------------------------         |                              |                      |                              v                      |                             / \                     |                            /   \                    |                           /     \                   |                          /V1==V2 \                  |                          \       /--------否---------                           \     /                            \   /                             \ /                              |                              是                              |                              v                            -----                            |结束|                            -----

⚠️内容来自互联网,如有侵权请点击反馈!侵权投诉
本帖共 1 条回复,登录后即可查看

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

hot.热文换一批
  1. 宇宙到底是大还是小?一张纸对折103次的厚度,宇宙已装不下
  2. 别眨眼!人类首次"看见"光子!你家灯泡里全是它!
  3. 人类至今无法彻底解释的15个顶级现象,看完敬畏万物
  4. 二十出头就显成熟?揭秘上一代人的"大人模样"背后原因
  5. 明明含有氢和氧,氢可燃氧助燃,为什么水偏偏不能燃烧?
  6. 🎉庆祝应用中心上架15个插件了,抽波优惠券![开奖时间:2026-04-26 20:00]
  7. 宇宙和大脑很相似?科学家新理论惊人,难道我们是活在生物体内?
  8. 人类已发现外星生命?科学家:就在12亿公里外,可能以甲烷为食
  9. 又一个新游戏上线了,欢迎体验!
  10. 自来水微塑料3000万/升,煮沸后降到480万,比桶装水高还是低?
  11. 女装退货率飙至80%,商家为何挂密码锁发货?
  12. 现在这金价是不是还有余地
  13. 世界上最凶猛的10种鸟,金雕到底有多厉害?猎杀野狼轻而易举?
  14. 古人为何50岁就百病缠身?揭秘1972年7月30日“马王堆女尸”出土之谜
  15. 中国真挖到“宝”了!海水里藏45亿吨铀,美国70年难题被突破
  16. 太阳和银河系之间,还有一个神秘结构?
  17. 科学家:在人类出生前,有两种神秘的力量帮助构建了人脑
  18. 下潜到1万多米的海底,人类发现了不该出现的东西?需要引起警惕
  19. 古人到底能有多苦?这么说吧,现代人在古人面前不值一提
  20. 🌱牛马论坛-每日早报-2026年10月08日
  21. 终于装上了LightSNS,然后模块一通买,还是花了上千
  22. 吃一块能顶一天?军用压缩饼干为啥抗饿?
  23. 小时候父母不让玩手机的秘密,网友亲测:经历过才懂!
  24. 测试
牛马论坛(niumabbs.com),一个专注于分享日常新鲜事、职场工作点滴、摸鱼乐趣与各类生活感悟的交流平台。

    关注我们

  • 加入Q群
  • 微信客服
  • QQ客服
Copyright © 2026 NIUMABBS 版权所有 All Rights Reserved. |网站地图 劰载中...
关灯 在本版发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表