前言
开发中不同的业务场景有不同的缓存使用方式,如对于单机的、数据量不大的场景,用HashMap
就能实现一个简单的缓存,也可以借助Guava cache
等来实现。对于分布式缓存,常用的如Redis
、Tair
。下面探讨下平时在项目中特别是高并发场景下使用缓存时需要考虑的一些问题以及其应对的策略。
缓存的使用
缓存模式
- Cache Aside Pattern
- Read/Write Through Pattern
- Write Behind Caching Pattern
Cache Aside Pattern
应用先去缓存中找数据,若命中缓存则直接返回。如果未命中缓存,则需要先去数据库中查询数据,并将查询到的数据存储到缓存中。在这种模式下,没有单独的缓存组件,缓存和DB的读写操作由应用方负责。
image先删除缓存,再写数据库有什么问题?
我们先来看一下场景:
image在请求A淘汰缓存后,这时候请求B查询缓存,未命中,再查询数据库,此时请求尚未写完成,请求B读取的老数据,再将其写入缓存中,缓存中的数据就会一直脏下去。
为什么是删除缓存,而不是更新缓存?
image写请求B在请求A之前更新了缓存,这时候请求B再更新缓存,则会导致缓存中的数据就会一直脏下去。
若缓存需要繁杂的计算才能得到,且写操作频繁,则会浪费大量的计算,可以通过请求来缓存数据,也符合数据懒加载。
先更新数据,再删除缓存,同样存在数据不一致,只是概率相对小很多
image若读请求B在写请求A写数据成功之前读取数据库中的数据,同时更新了缓存中的数据,缓存中的数据会一直脏下去。
由于写数据库和更新缓存是两个独立的操作,很难保证数据一致性,通常是尽量降低不一致的概率。
Read/Write Through Pattern
Cache Aside Pattern
是由应用程序来更新缓存中的数据,导致应用方数据库和缓存的维护设计侵入代码,数据层的耦合增大,而Read/Write Through Pattern
则是通过缓存自身来更新数据,调用方直接和缓存管理组件打交道,避免应用和数据库之间的直接连接。
Read Through
应用向缓存管理组件发送查询请求,由缓存管理组件查询缓存,若缓存未命中,查询数据库,并将查询的数据写入缓存,并返回给应用。
image
Write Through
Write Through
套路和Read Through
相仿,当更新数据的时候,将请求发送给缓存管理组件,由缓存管理组件同步更数据库和缓存数据。
Write Behind Caching Pattern
Write Behind
模式和Write Through
模式非常相似,不同点在于Write through
将数据更新到数据库中是同步的方式,而Write Behind
是通过异步方式。
请求直接将数据写入缓存,并没有更新到数据库。而通过定时或者一定阈值的方式,将数据异步的同步到数据库中。这种方式优势在于有效减少了更新数据的频率,读写响应非常快,吞吐量也会有明显的提升。
由于数据操作不是强一致性的,而此时数据存放在缓存中,如果缓存在同步到数据库的过程中宕机了,则这部分数据就会有丢失的风险。
总结
Cache Aside
使用简单,但需要使用方维护数据库数据和缓存的更新
Read/Write Through
引入了缓存组件来统一管理缓存和数据库,对于使用方来说是透明的,无代码侵入,但相对于Cache Aside实现会负责一些
Write Behind Caching
数据的读写直接通过缓存,性能最好,在高并发场景下可以降低数据库的压力,通过定时或阈值的方式刷新到数据库中,但是存在宕机丢失数据的风险。
网友评论