你有没有遇到过这种情况:在手机上刚改完密码,换个设备登录却发现旧密码还能用?或者点了“刷新”按钮,页面内容却迟迟没变。这背后很可能就是缓存出了问题。
缓存不是万能的
缓存就像家里的备用药箱,平时拿药方便,可一旦药品过期或换了新药,要是没及时更新,就可能出问题。程序里的缓存也一样,它能加快访问速度,但数据变了,缓存却没同步,就会导致用户看到的是“旧消息”。
常见的缓存失效方式
最简单的做法是设置一个过期时间,比如10分钟后自动清掉。这种方式叫 TTL(Time to Live),实现简单,但不够灵活。在这10分钟内,哪怕数据已经变了好几轮,用户看到的还是老样子。
另一种方法是主动删除。比如用户修改了个人资料,系统立刻把对应的缓存删掉。下次请求时发现缓存没了,就会去查最新数据,重新加载。这种策略更及时,但也可能因为删除失败而导致数据不一致。
// 伪代码示例:主动删除缓存
function updateProfile(userId, newData) {
saveToDatabase(userId, newData);
deleteCache('user:' + userId); // 删除缓存
}
双写一致性难题
有人想,那我更新数据库的同时也更新缓存,是不是更保险?理论上可以,但实际操作中很容易出乱子。比如先更新数据库再更新缓存,如果更新完数据库后服务突然崩溃,缓存就没更新,结果就是数据库新、缓存旧。
反过来,先更新缓存再改数据库,万一数据库写失败了,缓存却已经变了,那就更糟——整个系统都基于错误的数据运行。
加点容错机制
为了应对这些问题,有些系统会引入消息队列。数据一更新,就发个通知给其他模块,告诉它们“我变了,你们该处理一下”。这样即使某一步失败,也能重试,提高可靠性。
还有一种叫“延迟双删”的技巧:先删一次缓存,等数据库更新完成后再删一次。这样做是为了防止在更新过程中有旧数据被误读并重新写回缓存。
// 延迟双删示意
deleteCache(key);
saveToDatabase(data);
// 延迟一段时间,比如500ms
deleteCache(key);
没有完美的方案
每种策略都有适用场景。对一致性要求高的,比如银行交易记录,就得用更复杂的机制确保数据准确;而像新闻列表这类容忍短暂不一致的内容,用简单过期策略就够了。
关键是在性能和准确之间找平衡。就像家里药箱,定期检查加上使用前确认,才能既方便又安全。