左耳听风
陈皓
网名“左耳朵耗子”,资深技术专家,骨灰级程序员
立即订阅
40357 人已学习
课程目录
已完结 108 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 | 洞悉技术的本质,享受科技的乐趣
免费
01 | 程序员如何用技术变现(上)
02 | 程序员如何用技术变现(下)
03 | Equifax信息泄露始末
04 | 从Equifax信息泄露看数据安全
05 | 何为技术领导力?
06 | 如何才能拥有技术领导力?
07 | 推荐阅读:每个程序员都该知道的知识
08 | Go语言,Docker和新技术
09 | 答疑解惑:渴望、热情和选择
10 | 如何成为一个大家愿意追随的Leader?
11 | 程序中的错误处理:错误返回码和异常捕捉
12 | 程序中的错误处理:异步编程以及我的最佳实践
13 | 魔数 0x5f3759df
14 | 推荐阅读:机器学习101
15 | 时间管理:同扭曲时间的事儿抗争
16 | 时间管理:如何利用好自己的时间?
17 | 故障处理最佳实践:应对故障
18 | 故障处理最佳实践:故障改进
19 | 答疑解惑:我们应该能够识别的表象和本质
20 | Git协同工作流,你该怎么选?
21 | 分布式系统架构的冰与火
22 | 从亚马逊的实践,谈分布式系统的难点
23 | 分布式系统的技术栈
24 | 分布式系统关键技术:全栈监控
25 | 分布式系统关键技术:服务调度
26 | 分布式系统关键技术:流量与数据调度
27 | 洞悉PaaS平台的本质
28 | 推荐阅读:分布式系统架构经典资料
29 | 推荐阅读:分布式数据调度相关论文
30 | 编程范式游记(1)- 起源
31 | 编程范式游记(2)- 泛型编程
32 | 编程范式游记(3) - 类型系统和泛型的本质
33 | 编程范式游记(4)- 函数式编程
34 | 编程范式游记(5)- 修饰器模式
35 | 编程范式游记(6)- 面向对象编程
36 | 编程范式游记(7)- 基于原型的编程范式
37 | 编程范式游记(8)- Go 语言的委托模式
38 | 编程范式游记(9)- 编程的本质
39 | 编程范式游记(10)- 逻辑编程范式
40 | 编程范式游记(11)- 程序世界里的编程范式
41 | 弹力设计篇之“认识故障和弹力设计”
42 | 弹力设计篇之“隔离设计”
43 | 弹力设计篇之“异步通讯设计”
44 | 弹力设计篇之“幂等性设计”
45 | 弹力设计篇之“服务的状态”
46 | 弹力设计篇之“补偿事务”
47 | 弹力设计篇之“重试设计”
48 | 弹力设计篇之“熔断设计”
49 | 弹力设计篇之“限流设计”
50 | 弹力设计篇之“降级设计”
51 | 弹力设计篇之“弹力设计总结”
52 | 管理设计篇之“分布式锁”
53 | 管理设计篇之“配置中心”
54 | 管理设计篇之“边车模式”
55 | 管理设计篇之“服务网格”
56 | 管理设计篇之“网关模式”
57 | 管理设计篇之“部署升级策略”
58 | 性能设计篇之“缓存”
59 | 性能设计篇之“异步处理”
60 | 性能设计篇之“数据库扩展”
61 | 性能设计篇之“秒杀”
62 | 性能设计篇之“边缘计算”
63 | 区块链技术的本质
64 | 区块链技术细节:哈希算法
65 | 区块链技术细节:加密和挖矿
66 | 区块链技术细节:去中心化的共识机制
67 | 区块链技术细节:智能合约
68 | 区块链技术 - 传统金融和虚拟货币
69 | 程序员练级攻略:开篇词
70 | 程序员练级攻略:零基础启蒙
71 | 程序员练级攻略:正式入门
72 | 程序员练级攻略:程序员修养
73 | 程序员练级攻略:编程语言
74 | 程序员练级攻略:理论学科
75 | 程序员练级攻略:系统知识
76 | 程序员练级攻略:软件设计
77 | 程序员练级攻略:Linux系统、内存和网络
78 | 程序员练级攻略:异步I/O模型和Lock-Free编程
79 | 程序员练级攻略:Java底层知识
80 | 程序员练级攻略:数据库
81 | 程序员练级攻略:分布式架构入门
82 | 程序员练级攻略:分布式架构经典图书和论文
83 | 程序员练级攻略:分布式架构工程设计
84 | 程序员练级攻略:微服务
85 | 程序员练级攻略:容器化和自动化运维
86 | 程序员练级攻略:机器学习和人工智能
87 | 程序员练级攻略:前端基础和底层原理
88 | 程序员练级攻略:前端性能优化和框架
89 | 程序员练级攻略:UI/UX设计
90 | 程序员练级攻略:技术资源集散地
91 | 程序员面试攻略:面试前的准备
92 | 程序员面试攻略:面试中的技巧
93 | 程序员面试攻略:面试风格
94 | 程序员面试攻略:实力才是王中王
95 | 高效学习:端正学习态度
96 | 高效学习:源头、原理和知识地图
97 | 高效学习:深度,归纳和坚持实践
98 | 高效学习:如何学习和阅读代码
99 | 高效学习:面对枯燥和量大的知识
左耳听风
登录|注册

58 | 性能设计篇之“缓存”

陈皓 2018-04-19
前面分享了《分布式系统设计模式》系列文章的前两部分——弹力设计篇和管理设计篇。今天开始这一系列的最后一部分内容——性能设计篇,主题为《性能设计篇之“缓存”》。
基本上来说,在分布式系统中最耗性能的地方就是最后端的数据库了。一般来说,只要小心维护好,数据库四种操作(select、update、insert 和 delete)中的三个写操作 insert、update 和 delete 不太会出现性能问题(insert 一般不会有性能问题,update 和 delete 一般会有主键,所以也不会太慢)。除非索引建得太多,而数据库里的数据又太多,这三个操作才会变慢。
绝大多数情况下,select 是出现性能问题最大的地方。一方面,select 会有很多像 join、group、order、like 等这样丰富的语义,而这些语义是非常耗性能的;另一方面,大多数应用都是读多写少,所以加剧了慢查询的问题。
分布式系统中远程调用也会消耗很多资源,因为网络开销会导致整体的响应时间下降。为了挽救这样的性能开销,在业务允许的情况(不需要太实时的数据)下,使用缓存是非常必要的事情。
从另一个方面说,缓存在今天的移动互联网中是必不可少的一部分,因为网络质量不一定永远是最好的,所以前端也会为所有的 API 加上缓存。不然,网络不通畅的时候,没有数据,前端都不知道怎么展示 UI 了。既然因为移动互联网的网络质量而导致我们必须容忍数据的不实时性,那么,从业务上来说,在大多数情况下是可以使用缓存的。
缓存是提高性能最好的方式,一般来说,缓存有以下三种模式。

Cache Aside 更新模式

这是最常用的设计模式了,其具体逻辑如下。
失效:应用程序先从 Cache 取数据,如果没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从 Cache 中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。
这是标准的设计模式,包括 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?》,主要是怕两个并发的写操作导致脏数据。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《左耳听风》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(15)

  • 翎逸
    感觉更多的是不是应该说下缓存的监控,雪崩,缓存和数据库的一致性,以及热点缓存处理等一些场景的处理,这样会觉得更深入一些
    2018-05-10
    1
    24
  • W_T
    Read/Write Through 模式中对数据库的操作一定要交给交给缓存代理么,如果是这样就会带来两个问题:
    1. 需要在缓存服务中实现数据库操作的代码,我从来没有这么做过,也不清楚目前主流缓存是否支持这样的操作。
    2. 缓存服务与数据库之间建立了依赖。
    我在工作中更常见的做法是由应用服务操作缓存以及数据库,这样的话感觉就跟前面的cache aside模式很像了。
    可能是我对Read/Write Through模式理解不深,说错的地方还请老师指正
    2018-05-10
    4
  • river
    Cache aside 更新数据库 然后失效缓存,在读很高的情况下,会不会相当于缓存被击穿?

    作者回复: 怎么会呢?

    2018-05-13
    3
  • Black
    这篇的内容有大部分是跟之前博客上的一篇 缓存更新的套路 重复了

    作者回复: 是的。这是为了整个系列的完整。

    2018-05-10
    3
  • shawn
    你好,我看了“架构之路”的公众号,
    https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=404202261&idx=1&sn=1b8254ba5013952923bdc21e0579108e&scene=21#wechat_redirect

    这里说了大部分场景是建议 先 “缓存失效 -> 再更新数据”。

    比如:先写数据再淘汰缓存会有如下情况:
    “假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致【如上图:db中是新数据,cache中是旧数据】。”

    我想听听你的看法。
    2018-12-12
    2
  • MarksGui
    皓哥,对于很多需要统计的数据或者筛选条件复杂的怎么利用缓存了?
    2018-06-01
    2
  • river
    Cache aside 更新数据库 然后失效缓存。失效时读不到缓存,不是会打到数据库的流量很高?
    2018-05-14
    1
  • FF
    write through 这种模式,如果没有命中缓存更新数据库后返回,后面缓存谁来更新?这种更新想想好像很复杂
    2018-05-11
    1
  • 陈华
    大家缓存一般怎么用的啊。 我这里有一个疑问。

    比如我有一个组织机构表, 然后我有好几个针对组织机构表的查询方法, 然后我spring boot 直接对这些方法进行缓存。

    那么问题是:一旦我更新了某个机构的信息。 我想这个时候应该对上述多个方法的缓存都做失效处理。
    大家一般是怎么做的啊?,
    首先spring boot 的cache是自动根据所有参数信息来生成的key, 我更新机构信息的时候,都不知道应该让那些key失效。还有即使知道了这些key的信息, 那程序上我得一直维护一个  缓存keys 和 update 方法之间的映射?那不得在所有缓存和需要更新的方法加一层aop,动态维护这个映射关系?,
    2019-07-10
  • 陈华
    大家缓存一般怎么用的啊。 我这里有一个疑问。

    比如我有一个组织机构表, 然后我又好几个正对组织机构表的查询方法, 然后我spring boot 直接对这些方法进行缓存。

    那么问题是:一旦我更新了某个机构的信息。 我想这个时候应该对上述多个方法的缓存都做失效处理。
    关键是怎么做啊?,
    首先spring boot 的cache是自动根据所有参数信息来生成的key, 我更新机构信息的时候,都不知道应该让那些key失效。还有即使知道了这些key的信息, 那程序上我得一直维护一个 缓存keys 和 update 方法之间的映射?,
    2019-07-10
  • 黄绳霖
    Read/Write Through怎么避免并发造成的脏数据,难道要在数据服务上加锁吗?
    2019-07-03
  • edisonhuang
    缓存是一个提高系统性能的关键模块,通常一个系统的性能瓶颈会出现在数据库,而数据库增删改查的操作里,涉及到写的操作是带来性能瓶颈的地方。通常大多数的系统操作都是读多写少,写的过程因为要保证数据的一致性,因此在写的过程需要加锁,没有缓存会带来异能的极大损耗。
    缓存的实现分三种方式,cache aside,read write through,write back。缓存的替换算法一般是LRU替换算法,lru在存取过程中要加锁。
    2019-06-14
  • ~L.yy、
    数据在本地做了缓存,这些数据由下游的一组服务提供,当缓存过期时,将会穿透到下游去取数据。若下游的服务出现波动,将导致缓存的更新时间变长,在这个区间里的请求将全部会穿透,可能会导致服务过载。请问下老师有没有好的方式优化下这个场景
    2018-09-01
  • A圆规
    Cache aside 需要处理并发读问题,缓存失效时多个读会打到数据库
    2018-06-01
  • 陈皓老师好,redis 分片热点问题,有没有什么好的解决方案?

    作者回复: 建数据索引服务

    2018-05-10
收起评论
15
返回
顶部