Redis分布式ロックの原理と実現
ps:このブログカタログは【Redis分散ロック使用】のコードに瑕疵があるので、誤った使用を避けるために、移動してくださいhttps://blog.csdn.net/She_lock/article/details/88894096、興味のある人は傷がどこにあるかを見ることができます
前言
問題を解決します:複数のプロセスの複数の機械、1つのデータに対する操作の反発.たとえば、受注と在庫控除の操作は、2つの操作が一貫している必要があります.1つのスレッドがこの2つの操作を実行した後、次のスレッドが介入して実行することができます.同時に実行すると、「マルチ販売」の現象が発生する可能性があります.
解決方法:
1、sqlレベル:は、
2、コードレベル:
以上の3つの方法はいずれも問題を解決することができ,今日議論するのは3つ目の------
Redis分散ロックベース命令
SETNX
構文:
戻り値:1 keyが に設定されている場合0 keyが に設定されていない場合
例:
用途:足かせに使用できます.例えば商品idに鍵をかける.
詳細コマンドの説明は、「SETNX key value」を参照してください.
GETSET
構文:
keyをvalueに自動的に対応させ、元のkeyに対応するvalueを返します.keyが存在しますが、対応するvalueが文字列ではない場合は、エラーが返されます.
この解釈は少し拗ねていますが、実は分解して見ることができます.まず
例:
用途:デッドロックを解決します.原理は、元のロックを新しいロックに置き換えることです.
詳細コマンドの説明は、「GETSET key value」を参照してください.
Redis分散ロックの使用
次のツールクラス
ソースコードから分かるように、
運用:
前言
問題を解決します:複数のプロセスの複数の機械、1つのデータに対する操作の反発.たとえば、受注と在庫控除の操作は、2つの操作が一貫している必要があります.1つのスレッドがこの2つの操作を実行した後、次のスレッドが介入して実行することができます.同時に実行すると、「マルチ販売」の現象が発生する可能性があります.
解決方法:
1、sqlレベル:
SELECT FOR UPDATE
ライン・レベル・ロックを使用して実現することができる.2、コードレベル:
synchronized
キーワードは、メソッドにロックを追加することで、同時問題を解決することができますが、キューの実行速度が遅く、高同時性の場合は適切ではありません.Redis
分散ロックは、主にSETNX
コマンドとGETSET
コマンドを利用する.高同時性を解決します.以上の3つの方法はいずれも問題を解決することができ,今日議論するのは3つ目の------
Redis
分布式ロックである.Redis分散ロックベース命令
SETNX
構文:
SETNX key value
key
をvalue
に設定し、key
が存在しない場合はSET
コマンドに等しい.key
が存在する場合、何もしません.SETNX
は”SET if Not eXists”
の略語です.戻り値:
例:
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis>
用途:足かせに使用できます.例えば商品idに鍵をかける.
詳細コマンドの説明は、「SETNX key value」を参照してください.
GETSET
構文:
GETSET key value
keyをvalueに自動的に対応させ、元のkeyに対応するvalueを返します.keyが存在しますが、対応するvalueが文字列ではない場合は、エラーが返されます.
この解釈は少し拗ねていますが、実は分解して見ることができます.まず
GET
、最後にSET
、前の古い値を返します.前のKey
が存在しなければnil
を返します.例:
redis> INCR mycounter
(integer) 1
redis> GETSET mycounter "0"
"1"
redis> GET mycounter
"0"
redis>
用途:デッドロックを解決します.原理は、元のロックを新しいロックに置き換えることです.
詳細コマンドの説明は、「GETSET key value」を参照してください.
Redis分散ロックの使用
次のツールクラス
RedisLock
:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* Redis
*/
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
*
* @param key
* @param value +
* @return
*/
public boolean lock(String key, String value) {
if(redisTemplate.opsForValue().setIfAbsent(key, value)) {// SETNX ,setIfAbsent true, false
return true;
}
// currentValue=A value B , ( ), , “ ”
String currentValue = redisTemplate.opsForValue().get(key);
//
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
// , ,GETSET
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;// , ,
}
/**
*
* @param key
* @param value
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e) {
log.error("【redis 】 , {}", e);
}
}
}
ソースコードから分かるように、
setIfAbsent
はロック機能を実現し、一部のネットワークやioなどの原因で異常にデッドロックが発生した場合、現在のロック操作が期限切れになった後、getAndSet
は新しいロックで元の古いロックを置き換え、デッドロック問題の解決を実現する.運用:
private static final int TIMEOUT = 10 * 1000; // 10s
@Autowired
private RedisLock redisLock;
public void orderProductMockDiffUser(String productId)
{
//
long time = System.currentTimeMillis() + TIMEOUT ;
if(!redisLock.lock(productId,String.valueOf(time))){ //
throw SellException(101," , !");
}
/***** , *******/
//1. , 0 。
int stockNum = stock.get(productId);
if(stockNum == 0) {
throw new SellException(100," ");
}else {
//2. ( openid )
orders.put(KeyUtil.genUniqueKey(),productId);
//3.
stockNum =stockNum-1;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId,stockNum);
}
//
redisLock.unlock(productId,String.valueOf(time));
}