RedisTemplatを使った簡単な分散ロックの実装の話
redissonフレームワークを使用しないRedis分散ロックの実装
準備すること
依存関係のインポート
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
RedisConfigクラスの書き方
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String , Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//String type key serializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
//String type value serializer
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//Hash type key serializer
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//Hash type value serializer
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//Inject the connection factory
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
1. SpringBootTestでテストモジュールを書く
1.1: プレースホルダロックの使用
Placeholder locking problem
: 例外発生時にロックが解除できず、後続スレッドがデッドロックになる。
@SpringBootTest
class ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void lodsTest01(){
ValueOperations valueOperations = redisTemplate.opsForValue();
// create a placeholder, if the key does not exist to set success
Boolean isLock = valueOperations.setIfAbsent("k1", "v1");
//If the occupancy is successful, do the normal operation
if (isLock){
//set a name to store in redis
valueOperations.set("name","xxxx");
// Retrieve the name from redis
String name = (String) valueOperations.get("name");
System.out.println("name = " + name);
//Manually create exceptions
Integer.parseInt("xxxx");
//delete the lock at the end of the operation
redisTemplate.delete("k1");
}else{
System.out.println("A thread is in use, please try later");
}
}
}
test
/
ロックを解放できない例外が発生した最初のスレッド。
その後、すべてのスレッドにアクセスできなくなります。
Solutions
:
ロックに有効時間を追加する。
1.2: デッドロックを解決するために有効な時間を設定するためにプレースホルダーを使用する。
Placeholder set valid time problem
:
スレッドに例外が発生しても、プレースホルダーの時間が経過すればロックは解除されます。しかし、多数のスレッドが同時にアクセスする場合、スレッド1が外的要因(ネットワーク変動、サーバートラブルなど)の影響を受け、スレッド1の業務が終了していないのにロックの有効時間が切れた場合、次のスレッドが入り、スレッドインセキュリティが発生し、スレッドが互いのロックを削除し合うことになります。
@Test public void testLock02() { ValueOperations valueOperations = redisTemplate.opsForValue(); // If the key does not exist before you can set the success, set a valid time to prevent thread exceptions deadlock Boolean isLock = valueOperations.setIfAbsent("k1", "v1",5, TimeUnit.SECONDS); //If the occupancy is successful, proceed to normal operation if (isLock){ //set a name to store in redis valueOperations.set("name","xxxx"); // Retrieve the name from redis String str = (String) valueOperations.get("name"); System.out.println("name = " + str); // Create exception Integer.parseInt("xxxx"); //delete the lock at the end of the operation redisTemplate.delete("k1"); }else{ System.out.println("A thread is in use, please try later"); } } solution :
luaスクリプトで、各ロックのキーに対応する値に乱数を設定する。
1.3: lua スクリプトを使用してスレッドセキュリティを解決する。
Redisサーバー上でluaスクリプトを記述することができます。
Advantages
: サーバー上で高速に動作する
Disadvantages
コードを修正するのが面倒
lua スクリプトは java 経由で送信可能です。
Pros:
コードの修正が容易
Disadvantages :
リクエストが送信されるたびに、ネットワークリソースを消費する
1.3.1: luaスクリプトの書き方
if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
1.3.2: ReidsConfig クラスの修正
@Bean public DefaultRedisScript<Boolean> defaultRedisScript(){ DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(); //lock.lua script location is the same level as application.yml redisScript.setLocation(new ClassPathResource("lock.lua")); // set the type to boolean redisScript.setResultType(Boolean.class); return redisScript; }
1.3.3: テストモジュールの作成
@Test public void testLock03(){ ValueOperations valueOperations = redisTemplate.opsForValue(); String value = UUID.randomUUID().toString(); // If the key does not exist, set the value to a random number to prevent thread insecurity Boolean isLock = valueOperations.setIfAbsent("k1", value, 5, TimeUnit.SECONDS); //If the occupation is successful, proceed to normal operation if (isLock){ //set a name to store in redis valueOperations.set("name","xxxx"); // Retrieve the name from redis String name = (String) valueOperations.get("name"); System.out.println("name = " + name); // send lua script for redis to delete the value corresponding to the lock Boolean aBoolean = (Boolean) redisTemplate.execute(redisScript, Collections.singletonList("k1"), value); System.out.println(aBoolean); }else{ System.out.println("There are threads in use, please try later"); } } Test results:
/{br nameの値をredisに格納することに成功し、ロックを解除してtrueを返す
ロックは名前だけを残して正常に削除されます。
RedisTemplatを使った簡単な分散ロックの実装についての記事は以上です。RedisTemplatの分散ロックについては、BinaryDevelopの過去記事を検索するか、引き続き以下の関連記事を参照してください。
関連
-
CentOS 8.4へのRedis 6.2.6の詳細なインストール方法
-
Redisによる分散ロック(setnx, getset, incr)の実装とタイムアウトの扱い方
-
Redisのインクリメント呼び出しが失敗する理由と推奨される使い方を大きな白い嘘で解説
-
Redisの重複排除の3つの手法のまとめ
-
Redisで緯度・経度座標データを簡単に扱う方法
-
SpringBootプロジェクトにおけるRedis。包括的なアプリケーション
-
Redisのkeysコマンドの遅さ
-
ジェディスはRedisを操作してCaptcha配信をシミュレートする
-
Redisによる分散シングルナンバーと分散ID(カスタムルール生成)
-
RedisClusterが16,384個のスロットを持つ設計になっている理由
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Redisクラスタのマスターノードとスレーブノードを縮小する詳細チュートリアル
-
redis分散ロックについて解説(redis分散ロックの最適化処理とRedissonの利用について)
-
Redis永続化RDBとAOFの実装プロセス
-
redis クラスタの実装は同じプレフィックスを持つキーをクリーンアップします。
-
Redis 3つのクラスターモードの説明
-
インタビューFAQです。Redisキャッシュとデータベース間のデータ整合性を確保する方法
-
redis分散ロック最適化の実装
-
Redisトランザクション処理の使用方法
-
Redisの分散ロックについて簡単に説明します
-
インストール後、Redis-cliが動作しない(redis-cli: コマンドが見つからない)。