åå¸å¼é--Rediså°è¯çå
åèæç« ï¼
-
https://www.cnblogs.com/linjiqin/p/8003838.html
-
http://www.54tianzhisheng.cn/2018/04/24/Distributed_lock/
å¨è¿ä¸¤ç¯æç« çæå¼ä¸äº²æµ Redisåå¸å¼é
å¼è¨
åå¸å¼ç³»ç»ä¸å®ä¼åå¨CAPæè¡¡é®é¢ï¼æä»¥æä¼åºç°åå¸å¼é
ä»ä¹æ¯CAPç论ï¼
ä¸ºäºæ´å¥½ççè§£æç« ï¼å»ºè®®é 读CAPç论ï¼http://www.hollischuang.com/archives/666
ä»ä¹æ¯éï¼
-
å¨åè¿ç¨çç³»ç»ä¸ï¼å½åå¨å¤ä¸ªçº¿ç¨å¯ä»¥åæ¶æ¹åæä¸ªåéï¼å¯åå ±äº«åéï¼æ¶ï¼å°±éè¦å¯¹åéæä»£ç åå忥ï¼ä½¿å ¶å¨ä¿®æ¹è¿ç§åéæ¶è½å¤çº¿æ§æ§è¡æ¶é¤å¹¶åä¿®æ¹åéã
-
èåæ¥çæ¬è´¨æ¯éè¿éæ¥å®ç°çã为äºå®ç°å¤ä¸ªçº¿ç¨å¨ä¸ä¸ªæ¶å»åä¸ä¸ªä»£ç ååªè½æä¸ä¸ªçº¿ç¨å¯æ§è¡ï¼é£ä¹éè¦å¨æä¸ªå°æ¹å个æ è®°ï¼è¿ä¸ªæ è®°å¿ é¡»æ¯ä¸ªçº¿ç¨é½è½çå°ï¼å½æ è®°ä¸å卿¶å¯ä»¥è®¾ç½®è¯¥æ è®°ï¼å ¶ä½åç»çº¿ç¨åç°å·²ç»ææ è®°äºåçå¾ æ¥ææ è®°ç线ç¨ç»æåæ¥ä»£ç ååæ¶æ è®°ååå»å°è¯è®¾ç½®æ è®°ãè¿ä¸ªæ è®°å¯ä»¥ç解为éã
-
ä¸åå°æ¹å®ç°éçæ¹å¼ä¹ä¸ä¸æ ·ï¼åªè¦è½æ»¡è¶³ææçº¿ç¨é½è½çå¾å°æ è®°å³å¯ãå¦ Java ä¸ synchronize æ¯å¨å¯¹è±¡å¤´è®¾ç½®æ è®°ï¼Lock æ¥å£çå®ç°ç±»åºæ¬ä¸é½åªæ¯æä¸ä¸ª volitile 修饰ç int ååéå ¶ä¿è¯æ¯ä¸ªçº¿ç¨é½è½æ¥æå¯¹è¯¥ int çå¯è§æ§åååä¿®æ¹ï¼linux å æ ¸ä¸ä¹æ¯å©ç¨äºæ¥éæä¿¡å·éçå åæ°æ®åæ è®°ã
-
é¤äºå©ç¨å åæ°æ®åéå ¶å®ä»»ä½äºæ¥çé½è½åéï¼åªèèäºæ¥æ åµï¼ï¼å¦æµæ°´è¡¨ä¸æµæ°´å·ä¸æ¶é´ç»ååå¹çæ ¡éªå¯ä»¥ç使¯ä¸ä¸ªä¸ä¼éæ¾çéï¼æè ä½¿ç¨æä¸ªæä»¶æ¯å¦åå¨ä½ä¸ºéçãåªéè¦æ»¡è¶³å¨å¯¹æ è®°è¿è¡ä¿®æ¹è½ä¿è¯ååæ§åå åå¯è§æ§å³å¯ã
åå¸å¼åºæ¯
æ¤å¤ä¸»è¦æé群模å¼ä¸ï¼å¤ä¸ªç¸åæå¡åæ¶å¼å¯.
å¨è®¸å¤çåºæ¯ä¸ï¼æä»¬ä¸ºäºä¿è¯æ°æ®çæç»ä¸è´æ§ï¼éè¦å¾å¤çææ¯æ¹æ¡æ¥æ¯æï¼æ¯å¦åå¸å¼äºå¡ãåå¸å¼éçãå¾å¤æ¶åæä»¬éè¦ä¿è¯ä¸ä¸ªæ¹æ³å¨å䏿¶é´å åªè½è¢«åä¸ä¸ªçº¿ç¨æ§è¡ãå¨åæºç¯å¢ä¸ï¼éè¿ Java æä¾çå¹¶å API æä»¬å¯ä»¥è§£å³ï¼ä½æ¯å¨åå¸å¼ç¯å¢ä¸ï¼å°±æ²¡æé£ä¹ç®åå¦ã
-
åå¸å¼ä¸åæºæ åµä¸æå¤§çä¸åå¨äºå ¶ä¸æ¯å¤çº¿ç¨èæ¯å¤è¿ç¨ã
-
å¤çº¿ç¨ç±äºå¯ä»¥å ±äº«å å åï¼å æ¤å¯ä»¥ç®åçéåå åä½ä¸ºæ è®°åå¨ä½ç½®ãèè¿ç¨ä¹é´çè³å¯è½é½ä¸å¨åä¸å°ç©çæºä¸ï¼å æ¤éè¦å°æ è®°åå¨å¨ä¸ä¸ªææè¿ç¨é½è½çå°çå°æ¹ã
ä»ä¹æ¯åå¸å¼éï¼
-
å½å¨åå¸å¼æ¨¡åä¸ï¼æ°æ®åªæä¸ä»½ï¼ææéå¶ï¼ï¼æ¤æ¶éè¦å©ç¨éçææ¯æ§å¶æä¸æ¶å»ä¿®æ¹æ°æ®çè¿ç¨æ°ã
-
ä¸åæºæ¨¡å¼ä¸çéä¸ä» éè¦ä¿è¯è¿ç¨å¯è§ï¼è¿éè¦èèè¿ç¨ä¸éä¹é´çç½ç»é®é¢ãï¼æè§å¾åå¸å¼æ åµä¸ä¹æä»¥é®é¢åå¾å¤æï¼ä¸»è¦å°±æ¯éè¦èèå°ç½ç»çå»¶æ¶åä¸å¯é ãããä¸ä¸ªå¤§åï¼
-
åå¸å¼éè¿æ¯å¯ä»¥å°æ è®°åå¨å åï¼åªæ¯è¯¥å å䏿¯æä¸ªè¿ç¨åé çå åèæ¯å ¬å ±å åå¦ RedisãMemcacheãè³äºå©ç¨æ°æ®åºãæä»¶çåéä¸åæºçå®ç°æ¯ä¸æ ·çï¼åªè¦ä¿è¯æ è®°è½äºæ¥å°±è¡ã
æä»¬éè¦ææ ·çåå¸å¼éï¼
-
å¯ä»¥ä¿è¯å¨åå¸å¼é¨ç½²çåºç¨é群ä¸ï¼åä¸ä¸ªæ¹æ³å¨å䏿¶é´åªè½è¢«ä¸å°æºå¨ä¸çä¸ä¸ªçº¿ç¨æ§è¡ã
-
è¿æéè¦æ¯ä¸æå¯éå ¥éï¼é¿å æ»éï¼
-
è¿æéæå¥½æ¯ä¸æé»å¡éï¼æ ¹æ®ä¸å¡éæ±èèè¦ä¸è¦è¿æ¡ï¼
-
è¿æéæå¥½æ¯ä¸æå ¬å¹³éï¼æ ¹æ®ä¸å¡éæ±èèè¦ä¸è¦è¿æ¡ï¼
-
æé«å¯ç¨çè·åéåéæ¾éåè½
-
è·åéåéæ¾éçæ§è½è¦å¥½
å¯é æ§
é¦å ï¼ä¸ºäºç¡®ä¿åå¸å¼éå¯ç¨ï¼æä»¬è³å°è¦ç¡®ä¿éçå®ç°åæ¶æ»¡è¶³ä»¥ä¸å个æ¡ä»¶ï¼
-
äºæ¥æ§ãå¨ä»»ææ¶å»ï¼åªæä¸ä¸ªå®¢æ·ç«¯è½ææéã
-
ä¸ä¼åçæ»éãå³ä½¿æä¸ä¸ªå®¢æ·ç«¯å¨ææéçæé´å´©æºè没æä¸»å¨è§£éï¼ä¹è½ä¿è¯åç»å ¶ä»å®¢æ·ç«¯è½å éã
-
å ·æå®¹éæ§ãåªè¦å¤§é¨åçRedisèç¹æ£å¸¸è¿è¡ï¼å®¢æ·ç«¯å°±å¯ä»¥å éåè§£éã
-
è§£éè¿é¡»ç³»é人ãå éåè§£éå¿ é¡»æ¯åä¸ä¸ªå®¢æ·ç«¯ï¼å®¢æ·ç«¯èªå·±ä¸è½æå«äººå çéç»è§£äºã
ä¸è¬å®ç°æ¹å¼
åå¸å¼éä¸è¬æä¸ç§å®ç°æ¹å¼ï¼
-
æ°æ®åºä¹è§éï¼
-
åºäºRedisçåå¸å¼éï¼
-
åºäºZooKeeperçåå¸å¼éã
æ¬æå°ä»ç»ç¬¬äºç§æ¹å¼ï¼åºäºRediså®ç°åå¸å¼éã
æµè¯ä»£ç å®ç°
注æï¼ Redis ä»2.6.12çæ¬å¼å§ set å½ä»¤æ¯æ NX ã PX è¿äºåæ°æ¥è¾¾å° setnx ã setex ã psetex å½ä»¤çææï¼ææ¡£åè§ï¼ http://doc.redisfans.com/string/set.html
Spring Boot ä¸ç RedisTemplate 并䏿¯æ NX åæ¶è®¾ç½®è¿ææ¶é´è¿ç§ set æä½ï¼å ·æååæ§ï¼
æä»¥è¿éæä»¬éè¦ Maven å¼å ¥æ¯æè¿ç§ set æä½ç Jedis ä¾èµ
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
å¹¶åä¸åï¼åºåéæµè¯ï¼
å建10个线ç¨ï¼åæ¶å¯å¨ä¸åæä½ï¼å¯¹åºåæä½å å ¥åå¸å¼é
æµè¯ä»£ç ï¼
package com.elise.userinfocenter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
import java.util.Collections;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserInfoCenterApplicationTests {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
private int splitPoint = 500;
@Autowired
private RedisProperties redisConfig;
@Test
public void redisLock() {
ThreadTest[] threadTests = new ThreadTest[10];
for (int i=0; i<10; i++) {
threadTests[i] = new ThreadTest();
}
for (int i=0; i<10; i++) {
threadTests[i].start();
}
}
private class ThreadTest extends Thread {
@Override
public void run() {
Jedis jedis = new Jedis(redisConfig.getHost(),redisConfig.getPort(),redisConfig.getTimeout());
String requestId = this.getId()+"";
int i=0;
while (true){
i = ++i;
try {
if(tryGetDistributedLock(jedis,"lock-test",requestId,2000)) {
System.out.println("线ç¨ï¼"+requestId+" æåè·å¾åå¸å¼éï¼ï¼ï¼");
System.out.println("å½ååºåï¼"+splitPoint);
splitPoint = --splitPoint;
System.out.println("线ç¨ï¼"+requestId+"ä¸åæåååºåï¼"+splitPoint);
if(releaseDistributedLock(jedis,"lock-test",requestId)) {
System.out.println("线ç¨ï¼"+requestId+" æåéæ¾åå¸å¼éï¼ï¼ï¼");
}
break;
} else {
System.out.println("线ç¨ï¼"+requestId+" 第"+i+"æ¬¡æ æ³è·å¾åå¸å¼éï¼ç»§ç»æ¢éï¼ï¼ï¼");
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* å°è¯è·ååå¸å¼é
* @param jedis Redis客æ·ç«¯
* @param lockKey é
* @param requestId è¯·æ±æ è¯
* @param expireTime è¶
ææ¶é´
* @return æ¯å¦è·åæå
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* éæ¾åå¸å¼é
* @param jedis Redis客æ·ç«¯
* @param lockKey é
* @param requestId è¯·æ±æ è¯
* @return æ¯å¦éæ¾æå
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
ææå¾ï¼
éè¿ä¸é¢ç代ç +ææå¾å¯ä»¥ç¥éè¿å个线ç¨å¯å¨ä¹åé½å¼å§æ¢å redisåå¸å¼éï¼æ²¡æè·å¾éç»§ç»æ¢éï¼è¹è¹è¹å 䏿¯ä¸ªçº¿ç¨é½åç¡®æ 误滴æ§è¡äºä¸åï¼åå°åºåæä½ï¼ä¸é¢å ·ä½åæä¸ä¸å éï¼è§£é代ç
å é代ç
/**
* å°è¯è·ååå¸å¼é
* @param jedis Redis客æ·ç«¯
* @param lockKey é
* @param requestId è¯·æ±æ è¯
* @param expireTime è¶
ææ¶é´
* @return æ¯å¦è·åæå
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
å¯ä»¥çå°ï¼æä»¬å éå°±ä¸è¡ä»£ç ï¼jedis.set(String key, String value, String nxxx, String expx, int time)ï¼è¿ä¸ªset()æ¹æ³ä¸å
±æäºä¸ªå½¢åï¼
-
第ä¸ä¸ªä¸ºkeyï¼æä»¬ä½¿ç¨keyæ¥å½éï¼å 为keyæ¯å¯ä¸çã
-
第äºä¸ªä¸ºvalueï¼æä»¬ä¼ çæ¯requestIdï¼å¾å¤ç«¥éå¯è½ä¸æç½ï¼ækeyä½ä¸ºéä¸å°±å¤äºåï¼ä¸ºä»ä¹è¿è¦ç¨å°valueï¼åå å°±æ¯æä»¬å¨ä¸é¢è®²å°å¯é æ§æ¶ï¼åå¸å¼éè¦æ»¡è¶³ç¬¬å个æ¡ä»¶è§£éè¿é¡»ç³»é人ï¼éè¿ç»valueèµå¼ä¸ºrequestIdï¼æä»¬å°±ç¥éè¿æéæ¯åªä¸ªè¯·æ±å çäºï¼å¨è§£éçæ¶åå°±å¯ä»¥æä¾æ®ãrequestIdå¯ä»¥ä½¿ç¨
UUID.randomUUID().toString()æ¹æ³çæãï¼æ¬æµè¯ç¨ä¾ä½¿ç¨çæ¯å½å线ç¨IDï¼ -
第ä¸ä¸ªä¸ºnxxxï¼è¿ä¸ªåæ°æä»¬å¡«çæ¯NXï¼æææ¯SET IF NOT EXISTï¼å³å½keyä¸å卿¶ï¼æä»¬è¿è¡setæä½ï¼è¥keyå·²ç»åå¨ï¼åä¸å任使ä½ï¼
-
第å个为expxï¼è¿ä¸ªåæ°æä»¬ä¼ çæ¯PXï¼æææ¯æä»¬è¦ç»è¿ä¸ªkeyå ä¸ä¸ªè¿æç设置ï¼å ·ä½æ¶é´ç±ç¬¬äºä¸ªåæ°å³å®ã
-
第äºä¸ªä¸ºtimeï¼ä¸ç¬¬åä¸ªåæ°ç¸å¼åºï¼ä»£è¡¨keyçè¿ææ¶é´ã
æ»çæ¥è¯´ï¼æ§è¡ä¸é¢çset()æ¹æ³å°±åªä¼å¯¼è´ä¸¤ç§ç»æï¼
-
å½å没æéï¼keyä¸åå¨ï¼ï¼é£ä¹å°±è¿è¡å éæä½ï¼å¹¶å¯¹éè®¾ç½®ä¸ªæææï¼åæ¶value表示å éç客æ·ç«¯ã
-
å·²æéåå¨ï¼ä¸å任使ä½ã
å¿ç»çç«¥éå°±ä¼åç°äºï¼æä»¬çå éä»£ç æ»¡è¶³æä»¬å¯é æ§éæè¿°çä¸ä¸ªæ¡ä»¶ã
-
é¦å ï¼set()å å ¥äºNXåæ°ï¼å¯ä»¥ä¿è¯å¦æå·²ækeyåå¨ï¼å彿°ä¸ä¼è°ç¨æåï¼ä¹å°±æ¯åªæä¸ä¸ªå®¢æ·ç«¯è½ææéï¼æ»¡è¶³äºæ¥æ§ã
-
å ¶æ¬¡ï¼ç±äºæä»¬å¯¹é设置äºè¿ææ¶é´ï¼å³ä½¿éçææè åç»åçå´©æºè没æè§£éï¼éä¹ä¼å 为å°äºè¿ææ¶é´èèªå¨è§£éï¼å³key被å é¤ï¼ï¼ä¸ä¼åçæ»éã
-
æåï¼å 为æä»¬å°valueèµå¼ä¸ºrequestIdï¼ä»£è¡¨å éç客æ·ç«¯è¯·æ±æ è¯ï¼é£ä¹å¨å®¢æ·ç«¯å¨è§£éçæ¶åå°±å¯ä»¥è¿è¡æ ¡éªæ¯å¦æ¯åä¸ä¸ªå®¢æ·ç«¯ãç±äºæä»¬åªèèRedisåæºé¨ç½²çåºæ¯ï¼æä»¥å®¹éæ§æä»¬æä¸èèã
è§£é代ç
/**
* éæ¾åå¸å¼é
* @param jedis Redis客æ·ç«¯
* @param lockKey é
* @param requestId è¯·æ±æ è¯
* @return æ¯å¦éæ¾æå
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
å¯ä»¥çå°ï¼æä»¬è§£éåªéè¦ä¸¤è¡ä»£ç å°±æå®äºï¼ç¬¬ä¸è¡ä»£ç ï¼æä»¬åäºä¸ä¸ªç®åçLuaèæ¬ä»£ç ã第äºè¡ä»£ç ï¼æä»¬å°Lua代ç ä¼ å°jedis.eval()æ¹æ³éï¼å¹¶ä½¿åæ°KEYS[1]èµå¼ä¸ºlockKeyï¼ARGV[1]èµå¼ä¸ºrequestIdãeval()æ¹æ³æ¯å°Lua代ç 交ç»Redisæå¡ç«¯æ§è¡ã
é£ä¹è¿æ®µLua代ç çåè½æ¯ä»ä¹å¢ï¼å ¶å®å¾ç®åï¼é¦å è·åé对åºçvalueå¼ï¼æ£æ¥æ¯å¦ä¸requestIdç¸çï¼å¦æç¸çåå é¤éï¼è§£éï¼ãé£ä¹ä¸ºä»ä¹è¦ä½¿ç¨Luaè¯è¨æ¥å®ç°å¢ï¼å 为è¦ç¡®ä¿ä¸è¿°æä½æ¯ååæ§çã
ç®åæ¥è¯´ï¼å°±æ¯å¨evalå½ä»¤æ§è¡Lua代ç çæ¶åï¼Lua代ç å°è¢«å½æä¸ä¸ªå½ä»¤å»æ§è¡ï¼å¹¶ä¸ç´å°evalå½ä»¤æ§è¡å®æï¼Redisæä¼æ§è¡å ¶ä»å½ä»¤ã