最初にキャッシュが正常に更新されてもデータベースの更新が失敗すると、確実にデータの不整合が発生するため、この方法は簡単に削除できます。
このキャッシュ更新戦略は、一般に二重書き込みとして知られています。問題は、データベースを同時に更新するシナリオでは、ダーティ データがキャッシュにフラッシュされました
updateDB(); updateRedis();
例: 2 つの操作の間の後続のリクエストによってデータベースとキャッシュが変更された場合、この時点でキャッシュを更新すると、データはすでに期限切れになります。
問題があります:データベースを更新する前に、クエリ要求の場合、ダーティ データはキャッシュにフラッシュされます
deleteRedis(); updateDB();
例:2 つの操作の間にデータ クエリが発生すると、古いデータがキャッシュに入れられます。
この解決策では、リクエスト データの不一致が発生します。
更新操作のリクエスト A とクエリ操作の別のリクエスト B がある場合。次に、次の状況が発生します。
A に書き込み操作の実行とキャッシュの削除を要求します
B にクエリを実行して、キャッシュを削除するよう要求します。キャッシュが存在しません
B にデータベースにクエリを実行して古い値を取得するように要求します。
# B に古い値をキャッシュに書き込むように要求します
A に古い値をキャッシュに書き込むよう要求する 新しい値をデータベースに書き込む
問題があります:データベースを更新する前にクエリ要求があり、キャッシュが無効です。データベースがクエリされ、キャッシュが更新されます。データベースのクエリとキャッシュの更新の間にデータベースの更新操作が実行された場合、ダーティ データはキャッシュにフラッシュされます
updateDB(); deleteRedis();
例: データベースとキャッシュの間にクエリがある場合キャッシュ データが更新され、操作中にキャッシュが削除されると、古いデータがキャッシュに置かれます。
2 つのリクエストがあり、1 つは A にクエリ操作の実行を要求し、もう 1 つは B に更新操作の実行を要求すると仮定すると、次の状況が発生します。ただし、データベース読み取り操作の速度ははるかに高速です。書き込み操作よりも
#したがって、この状況は発生しにくくなります。 スキームの比較
スキーム 1 とスキーム 2 の共通の欠点:データベースの同時更新シナリオでは、ダーティ データがキャッシュされますが、一般に、シナリオが同時に書き込まれる可能性は比較的小さいです。 スレッド セーフティの観点から、ダーティ データが生成されます (例:
スレッド A)データベースを更新しました
スレッド B がデータベースを更新しました
どちらの順序を採用しても、両方の方法にいくつかの問題があります。 :
シンプル、低コスト、開発が簡単; 欠点: キャッシュミスの原因キャッシュの更新コストが小さく、読み取りが多く書き込みが少なく、基本的に同時書き込みが存在しない場合は、キャッシュを更新することもできますが、そうでない場合は、キャッシュを削除するのが一般的な方法です。
方案 | 问题 | 问题出现概率 | 推荐程度 |
---|---|---|---|
更新缓存 -> 更新数据库 | 为了保证数据准确性,数据必须以数据库更新结果为准,所以该方案绝不可行 | 大 | 不推荐 |
更新数据库 -> 更新缓存 | 并发更新数据库场景下,会将脏数据刷到缓存 | 并发写场景,概率一般 | 写请求较多时会出现不一致问题,不推荐使用。 |
删除缓存 -> 更新数据库 | 更新数据库之前,若有查询请求,会将脏数据刷到缓存 | 并发读场景,概率较大 | 读请求较多时会出现不一致问题,不推荐使用 |
更新数据库 -> 删除缓存 | 在更新数据库之前有查询请求,并且缓存失效了,会查询数据库,然后更新缓存。如果在查询数据库和更新缓存之间进行了数据库更新的操作,那么就会把脏数据刷到缓存 | 并发读场景&读操作慢于写操作,概率最小 | 读操作比写操作更慢的情况较少,相比于其他方式出错的概率小一些。勉强推荐。 |
采用更新前后双删除缓存策略
public void write(String key,Object data){ redis.del(key); db.update(data); Thread.sleep(1000); redis.del(key); }
先淘汰缓存
再写数据库
休眠1秒,再次淘汰缓存
大家应该评估自己的项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上即可。
这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
问题及解法:
1、同步删除,吞吐量降低如何处理
将第二次删除作为异步的,提交一个延迟的执行任务
2、解决删除失败的方式:
添加重试机制,例如:将删除失败的key,写入消息队列;但对业务耦合有些严重;
延时工具可以选择:
最普通的阻塞Thread.currentThread().sleep(1000);
Jdk调度线程池,quartz定时任务,利用jdk自带的delayQueue,netty的HashWheelTimer,Rabbitmq的延时队列,等等
我们有个商品中心的场景,是读多写少的服务,并且写数据会发送MQ通知下游拿数据,这样就需要严格保证缓存和数据库的一致性,需要提供高可靠的系统服务能力。
缓存key设置失效时间
先DB操作,再缓存失效
写操作都标记key(美团中间件)强制走主库
接入美团中间件监听binlog(美团中间件)变化的数据在进行兜底,再删除缓存
先判断是否走主库
如果走主库,则使用标记(美团中间件)查主库
如果不是,则查看缓存中是否有数据
缓存中有数据,则使用缓存数据作为结果
如果没有,则查DB数据,再写数据到缓存
关于缓存过期时间的问题
如果缓存设置了过期时间,那么上述的所有不一致情况都只是暂时的。
但是如果没有设置过期时间,那么不一致问题就只能等到下次更新数据时解决。
所以一定要设置缓存过期时间。
以上がJava でキャッシュの一貫性を確保する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。