分散型並行システムでは、データベースとキャッシュ データの一貫性が技術的に困難です。完全な産業グレードの分散トランザクション ソリューションが存在すると仮定すると、データベースとキャッシュ データの一貫性は簡単に解決されますが、実際、分散トランザクションは現時点では未熟です。
データベースとキャッシュ データの整合性ソリューションには、さまざまな声があります。
public BuOrder getOrder(Long orderId) { String key = ORDER_KEY_PREFIX + orderId; BuOrder buOrder = RedisUtils.getObject(key, BuOrder.class); if (buOrder != null) { return buOrder; } BuOrder order = getById(orderId); RedisUtils.setObject(key, order, 5, TimeUnit.MINUTES); return order; }
#
@Override public BuOrder getOrder(Long orderId) { /* 如果缓存不存在,则添加分布式锁更新缓存 */ String key = ORDER_KEY_PREFIX + orderId; BuOrder order = RedisUtils.getObject(key, BuOrder.class); if (order != null) { return order; } String orderLock = ORDER_LOCK + orderId; RLock lock = redissonClient.getLock(orderLock); if (lock.tryLock()) { order = RedisUtils.getObject(key, BuOrder.class); if (order != null) { LockOptional.ofNullable(lock).ifLocked(RLock::unlock); return order; } BuOrder buOrder = getById(orderId); RedisUtils.setObject(key, buOrder, 5, TimeUnit.MINUTES); LockOptional.ofNullable(lock).ifLocked(RLock::unlock); } return RedisUtils.getObject(key, BuOrder.class); }
public Boolean editOrder(BuOrder order) { /* 更新数据库 */ updateById(order); /* 删除缓存 */ RedisUtils.deleteObject(OrderServiceImpl.ORDER_KEY_PREFIX + order.getOrderId()); return true; }
public Boolean editOrder(BuOrder order) { String orderLock = ORDER_LOCK + order.getOrderId(); RLock lock = redissonClient.getLock(orderLock); try { /* 超时未获取到锁,快速失败,用户端重试 */ if (lock.tryLock(1, TimeUnit.SECONDS)) { /* 更新数据库 */ updateById(order); /* 删除缓存 */ RedisUtils.deleteObject(OrderServiceImpl.ORDER_KEY_PREFIX + order.getOrderId()); /* 释放锁 */ LockOptional.ofNullable(lock).ifLocked(RLock::unlock); return true; } } catch (InterruptedException e) { e.printStackTrace(); } return false; }
<dependency> <groupId>xin.altitude.cms</groupId> <artifactId>ucode-cms-common</artifactId> <version>1.4.3.2</version> </dependency>
LockOptionalロックのステータスに基づいて後続の操作を実行します。
(2) A にデータベースにクエリを実行して古い値を取得するようリクエストします
(3) B に新しい値をデータベースに書き込むようリクエストします
( 4) B にキャッシュの削除を要求します
(5) A に見つかった古い値をキャッシュに書き込むよう要求します
上記の同時実行性の問題の鍵は、ステップ 3 と 4 の後にステップ 5 が発生することです。オペレーティング システムの中断という不確実な要因から、この状況が発生する可能性があることがわかります。
実際の状況から見ると、Redis へのデータの書き込みは、データベースへのデータの書き込みよりもはるかに時間がかかりません。発生確率は低いですが、それでも発生します。
(1) キャッシュの有効期限を長くします
#キャッシュの有効期限を長くして、ダーティ データが特定の時間範囲内に存在できるようにします。次の同時更新が発生すると、ダーティ データが発生する可能性があります。ダーティデータは定期的に存在します。
(2) 更新とクエリは行ロックを共有します
更新とクエリは行分散ロックを共有し、上記の問題は存在しなくなりました。存在する。読み取りリクエストがロックを取得すると、書き込みリクエストはブロック状態になり (タイムアウトが失敗し、すぐに戻ります)、ステップ 3 の前にステップ 5 が実行されることが保証されます。
(3) キャッシュ削除の遅延
RabbitMQ を使用してキャッシュ削除を遅延し、手順 5 の影響を排除します。非同期メソッドを使用しても、パフォーマンスにはほとんど影響がありません。
データベースには、操作の成功を保証するトランザクション メカニズムがあります。単一の Redis 命令はアトミックですが、結合するとアトミックな特性はありません。具体的には、データベース操作が成功すると、アプリケーションが異常にハングアップし、その結果、Redis キャッシュが削除されませんでした。この問題は、Redis サービスのネットワーク接続がタイムアウトすると発生します。
キャッシュの有効期限が設定されている場合、キャッシュの有効期限が切れる前にダーティ データが常に存在します。有効期限が設定されていない場合、ダーティ データは次回データが変更されるまで存在します。 (データベースのデータが変更されており、キャッシュが更新されていません)
データベースを操作する前に、RabbitMQに遅延キャッシュ削除メッセージを書き込み、データベース操作を実行してキャッシュを実行します。削除操作を行います。コードレベルのキャッシュが正常に削除されたかどうかに関係なく、MQ は保証された操作としてキャッシュを削除します。
以上がJava 同時プログラミングのデータベースとキャッシュ データの整合性スキームは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。