분산 잠금은 분산 시스템 간의 공유 리소스에 대한 동기 액세스를 제어하는 방법입니다.
분산 시스템에서는 작업을 조정해야 하는 경우가 많습니다. 서로 다른 시스템 또는 동일한 시스템의 서로 다른 호스트가 하나 또는 그룹의 리소스를 공유하는 경우 이러한 리소스에 액세스할 때 상호 간섭을 방지하고 일관성을 보장하기 위해 상호 배제가 필요한 경우가 많습니다. 이 경우 분산 잠금을 사용해야 합니다.
4개의 Redis 명령 setnx, getset,expiration 및 del을 사용하여 구현합니다.(권장 학습: Redis 비디오 튜토리얼)
setnx는 "SET if Not eXists"의 약어입니다(존재하지 않는 경우 세트). 명령 형식: SETNX 키 값, 사용법: 키 키가 존재하지 않는 경우에만 키 값을 value로 설정합니다. 키 키가 이미 존재하는 경우 SETNX 명령은 아무 작업도 수행하지 않습니다. 반환 값: 이 명령은 설정에 성공하면 1을 반환하고 설정에 실패하면 0을 반환합니다.
getset 명령 형식: GETSET 키 값, 키 키 값을 value로 설정하고 설정되기 전 키 키의 이전 값을 반환합니다. 반환 값: 키 키에 이전 값이 없는 경우, 즉 키가 설정되기 전에 키 키가 존재하지 않는 경우 명령은 nil을 반환합니다. 키가 존재하지만 문자열 유형이 아닌 경우 명령은 오류를 반환합니다.
expire 명령 형식: EXPIRE 키 초, 사용법: 해당 키의 생존 시간을 설정합니다. 키가 만료되면(생존 시간이 0) 자동으로 삭제됩니다. 반환 값: 설정에 성공하면 1이 반환됩니다. 키가 존재하지 않거나 키의 수명을 설정할 수 없는 경우(예: Redis 2.1.3 이전 버전에서 키 수명을 업데이트하려고 하는 경우) 0이 반환됩니다.
del 명령 형식: DEL 키 [key...], 사용법: 하나 이상의 지정된 키를 삭제하고, 존재하지 않는 키는 무시됩니다. 반환 값: 삭제된 키의 수.
Redis의 분산 잠금 구현 원리:
1. setnx(lock_timeout)을 통해 구현됩니다. 잠금이 설정된 경우 1을 반환하고, 성공적으로 설정되지 않은 값이 있으면 0을 반환합니다. 2. 교착상태 문제: 실습을 통해 판단 만료된 경우 만료 시간 get(lockKey)을 얻은 후 getset(lock_timeout)과 동일한지 확인합니다. 여러 스레드가 동시에 getset(lock_timeout) 메서드를 실행하게 할 수 있으므로 잠금이 성공적으로 잠겼음을 증명합니다. 결과적으로 다중 스레드는 잠금을 결정하는 스레드에 대해 getset만 필요합니다. 성공하면 여러 스레드가 동시에 시간을 겹쳐서 잠금 에이징 시간이 두 배로 늘어나는 것을 방지하기 위해 만료(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS) 만료 시간을 추가합니다.
코드: /**
* @author yaoxin
* @date 2018/8/13下午5:04
*/
public class RedisLockTest {
public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8";
public static final String name = "com.mysql.jdbc.Driver";
public static final String user = "root";
public static final String password = "";
public static void main(String[] args) {
Integer count = 50;
while (count > 0) {
count--;
new Thread(new Runnable() {
@Override
public void run() {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.auth("1234");
String lock = lock(jedis);
if (lock != null) {
Statement statement = null;
Connection conn = null;
ResultSet resultSet = null;
try {
Class.forName(name);// 指定连接类型
conn = DriverManager.getConnection(url, user, password);// 获取连接
statement = conn.createStatement();// 准备执行语句
String querySql = "SELECT id,name,count FROM production WHERE id=2";
resultSet = statement.executeQuery(querySql);
int count = 0;
while (resultSet.next()) {
System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id")
+ " name: " + resultSet.getString("name")
+ " count: " + resultSet.getString("count"));
count = Integer.valueOf(resultSet.getString("count"));
}
String updateSql = "UPDATE production SET count=" + (count - 1)
+ " WHERE id=2";
int rows = statement.executeUpdate(updateSql);
if (rows > 0) {
System.out.println("更新成功" + Thread.currentThread().getName() + " 库存剩余:" + (count - 1));
System.out.println(Thread.currentThread().getName() + " === > >开始解锁");
boolean unlock = unlock(jedis, lock);
if (unlock)
System.out.println(Thread.currentThread().getName() + " === > >解锁成功");
} else {
System.out.println("更新失败" + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null)
conn.close();
if (statement != null)
statement.close();
if (resultSet != null)
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}, "线程" + count).start();
}
}
public static String lock(Jedis jedis) {
try {
while (true) {
String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + "";
if (jedis.setnx("lock", lockTime) == 1) {
jedis.expire("lock", 5);
return lockTime;
}
String lock = jedis.get("lock");
if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) {
String oldLockTime = jedis.getSet("lock", lockTime);
if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) {
return lockTime;
}
}
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static boolean unlock(Jedis jedis, String lockTag) {
if (lockTag.equals(jedis.get("lock"))) {
jedis.del("lock");
return true;
}
return false;
}
}
더 많은 Redis 관련 기술 기사를 보려면
Redis 시작 튜토리얼위 내용은 Redis 분산 잠금 원리를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!