开发者社区> 段嗣国> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

浅析缓存读写策略

简介: 随着我们业务量的增长,系统面对的压力也陡然上升,大量的读写请求到数据库往往会伴随着各式各样的问题,可能仅仅是一条慢SQL,就有可能拖垮整个系统服务。通常这个时候,我们除了做数据库的读写分离架构,还会对数据库进行分库分表。但是可能有些一成不变或者极少时间触发变更的数据,像类目、类目属性等,大量的针对类目维度的读数据库也会给数据库带来各种压力,通常会以NoSql数据库与关系型数据库互相搭配的方式,以用来更好的服务与我们的业务发展。
+关注继续查看

前言

image.png
随着我们业务量的增长,系统面对的压力也陡然上升,大量的读写请求到数据库往往会伴随着各式各样的问题,可能仅仅是一条慢SQL,就有可能拖垮整个系统服务。通常这个时候,我们除了做数据库的读写分离架构,还会对数据库进行分库分表。但是可能有些一成不变或者极少时间触发变更的数据,像类目、类目属性等,大量的针对类目维度的读数据库也会给数据库带来各种压力,通常会以NoSql数据库与关系型数据库互相搭配的方式,以用来更好的服务与我们的业务发展。

为什么要使用缓存

伴随着业务量的增长,数据量、并发量也会翻个很多倍,这个时候数据库的IO也逐渐成了系统的性能瓶颈,这个时候我们可以利用缓存中间件,来提升访问速度降低请求响应时间,以便提升整体系统性能。
说到缓存,大家可能都会想到memcached、redis、tair等,日常使用中可能大部分场景都是简单的读取缓存,有数据就返回,没有数据就查数据库,然后再写入缓存中。但是真正的缓存读写策略是比较复杂的,不仅要考虑各种场景,还需针对业务的各种因素权衡利弊的,比如“缓存与数据库”以谁为准、数据更新时是“淘汰缓存”还是“更新缓存”、缓存和数据库的操作时序如何保障等。

缓存的读写策略

缓存读写策略中有三种,分别是旁路缓存模式策略、读写穿透策略、异步缓存写入策略,日常开发中最常用的就是旁路缓存模式策略,它常常用于读请求比写请求多的场景下,具体的策略定义如下:

  • 旁路缓存策略(Cache Aside Pattern)

    • 读:先读取缓存,读取到就直接返回;没有读取到缓存,就读取数据库,然后拿出数据后放入缓存,同时返回响应。
    • 写:先更新数据库,然后再删除缓存
  • 读写穿透策略(Read/Write Through Pattern)

    • 先:先读取缓存,读取到就直接返回;没有读取到缓存,就读取数据库,然后拿出数据后放入缓存,同时返回响应。
    • 写:与旁路缓存策略相反,先看缓存中是否存在该数据,存在则先更新缓存,再更新数据库,也就是同时更新数据库跟缓存;不存在直接更新数据库。
  • 异步缓存写入策略(Write Behind Pattern):

    • 它跟读写穿透策略类似,但是它们的不同点就是:读写穿透是同步更新DB和cache,而异步缓存只更新缓存不直接更新数据库,而是通过异步批量的方式来更新数据库
关于这三种读写策略详细的解析,本文暂不展开,且听下回书讲解。

Cache Aside Pattern

上文说到旁路缓存策略是日常使用最多的一种策略,我们通过一个案例分析来看看是如何使用的。
假设现在数据库有个订单,字段order_id为订单号,字段price为订单价格。缓存中存储了以订单号为key的缓存(20221126:100),如下所示:
image.png

需求:由于可能订单算错了价格,需要将order_id为20221126的订单的价格改成300,我们该如何去做?

1、先更新数据库,在更新缓存

image.png
首先说明,这种读写策略下可能会存在数据不一致的情况的,假如A请求是将order_id=20221126的价格改为300,请求B是将order_id=20221126的价格改为100,那么最终情况应该是order_id=20221126的价格为100,且数据库与缓存中的数据一致,那么如果没有并发等控制,那么如下图所示:
image.png
因为更新数据库和更新缓存是两个非原子性的操作,而且没有并发控制等策略,所以请求A/B执行的顺序是不能保证的,如果你是因为系统并发量小而采取这种操作,那么是有问题的,这种情况发生的概率还是很高的,它不一定是并发导致,还有可能因为查询接口的耗时或者网络波动导致操作并发,最终导致数据的不一致。

2、先更新数据库,在删除缓存

其实解决上面那样的问题也很简单,我们不采取缓存更新的方案,而是删除缓存。而读取数据的时候,如果缓存没命中,就去查数据库,然后再回填到缓存中,如下所示:
image.png
这种解决方案就是上文的旁路缓存策略,它是以数据库的数据为基准的,而缓存是按需才加载,一般被分为读策略和写策略。此示例只是作为数据不一致的推导。
其实像Cache Aside这种缓存策略,也是有缺点的即也会出现数据不一致的情况,同时操作数据库以及缓存,任何一个操作失败都会造成数据不一致。假如设置了数据库,但是操作缓存失败了,没有清除掉,就会导致数据不一致。
而且这种方案也有可能出现脏数据可能,请求A读取缓存,此时缓存刚好失效,而更新操作执行速度比读操作执行快,读线程最后更新缓存,但是是个老数据,虽然更新操作最后会删除缓存,但是在这中间,脏数据就会产生。

Cache Aside策略有什么缺陷

最大的缺陷就是当我们写入操作很频繁的时候,缓存中的数据就会被频繁的删除掉,会直接导致缓存命中率下降,但是如果我们业务中又必须要很高的缓存命中率怎么办呢?

  1. 在更新数据库记录的时候也更新缓存,我们在代码写更新缓存前加上分布式锁,每次运行一个线程更新缓存,防止并发问题,这种做法就是会对写入性能带了一定影响,毕竟加了锁。
  2. 第二种方案,同样也是更新数据库的时候更新缓存,但是这次我们把缓存设置一个过期时间,一般很短,即使根据业务需求计算,即使出现了数据不一致的情况,也是会很快就过期了,这种情况是可以接受的。

还有就是首次的请求的数据一定不在缓存的问题,这种解决方式也简单,我们可以在系统加载的时候将热点数据提前写入缓存中。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
缓存有效期和淘汰策略|学习笔记
快速学习缓存有效期和淘汰策略
0 0
GoFrame gcache使用实践 | 缓存控制 淘汰策略
gcache模块默认提供的是一个高速的内存缓存,操作效率非常高效,CPU性能损耗在ns纳秒级别。使用简单易上手,非常适合单机应用使用。
0 0
PWA 应用 Service Worker 缓存的一些可选策略和使用场景
PWA 应用 Service Worker 缓存的一些可选策略和使用场景
0 0
SpringBoot缓存注解@Cacheable之自定义key策略及缓存失效时间指定
上一篇博文介绍了Spring中缓存注解@Cacheable @CacheEvit @CachePut的基本使用,接下来我们将看一下更高级一点的知识点 (Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍)
0 0
Redis 缓存淘汰策略(下)
本文主要讲解了 redis 缓存过期淘汰策略, 后面我们会结合 LeetCode 的 LRU 算法题目,自己动手实现 LRU 的算法案例(LRU 算法实践)。
0 0
Redis 缓存淘汰策略(上)
本文主要讲解了 redis 缓存过期淘汰策略, 后面我们会结合 LeetCode 的 LRU 算法题目,自己动手实现 LRU 的算法案例(LRU 算法实践)。
0 0
Redis 缓存有哪些淘汰策略?
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
0 0
Redis缓存有哪些淘汰策略
  redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
0 0
+关注
段嗣国
互联网一线农民工。
文章
问答
文章排行榜
最热
最新
相关电子书
更多
分布式高并发缓存6.0
立即下载
基于英特尔 SSD 的虚拟机缓存解决SSD
立即下载
用户态高速块缓存方案
立即下载