成为工程师 - 如何提升系统读性能(上)

简介: 成为工程师 - 如何提升系统读性能(上)

今日内容


从今天开始,我们开始讲另一个重要的话题:性能【性能】是指“如何让系统变得更快”。


从【读性能】和【写性能】两块来做展开,帮助大家识别并提升自己的系统能力。我们先从【读性能】开始。由于读性能的内容比较多,我们会分为两期来讲。


所谓【读性能】,也就是“查询数据的性能”。我们会从【缓存神器】、【读写分离】、【并发思维】、【异步设计】、【产品设计】这五个方面来给大家展开。


今天我们来讲其中的第一个,也是最重要的一个,那就是【缓存神器】


01缓存用法多


缓存是大家最容易想到的提升读性能的办法。而且,使用缓存往往立竿见影,对读性能的提升产生奇效,所以我把他称之为“神器”。


缓存在具体的使用时,有很多种方式:


从上图中我们可以看到,缓存既可以用于对DB数据的缓存、也可以用于对业务计算结果的缓存、还可以用于对返回结果的缓存


缓存的本质是:用空间替换时间。这和很多算法有着相似的思路。


02【本地缓存】和【中心缓存】


缓存又可以分为【本地缓存】【中心化缓存】


如图所示,【本地缓存】和【中心化缓存】各有利弊。在真实的生产环境中,这两者往往一起使用。


【本地缓存】用于对元数据和配置的加载(这些数据相对稳定,没有数据频繁变更下的一致性问题,同时缓存的数据量也不大,内存hold得住),避免系统读取这些数据需要反复和DB打交道。


【中心化缓存】用于对用户请求数据的缓存。可以缓存较大的数据量,同时,可以避免一致性问题。


02缓存更新的难题


缓存利器虽然强大,但是使用起来却要非常小心,不然就会有意想不到的效果。

在你的印象中,缓存的典型用法应该是这样的:


我们先读取缓存,如果读不到数据,我们则会读DB,然后再把读出来的数据更新到缓存中。


这是典型的缓存使用方式,但这里有个大漏洞,那就是如果DB中的数据更新了,缓存里还是旧数据,要怎么办呢?


上图是我们常用的一种方式。在写入缓存时,我们设置一个过期时间,时间一到,缓存会自己删除数据,继而通过读请求加载新的数据到缓存中


这种方式实现简单,不容易有坑。但他的问题在于,新数据写入DB后,需要等待旧数据到达过期时间后才能被访问到。这个就比较“看脸”了,如果过期时间设置的长了些,新数据可能会滞后很久。


一种直观的思路就是:是否能够在数据写入DB时,同时更新cache中的数据呢?

当然可以,如下图:



这样是不是就完事大吉了呢?一个坑正向你走来


如果出现下图这样的情况,缓存里的值依然是有问题的。请跟着下图的序号来看看“两个更新数据的请求是如何挖坑的”。



图中蓝色的线携带的值是一个更加“新鲜”的值。但是由于蓝色线执行得比较快,优先写入了缓存,然后被“动作较慢”的黄色线最后覆盖成了旧值。


惊不惊喜?意不意外?


那我们要怎么解决这个问题呢?


很简单,我们只要把【更新缓存】的动作换成【删除缓存】就可以了。像下图这样:



这是经典的【cache aside pattern】缓存更新策略。在写入DB的时候淘汰缓存,在读取DB的时候写入缓存。简单地说就是:写淘汰 + 读更新


我们终于可以安稳睡觉了!不!还是有问题!


请大家仔细看下面这张图,按照步骤来看:


根据上图的顺序可以看到,最终读请求写入cache的缓存还是旧的。

想睡个好觉怎么就那么难呢?


所以最终,你还是不得不通过一些手段,保证在各种情况下,缓存里的值还是可以被正确更新。怎么弄呢?写入缓存时还是加个过期时间呗!


一路看下来,你是不是觉得缓存的问题特别的费劲。没错,各种读写并发,缓存里的值就像迷一样难以捉摸。


但是,我们还是有一个绝招的。那就是记住一个宗旨:缓存里的任何数据都不能够永久地待在里面


你可以通过过期时间删除、可以通过定时淘汰缓存、也可以通过定时刷新缓存这些方式。总之,任何一个被你扔到缓存里的东西,都要保证有被拿出来或者被覆盖的一天。


03缓存穿透


在使用缓存的时候,我们期望缓存可以挡住绝大部分流量,以分担DB的压力。一个你期望的理想情况应该如下

这样:


但是,人生吧,意外总是不期而遇,比如下图这样:



从图中可以看到,如果有大量不存在的key的请求,那么缓存完全拦截不住,所有流量就都打到了DB,DB就挂了。


这就是【缓存穿透】。也就是针对这样的请求,缓存完全没有抗流的能力。


那要怎么应对这样的问题呢?要知道,这样的问题既可能是上游bug,也可能是恶意攻击。


你有两种办法可以应对,我们来看一下第一种办法是:缓存空值





之所以之前的方案会把流量打到DB,是因为缓存里拿不到后,DB里也拿不到。DB里拿不到就不会往缓存里写。


缓存空值就是在DB读不到数据的时候,写一个特殊的标记到缓存中,这样缓存就知道DB里也没值,直接返回请求。


第二种方法是:布隆过滤器



布隆过滤器是专门用来判断一个元素是不是在集合中的。如上图所示,通过将DB数据加载到布隆过滤器中(只加载查询的key),布隆过滤器就具备了识别请求key是否有必要继续访问的能力。


你也许会说,“用布隆过滤器我还不如用一个set呢,存下所有的key,然后访问时候判断在不在set里就可以了”。


没错,你这样做事可以的,但布隆过滤器需要的内存更少,判断也更快。在大请求量的场景下,有非常好的性能。


关于布隆过滤器的细节我不在这里做展开,有兴趣的同学可以自己学习一下,它的设计思路非常巧妙,很有意思。



04缓存雪崩


在应对完【缓存穿透】问题后,你敢拍着胸脯说没问题了不?你肯定不敢了吧?你的判断没错,你起码还要解决另一个问题,那就是【缓存雪崩】


所谓的【缓存雪崩】,就是指缓存突然之间起不到任何抗流作用了。原因在于服务端而非请求方。比如下面两种情况:
【情况一:缓存挂了】



这种情况很容易理解,缓存突然直接崩了,所以流量打到DB。


【情况二:缓存集中过期】




缓存里的数据设置了相同的过期时间,一到时间,突然“集体失踪”。这种情况较多见于“通过定时批量刷新缓存”或者“缓存预热”的情况。


应对这两种情况,我们一般有如下这样的方案。


针对【缓存挂了】的情况我们可以使用如下两种:


上图左侧是指,我们可以对缓存进行分片。这样的话,如果某个分片挂掉了,也只会影响一部分的缓存数据,不会整体雪崩。


上图右侧是指,你可以使用成熟的缓存中间件。像redis、memcached都有自己的高可用方案,值得信赖。


针对【缓存集中过期】的情况,我们也有两种方案。



上图左侧是指,我们在写缓存的时候,可以设置一个随机的过期时间。一次来避免缓存同时失效。


上图右侧是指,我们可以通过主动刷新的方式,在缓存过期前重置他们的过期时间。事实上,如果你采用这样的方式,你甚至可以不用设置缓存过期时间(因为定时刷新的动作就可以保证原来的缓存数据不会永久生效)。


05缓存当DB用?


一般来说,在我们的印象里,缓存是不可靠的。因为缓存之所以快,是基于内存,而内存断了电就忘记了所有。但是,现在流行的缓存中间件都有不错的高可用方案(多节点冗余+持久化),那缓存可以当做DB来用吗?在一些场景下是可以的,比如:海量商品的查询。


例如很多电商网站,流量大+产品多。当然你可以使用NoSQL数据库来支撑,但是如果流量实在太大,并且有一定的关联查询和筛选逻辑,要怎么做呢?还是得靠缓存。可以使用如下这样的方案:


图中我标明了,场景需要满足:只读 + 高可用


上图中,因为服务器所有的数据都来自于缓存,缓存的高可用要求毋庸置疑。而只读的话,可以避免写数据的丢失。就算是使用redis以及其配套的持久化方案,在极端情况下依然会有数据的丢失。


但是,真的必须是“只读”才能用缓存替代DB吗?


我们到写性能的篇章中会给大家再来说说。


今日小结




今天,我们开始讲系统性能方面的内容。我们把性能分为【读性能】和【写性能】。


【读性能】中又有很多的话题,我们今天把所有的篇幅都放在了【缓存】上。缓存可以说是提升系统【读性能】的关键手段。


我们讲了使用缓存时你会遇到的一些坑。包括:更新缓存的各种意外、缓存的穿透、缓存的雪崩。我们带着大家一起看了他们发生的原因以及应对的方式。


此外,我们还提到,缓存在一些场景下可以当做DB来使用,只要你把重点关注到就可以了。

相关文章
|
缓存 NoSQL 关系型数据库
如何设计一个高并发系统?
如何设计一个高并发系统?
110 0
|
3月前
|
缓存 前端开发 数据库
成为工程师 - 如何提升系统读性能(下)
成为工程师 - 如何提升系统读性能(下)
|
3月前
|
存储 缓存 NoSQL
成为工程师 - 如何提升系统写性能(上)
成为工程师 - 如何提升系统写性能(上)
|
3月前
|
存储 缓存 分布式计算
成为工程师 - 如何提升系统写性能(下)
成为工程师 - 如何提升系统写性能(下)
|
5月前
|
Java 数据库 图形学
论系统的木桶理论与性能瓶颈
论系统的木桶理论与性能瓶颈
61 7
|
6月前
|
存储 缓存 监控
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
110 0
|
缓存 测试技术 数据库
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
365 0
|
存储 缓存 监控
浅谈系统性能提升的经验和方法
资金核对的数据组装-执行-应急链路,有着千万级TPS并发量,同时由于资金业务特性,对系统可用性和准确性要求非常高;日常开发过程中会遇到各种各样的高可用问题,也在不断地尝试做一些系统设计以及性能优化,在此期间总结了部分性能优化的经验和方法,跟大家一起分享和交流,后续遇到一些新的问题也会持续总结和补充。
39470 17
浅谈系统性能提升的经验和方法
|
5G 索引
带你读《5G 系统技术原理与实现》——3.4 5G SSB
带你读《5G 系统技术原理与实现》——3.4 5G SSB
带你读《5G 系统技术原理与实现》——3.4 5G SSB
|
存储 缓存 数据可视化
CPU缓存读写以及一致性问题,你大学课堂睡过去的现在再温习下,绝对受益颇多
CPU缓存读写以及一致性问题,你大学课堂睡过去的现在再温习下,绝对受益颇多