【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

缓存,Promies 和锁

  • 2020-03-01
  • 本文字数:2457 字

    阅读完需:约 8 分钟

缓存,Promies和锁

Instagram 最近在他们的软件工程博客发布了一篇有关缓存值 promise 的概念的帖子,在缓存值未被命中情况下,需要耗费一定时间从底层的数据库管理系统中获取到未命中的缓存值,但这可能会导致数据管理系统拥塞。此外,如果多个并发的请求在请求获取缓存中一个不存在的值,触发多个工作实例从底层数据库获取该值并填充到缓存中,会引起数据库的拥塞(参考下图)。



在帖子中,Instagram 的 NickCooper 展示一种信号通知机制,当缓存正在准备某个值时,可以向其他的对该值请求发送通知,所以其他请求暂停对该值获取,避免直接访问底层数据库。


这不是什么新方法(它是读写缓存的特性之一,因为其对数据库透明的,开箱即用),但这个方法值得好好讨论一下,在这篇文章中,我将分享展示该方法的一个简单应用,将读写缓存的更多优势给发挥出来。

Redis

在详细介绍我的方法应用之前,简单介绍下如何通过 redis 来实现的我的目标。


  1. 支持多种语言,应用客户端只需要一个 redis 客户端库和需要使用 Redis 的 key 的名称。可以在不同程序和网络边界之间,同其他的客户端生成/解析 promises。

  2. 集群模式下扩展更高效,不需要通过轮询或者其他低效的模式,Redis 在此更能体现其独特的优势。

  3. 在避免无效的工作和保持可伸缩性之间有效地权衡。作为分布式系统的一部分,更强有力的保证需要更多的协作。


我的应用方案主要依赖 redis 的三个特性:key 过期(TTL),原子操作(SETNX)和 Pub/Sub;一般来说,我只是很好的利用了我在前面一篇帖子(https://redislabs.com/blog/what-to-choose-for-your-synchronous-and-asynchronous-communication-needs-redis-streams-redis-pub-sub-kafka-etc-best-approaches-synchronous-asynchronous-communication/)中提到的原则:共享状态有益于协作,反之亦然。


Redis 很适合用于状态共享,使用 Pub/Sub 消息机制实现一个锁,帮我构建一个跨网络 promise 机制。

内存锁的介绍

接下来,我将介绍 redis 内存锁模式的实现,它的工作模式如下:


  1. 一个服务应用实例,需要从 redis 中获取 key 值 foo, 如果获取到,就直接返回。

  2. 如果 foo 这个 key 值不存在,可以通过 SET NX 创建一个 key 值 lock:foo,NX 参数确保如果存在通过并发设置请求,只有一个请求能够设置键值成功(key 的 value 值在此处不重要)。

  3. 3.如果这个 key 值在缓存存在,我们获取到其对应的 value 值,完成后,接下来将其保存到 redis 中,并在叫做 notif:foo 的 Pub/Sub 通道中发布一条消息,通知所有等待该值的客户端,可以从缓存中获取到该值。

  4. 4.如果不能获取到对应的锁,此时只需订阅到 notif:foo 的 Pub/Sub 通道,等待被通知对应的缓存值已经存在。

  5. 实际上,这个算法稍微有点复杂,因此我们能很好的处理并发和超时(无过期的锁/Promises 在分布式环境中是无用的)。我们的命名的稍微有点复杂,因为需要为每个资源指定一个一个名称空间,以便多个独立的服务使用同一个集群时,不会面临键名冲突的风险,除此之外,解决这类问题不存在复杂性。

代码

你可以从 github 上获取相关示例代码(https://github.com/kristoff-it/redis-memolock),示例代码是基于 Go 语言实现的。


package main
import ( "fmt" "time" "github.com/go-redis/redis" "github.com/kristoff-it/redis-memolock/go/memolock")
func main () { // First, we need a redis connection pool: r := redis.NewClient(&redis.Options{ Addr: "localhost:6379", // use default Addr Password: "", // no password set DB: 0, // use default DB })
// A memolock instance handles multiple resources of the same type, // all united by the same tag name, which will be then used as a key // prefix in Redis. queryResourceTag := "likes" queryMemoLock, _ := memolock.NewRedisMemoLock(r, queryResourceTag, 5 * time.Second) // This instance has a 5 second default lock timeout: // Later in the code you can use the memolock to cache the result of a function and // make sure that multiple requests don't cause a stampede.
// Here I'm requesting a queryset (saved in Redis as a String) and providing // a function that can be used if the value needs to be generated: resourceID := "kristoff" requestTimeout := 10 * time.Second cachedQueryset, _ := queryMemoLock.GetResource(resourceID, requestTimeout, func () (string, time.Duration, error) { fmt.Println("Cache miss!\n") // Sleeping to simulate work. <- time.After(2 * time.Second)
result := fmt.Sprintf(`{"user":"%s", "likes": ["redis"]}`, resourceID) // The function will return a value, a cache time-to-live, and an error. // If the error is not nil, it will be returned to you by GetResource() return result, 5 * time.Second, nil }, )
fmt.Println(cachedQueryset) fmt.Println("Launch the script multiple times, see what changes. Use redis-cli to see what happens in Redis.")}
复制代码


如果你在 5S 中内运行该程序的两个实例,无论第一个实例是否运行结束(即,参数值已被缓存),还是在继续运行,“Cachemiss!”只会显示一次。

为什么这比其他解决方案更好?

两大原因:


  1. MemoLock 不仅可以保护 DBMS,还可以保护其他的昂贵的资源。

  2. 即使我们只想限制查询集缓存,CQRS 也会告诉我们,数据存储在 DBMS 中对制定的查询不一定是最有用的数据格式。对查询数据进行必要的转换应该是业务逻辑的一部分,除非在必要的条件下,对通过存储过程对每个请求都要重新转换下数据。


本文转载自 中间件小哥 公众号。


原文链接:https://mp.weixin.qq.com/s/WhFVFTHKNEIt9Jec34rB0A


2020-03-01 21:43554

评论

发布
暂无评论
发现更多内容

企业搭建知识库的重要性,你了解多少?

Geek_da0866

华为云会议智能会议室助力财通证券加速数字化

科技怪咖

微信小程序开发速览

乌龟哥哥

8月月更

Kubernetes 核心组件

CTO技术共享

开源 签约计划第三季

AIRIOT答疑第9期|AIRIOT平台服务于哪些客户?

AIRIOT

低代码 物联网 低代码,项目开发

如何用紧凑型语音表征打造高性能语音合成系统

小红书技术REDtech

深度学习 语音合成 自编码器 表征学习 语音表征

3000人无缝连接,WeLink助力华荣科技全场景数字化办公

神奇视野

华为云桌面说“高清”的时候,究竟在说什么

科技云未来

政企视频会议首选,华为云OneMeeting全场景视频会议解决方案正式发布

sofiya

金融和科创深化合作,常熟农商银行与博云签署战略合作协议

BoCloud博云

云计算 容器 云原生

2022各互联网大厂面经及总结+大厂Java岗面试真题解析(进大厂必看攻略)

程序知音

Java 程序员 Java 面试 后端技术 八股文

华为云桌面说“流畅”的时候,究竟在说什么

科技云未来

Kubernetes Nginx配置热加载

CTO技术共享

开源 签约计划第三季

​五大不良 coding 习惯,你占了几样?

SEAL安全

Code 代码规范 CODING

网红50万卖微信号被判交易无效:如何监管互联网账号交易市场

石头IT视角

游族马寅龙:常见信息安全风险及应对方案

声网

网络安全 创业讲堂

RocketMQ高可用设计之异步刷盘

急需上岸的小谢

8月月更

IPv6中的隧道技术

穿过生命散发芬芳

ipv6 8月月更 隧道技术

在Rust和C之间传递字符串,有 7 种方法!

非凸科技

c rust

创建知识库使您的客户能够体验自助服务

Geek_da0866

用户权限-Linux系统用户管理

Albert Edison

Linux centos 运维 用户权限 8月月更

Kubernetes服务的注册与发现

CTO技术共享

签约计划第三季

华为云会议,用高效联接推进工业企业数字化转型

sofiya

沟通不受限,审批不堆积 深大智能通过华为云WeLink+OA实现办公提速

神奇视野

每日一R「10」数据结构(一)智能指针

Samson

8月日更 ​Rust

华为云安全云脑,让企业云化运营更放心

IT资讯搬运工

华为云主机安全助力企业种出“金葫芦”

IT资讯搬运工

沉浸感拉满!这样的电影杀约起来

科技云未来

华为发布IdeaHub S2系列,与华为云会议结合更强大!

科技怪咖

2022 Gartner RPA魔力象限,弘玑Cyclone位置飞跃国产RPA进击全球

王吉伟频道

RPA 机器人流程自动化 Gartner RPA魔力象限 弘玑Cyclone

ITIL服务支持5个运营级流程简单介绍

阿泽🧸

ITIL 8月月更

缓存,Promies和锁_行业深度_翻译自redis.io_InfoQ精选文章