专栏首页DDD百万QPS系统的缓存实践

百万QPS系统的缓存实践

标题有些吸引眼球了,但并不浮夸,甚至还会远远超过百万,现在的平均响应时间在1ms内,0.08ms左右

如此高的QPS,如此低的AVG,为什么会有如此效果,关键点可能就在多级缓存上

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流

概述

上图基本上就是查询的通用方案,缓存中是否存在,存在就返回,不存在再查询Db,查询到的结果load进缓存

实践

缓存,逃不过三种操作,创建、查询、删除

此实践可能不保证全场景通用,但满足当前系统各项指标,当然没有完美的方案,只有适合的方案。

下面的时序图中,cache lv1是指本地缓存,cache lv2是cache cluster

查询

查询过程:

从一级缓存开始查,如果没有,再向下一级查询,直到db

注意点:

  1. 一直查到db时,需要回源各级cache
  2. 防止击穿,需要在cache中填充value

创建

创建过程:

  1. 创建cacheObject
  2. 放入Db(为了性能,以及db的降级,这儿可以引入异步开关)
  3. 放入cache lv2
  4. 放入cache lv1
  5. publish创建成功消息
  6. 消息监听服务会通知其它服务更新本地缓存

注意点:

  1. 到底是先放入Db,还是先放入cache
  2. db与cache的一致性保障

删除

删除过程:

  1. 通过key查询cacheobject
  2. 清除db
  3. 清除各级cache
  4. publish消除成功消息
  5. 监听服务清除其它服务的本地缓存

注意点:

  1. 先清除db还是cache
  2. Db与cache的一致性保障

缓存操作模式

除了创建,查询,删除,还有更新操作;但我们业务场景没有。

对于我们的实践是不是放之四海而皆准,肯定是不行的。不以业务为基础的设计都是无根之木

先看下业界常见的操作缓存模式

更新缓存的的Design Pattern有四种:Cache aside, Read through, Write through, Write behind caching

Cache aside

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。为什么不是写完数据库后更新缓存?你可以看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕两个并发的写操作导致脏数据。

那么,是不是Cache Aside这个就不会有并发问题了?不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必须在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

Cache Aside,我们的应用代码需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。所以,应用程序比较啰嗦。而Read/Write Through套路是把更新数据库(Repository)的操作由缓存自己代理了,所以,对于应用层来说,就简单很多了。可以理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的Cache。

Read Through

Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

这似乎很像guave的LoadCache

Write Through

Write Through 套路和Read Through相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

Write Back

在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比(因为直接操作内存嘛 ),因为异步,write backg还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

在wikipedia上有一张write back的流程图,基本逻辑如下:

在游戏开发中基本上都是使用这种模式

但他也有缺点:

  1. 数据不是强一致性
  2. 数据可能会丢失
  3. 逻辑比较复杂

争论

  1. 一致性问题 这儿的一致性是说强一致性,在分布式环境下,保证强一致性促使系统复杂性增加,或者性能有所下降。所以现在一般对非强制性业务场景都使用最终一致性解决。一致性的解读可以看看《zookeeper-paxos》,在我们实践时,在删除操作时,在清理失败时也通过补偿操作去尝试清除。
  2. 到底是update cache,还是delete cache 其实任务技术手段都是看业务场景的,不能一概而论
    • update cache 这个在并发写时,A1写db,B1写db,B2写cache,A2写cache;这时就出现db与Cache不一致的问题 主动更新缓存,如果cacheobject复杂,需要Db与cache的多次交互,虽然减少了一次cache miss,但却增加了系统复杂度,得不偿失
    • delete cache 这个不会有不一致问题了,但会造成cache miss,会不会造成热key穿透?
  3. 是先操作Db,还是cache 假设先操作cache,再操作db;A B并发操作,A1 delete cache; B1 get cache --> miss --> select db --> load cache;A2 delete db; 此种情况就出现此key一直有效状态,如果没有设置超时时间,那会长期在缓存中。这是不是得先操作db呢? 一个操作先update db,再delte cache时失败了;那会数据库里是新数据,而缓存里是旧数据,业务无法接受。那是不是该先操作缓存呢?

是不是已经晕头了呢?

再有db主从架构中,主从不一致的情况,是不是没法玩了

所以还是开篇讲的没有放之四海而皆准的方案,只能寻找最适合的方案

在各种业务场景下,还是需要去寻找一些最佳实践,比如关注一下缓存过期策略、设置缓存过期时间

参考资料

缓存更新的套路

A beginner’s guide to Cache synchronization strategies

本文分享自微信公众号 - 码农戏码(coder-game),作者:朱兴生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一直再说高并发,多少QPS才算高并发?

    首先是无状态前端机器不足以承载请求流量,需要进行水平扩展,一般QPS是千级。 然后是关系型数据库无法承载读取或写入峰值,需要数据库横向扩展或引入nosql,一般...

    春哥大魔王
  • 为什么需要消息队列?使用消息队列有什么好处?

    业务无关,一个具有普适性质的消息队列组件不需要考虑上层的业务模型,只做好消息的分发就可以了,上层业务的不同模块反而需要依赖消息队列所定义的规范进行通信。

    芋道源码
  • 达达O2O后台架构演进实践:从0到4000高并发请求背后的努力

    达达创立于2014年5月,业务覆盖全国37个城市,拥有130万注册众包配送员,日均配送百万单,是全国领先的最后三公里物流配送平台。 达达的业务模式与滴滴以及Ub...

    JackJiang
  • 百亿流量系统,是如何从0开始搭建的?

    前几天,偶然看到了 《扛住100亿次请求——如何做一个“有把握”的春晚红包系统”》一文,看完以后,感慨良多,收益很多。正所谓他山之石,可以攻玉,虽然此文发表于2...

    Bug开发工程师
  • 后端架构设计,如何扛住100亿次请求?

    前几天,偶然看到了 《扛住100亿次请求——如何做一个“有把握”的春晚红包系统”》一文,看完以后,感慨良多,收益很多。正所谓他山之石,可以攻玉,虽然此文发表于2...

    好好学java
  • IM技术分享:万人群聊消息投递方案的思考和实践

    本文由融云技术团队原创分享,原题“技术实践丨万人群聊的消息分发控速方案”,为使文章更好理解,内容有修订。

    JackJiang
  • 个推消息中心如何实现多渠道消息智能下发?

    目前,各行业的数智化进程如火如荼,企业对数智化用户运营的需求日益旺盛;同时,在万物互联的5G时代,用户触达的渠道也变得更加丰富。企业需要更高效、智能的方式进行用...

    个推
  • 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践

    本文原题“百度直播消息服务架构实践”,由百度APP消息中台团队原创分享于“百度Geek说”公众号,为了让文章内容更通俗易懂,本次已做排版优化和内容重新划分,原文...

    JackJiang
  • 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践

    本文原题“百度直播消息服务架构实践”,由百度APP消息中台团队原创分享于“百度Geek说”公众号,为了让文章内容更通俗易懂,本次已做排版优化和内容重新划分,原文...

    JackJiang
  • 不知道怎么学高并发系统设计?和我一起做好这7点,你也能玩转高并发!

    我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能...

    Java程序猿阿谷
  • 技术 | 热数据探测技术及架构设计

    今天某公众号推送了一篇文章,标题为“京东的热点key探测系统发布,单机 QPS 提升至 37 万”。后台开发同学都知道,单机QPS 37万是什么概念,多么充满噱...

    程序员鱼皮
  • 腾讯云5位技术专家在上海 ArchSummit 峰会分享云原生、大数据计算等话题

    ? 关于 ArchSummit ArchSummit 全球架构师峰会是 InfoQ 中国团队推出的面向高端技术管理者、架构师的技术大会,54%参会者拥有8年...

    腾讯云原生
  • 到底什么级别才算是高并发?

    https://segmentfault.com/a/1190000010844969

    Java技术栈
  • 历经8年双11流量洗礼,淘宝开放平台如何攻克技术难关?

    淘宝开放平台(open.taobao.com)是阿里系统与外部系统通讯的最重要平台,每天承载百亿级的API调用,百亿级的消息推送,十亿级的数据同步,经历了8年双...

    Java高级架构
  • 记一次单机系统的性能优化:最后竟是 TCP 的锅

    这篇文章的主题是记录一次 Python 程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的。为大家提供一个优化的思路,首先要声明的一点是,我的方式不是唯...

    杰哥的IT之旅
  • 性能测试:记一次生产环境性能测试优化实践

    这篇文章的主题是记录一次程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的。

    测试开发技术
  • 记一次性能优化,单台4核8G机器支撑5万QPS

    这篇文章的主题是记录一次程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的。

    肉眼品世界
  • 记一次性能优化,单台4核8G机器支撑5万QPS

    这篇文章的主题是记录一次程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的。

    周辰晨
  • Web开发中说高并发的时候,我们在说什么

    大家先心里仔细想想,当你们听到高并发网站时,心里对这个网站是个什么概念?首先想到的是淘宝吗?带着问题,我们一起思考技术

    大愚

扫码关注云+社区

领取腾讯云代金券