跳到主要内容

数据一致性

2025年01月06日
柏拉文
越努力,越幸运

一、认识


在分布式系统中,Redis 缓存和数据库可能存储相同的数据,但它们的更新速度不同,可能会导致数据不一致问题。数据一致性是确保缓存与数据库中的数据保持同步的关键。我们最优的解决方案是先更新数据库,再操作缓存,确保数据源是数据库。

二、原因


  1. 直接更新数据库而忘记更新缓存: 更新数据库的数据,但未同步更新缓存,导致缓存中的数据陈旧。

  2. 先更新缓存,再更新数据库: 如果在缓存更新后,数据库更新失败,可能导致缓存和数据库数据不一致。

  3. 缓存延迟更新: 数据库已经更新,但缓存中的旧数据还未被淘汰或刷新。

三、解决方案


3.1 旁路缓存模式

旁路缓存模式 Cache Aside Pattern: 应用程序先从缓存读取数据,如果未命中(Cache Miss),则从数据库读取并更新到缓存。更新或删除数据时,先操作数据库,再使缓存失效。这样的好处是数据更新逻辑简单,适合读多写少场景。

// 读取数据
const getData = async (key) => {
let data = await redis.get(key);
if (!data) {
data = await db.query("SELECT * FROM table WHERE key = ?", [key]);
await redis.set(key, JSON.stringify(data), 'EX', 3600); // 设置缓存过期时间
}
return data;
};

// 更新数据
const updateData = async (key, value) => {
await db.update("UPDATE table SET value = ? WHERE key = ?", [value, key]);
await redis.del(key); // 删除缓存
};

3.2 直写缓存模式

直写缓存模式 Write Through Pattern: 所有的写操作直接写入缓存,缓存再同步写入数据库。这样的好处是缓存与数据库实时同步,适合频繁读写场景。但是数据写入延迟增加,因为需要同步写数据库。

3.3 异步写缓存模式

异步写缓存模式 Write Behind Pattern 写操作先写入缓存,数据库更新通过异步任务队列延迟处理。这样的话,数据写入延迟较小,适合写操作密集的场景。但是异步处理失败时可能导致数据丢失。

3.4 延迟双删策略

延迟双删策略 Delayed Double Delete: 适用于高并发场景,解决缓存和数据库更新时的时间差问题。具体操作步骤为: 首先更新数据库,然后删除缓存, 等待短暂时间后, 再次删除缓存(避免并发请求更新缓存为旧数据)。

const updateWithDoubleDelete = async (key, value) => {
await db.update("UPDATE table SET value = ? WHERE key = ?", [value, key]);
await redis.del(key); // 第一次删除缓存
setTimeout(async () => {
await redis.del(key); // 延迟第二次删除缓存
}, 500); // 延迟时间根据具体场景调整
};