浅析缓存读写策略

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

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

目录
相关文章
|
28天前
|
存储 缓存 算法
缓存淘汰策略
缓存淘汰策略
30 0
|
1月前
|
存储 缓存 NoSQL
后端开发中的缓存策略:提升应用性能的关键
后端开发中的缓存策略:提升应用性能的关键
24 0
|
1月前
|
缓存 监控 NoSQL
解析Redis缓存雪崩及应对策略
解析Redis缓存雪崩及应对策略
|
2月前
|
存储 缓存 UED
缓存策略与Apollo:优化网络请求性能
缓存策略与Apollo:优化网络请求性能
|
3月前
|
缓存 NoSQL 关系型数据库
mysql缓存策略
mysql缓存策略
28 0
|
3月前
|
缓存 NoSQL 关系型数据库
|
16天前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
|
3天前
|
缓存 NoSQL Java
使用Redis进行Java缓存策略设计
【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
|
6天前
|
存储 缓存 自动驾驶
缓存策略与Apollo:优化网络请求性能
缓存策略与Apollo:优化网络请求性能
|
10天前
|
存储 缓存 iOS开发
基于iOS的高效图片缓存策略实现
【4月更文挑战第9天】在移动应用开发中,图片资源的加载与缓存是影响用户体验的重要因素之一。特别是对于iOS平台,合理设计图片缓存策略不仅能够提升用户浏览图片时的流畅度,还能有效降低应用程序的内存压力。本文将介绍一种针对iOS环境优化的图片缓存技术,该技术通过多级缓存机制和内存管理策略,实现了图片快速加载与低内存消耗的目标。我们将从系统架构、关键技术细节以及性能评估等方面展开讨论,为开发者提供一套实用的图片缓存解决方案。
15 0