#分散ロックは、分散システム間の共有リソースへの同期アクセスを制御する方法です。
分散システムでは、多くの場合、それらのアクションを調整する必要があります。異なるシステムまたは同じシステムの異なるホストが 1 つまたはグループのリソースを共有する場合、これらのリソースにアクセスするときは、相互干渉を防止し、一貫性を確保するために相互排他が必要になることがよくあります。この場合、分散ロックを使用する必要があります。
4 つの Redis コマンド setnx、getset、expire、del を使用して を実装します (推奨学習: Redis ビデオ チュートリアル )
setnx は『 SET 「if Not eXists」の略称(存在しない場合はSET)。コマンド形式: SETNX key value; 使用方法: key key が存在しない場合にのみ、key key の値を value に設定します。キー key がすでに存在する場合、SETNX コマンドは何も行いません。戻り値:設定成功時は1、設定失敗時は0を返します。
getset コマンド形式: GETSET key value、キー key の値を value に設定し、設定される前のキー key の古い値を返します。戻り値: キー key に古い値がない場合、つまり、キー key が設定される前に存在しない場合、コマンドは nil を返します。キー key は存在するが、文字列型ではない場合、コマンドはエラーを返します。
expire コマンド形式: EXPIRE key 秒、使用法: 指定されたキーの生存時間を設定します。キーの有効期限が切れると (生存時間が 0 になると)、キーは自動的に削除されます。戻り値: 設定が成功した場合は1を返します。キーが存在しない場合、またはキーの有効期間を設定できない場合 (たとえば、2.1.3 より前のバージョンの Redis でキーの有効期間を更新しようとした場合)、0 が返されます。
del コマンド形式: DEL キー [キー...]、使用法: 1 つ以上の指定されたキーを削除します。存在しないキーは無視されます。戻り値: 削除されたキーの数。
Redis による分散ロック実装の原則:
1. setnx(lock_timeout) を通じて実装され、ロックが設定されている場合は 1 を返し、すでにロックが設定されている場合は 1 を返します。
2. デッドロック問題: 有効期限が切れているかどうかを実践して判断し、有効期限が切れている場合は有効期限を取得する get(lockKey)、次に getset(lock_timeout) ) を使用して get と同じかどうかを判断します。同じであれば、ロックが正常にロックされたことが証明されます。これは、複数のスレッドが getset(lock_timeout) メソッドを同時に実行する可能性があるためです。
#コード: #/**
* @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 中国語 Web サイトの他の関連記事を参照してください。