浅析缓存读写策略

简介: 随着我们业务量的增长,系统面对的压力也陡然上升,大量的读写请求到数据库往往会伴随着各式各样的问题,可能仅仅是一条慢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. 第二种方案,同样也是更新数据库的时候更新缓存,但是这次我们把缓存设置一个过期时间,一般很短,即使根据业务需求计算,即使出现了数据不一致的情况,也是会很快就过期了,这种情况是可以接受的。

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

目录
相关文章
|
2月前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
52 3
|
4月前
|
缓存 Java
Java本地高性能缓存实践问题之Caffeine缓存库中基于时间设置驱逐策略的问题如何解决
Java本地高性能缓存实践问题之Caffeine缓存库中基于时间设置驱逐策略的问题如何解决
|
28天前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
62 10
|
24天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
70 4
|
25天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
28天前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
43 4
|
2月前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
2月前
|
存储 缓存 监控
|
2月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
65 4
|
2月前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
68 2

热门文章

最新文章

下一篇
无影云桌面