【工作技术栈】通用的旁路缓存一致性缺陷以及解决方式

前言

如果你使用的缓存一致性不是“旁路缓存","写后删"的话,那么这篇文章可能就让您失望了,但是篇幅不长可以参考一下

现象

大多数缓存一致性解决方案都使用了旁路缓存,之前的工作在这方面的方案并没有特别大的出入,直到最近看到旁路缓存带来的一些问题时才饶有兴趣的看了看解决方案
首先看下旁路缓存的玩法:
在这里插入图片描述

分析原因

之前线上没出现过什么特别大的问题,也就没有想过这个方案可能会存在什么问题,后来有一次出现修改没有生效,但是重新登录后才生效的场景,这个时候才有心去分析一下,结果大致如下:

在这里插入图片描述

解决方法

核心原因:读取的时候回写过程由于缓存被删除,无法判断自己写的这个版本是否是新的还是老的
一句话解决方案:增加版本机制

具体解决策略如下:
在这里插入图片描述

思考感悟

几个优化点:
1、首先存储cache数据需要一个通用的数据结构来封装对于后续的拓展非常有帮助,每一个值即使是空值也可以用通用数据结构占位,这样既可以解决缓存一致性问题,还能够解决缓存穿透的问题

存在的问题:
1、以前删除的数据就不再占用空间,现在不存在的数据还会占用一定的数据结构空间,对缓存的存储压力会增加
2、版本机制的version复杂度也增加了,需要缓存底层并发支持版本号机制的判断,如果在业务代码中写通用的版本比对会仍然会造成业务代码获取的版本为老版本导致判断出错从而写入老数据的问题,所以该机制一定需要缓存底层支持,否则就需要加细粒度锁来支持。

### Redis 双写一致性旁路缓存实现方案 #### 1. 缓存读取逻辑 当应用程序尝试获取某个键的数据时,会首先访问缓存。如果缓存命中,则直接返回缓存中的数据[^4]。 ```python def get_data_from_cache(key): data = redis.get(key) if data is not None: return data # Cache miss, read from database and populate cache data = db.query(key) # Check deleteKey to avoid dirty reads during replication delay if check_delete_key(key): data = master_db.query(key) # Force read from master else: data = slave_db.query(key) # Normal read from slave redis.setex(key, ttl, data) # Populate the cache with fresh data return data ``` #### 2. 数据更新逻辑 对于数据更新操作,在修改数据库之前先清除对应的缓存条目,并设置一个`deleteKey`标志位用于处理主从延迟期间可能发生的脏读问题[^3]。 ```python def update_data_in_database(key, value): set_delete_key(key) # Set a flag indicating this key was deleted recently try: result = db.update(key, value) clear_delete_key_after_delay(key) # Clear after estimated replication lag time return result finally: redis.delete(key) # Invalidate stale cached entry immediately ``` #### 3. 处理主从复制延迟 为了避免因MySQL主从同步延迟而导致的脏读现象,可以在删除缓存的同时创建一条带有过期时间(`TTL`)等于预计最大主从延迟周期的特殊标记记录(deleteKey)[^3]。 ```python import threading def set_delete_key(key): """Set a temporary marker that indicates recent deletion.""" redis.set(f'delete:{key}', 'true', ex=replication_lag_time) def check_delete_key(key): """Check whether there's an active delete mark for given key.""" return bool(redis.exists(f'delete:{key}')) def clear_delete_key_after_delay(key): timer = threading.Timer(replication_lag_time, lambda: redis.delete(f'delete:{key}')) timer.start() ``` 通过上述措施可以有效解决Redis作为旁路缓存时可能出现的一致性问题,既保持了良好的性能又确保了数据准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

元空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值