Verteilte Sperre ist eine Möglichkeit, den synchronen Zugriff auf gemeinsam genutzte Ressourcen zwischen verteilten Systemen zu steuern.
In verteilten Systemen ist es oft notwendig, ihre Aktionen zu koordinieren. Wenn verschiedene Systeme oder verschiedene Hosts desselben Systems eine oder eine Gruppe von Ressourcen gemeinsam nutzen, ist beim Zugriff auf diese Ressourcen häufig ein gegenseitiger Ausschluss erforderlich, um gegenseitige Beeinträchtigungen zu verhindern und die Konsistenz sicherzustellen. In diesem Fall müssen verteilte Sperren verwendet werden.
Verwenden Sie die vier Redis-Befehle Setnx, Getset, Expire und Del, um zu implementieren (Empfohlenes Lernen: Redis-Video-Tutorial)
Setnx ist 『 SET Abkürzung für „if Not eXists“ (wenn es nicht existiert, dann SET). Befehlsformat: SETNX-Schlüsselwert; Verwendung: Setzen Sie den Wert von Schlüsselschlüssel nur dann auf Wert, wenn Schlüsselschlüssel nicht vorhanden ist. Wenn der Schlüsselschlüssel bereits vorhanden ist, führt der Befehl SETNX nichts aus. Rückgabewert: Der Befehl gibt 1 zurück, wenn die Einstellung erfolgreich ist, und 0, wenn die Einstellung fehlschlägt.
getset-Befehlsformat: GETSET-Schlüsselwert, setzen Sie den Wert von key key auf value und geben Sie den alten Wert von key key zurück, bevor er festgelegt wurde. Rückgabewert: Wenn der Schlüssel key keinen alten Wert hat, d. h. der Schlüssel key nicht existiert, bevor er festgelegt wird, gibt der Befehl Null zurück. Wenn der Schlüssel vorhanden ist, aber nicht vom Typ „String“ ist, gibt der Befehl einen Fehler zurück.
Ablaufbefehlsformat: EXPIRE-Schlüsselsekunden, Verwendung: Legen Sie die Überlebenszeit für einen bestimmten Schlüssel fest. Wenn der Schlüssel abläuft (die Überlebenszeit ist 0), wird er automatisch gelöscht. Rückgabewert: 1 wird zurückgegeben, wenn die Einstellung erfolgreich ist. Wenn der Schlüssel nicht vorhanden ist oder die Lebensdauer für den Schlüssel nicht festgelegt werden kann (z. B. wenn Sie versuchen, die Lebensdauer des Schlüssels in einer Redis-Version vor 2.1.3 zu aktualisieren), wird 0 zurückgegeben.
del-Befehlsformat: DEL-Taste [Taste...], Verwendung: Löschen Sie einen oder mehrere angegebene Schlüssel, nicht vorhandene Schlüssel werden ignoriert. Rückgabewert: Die Anzahl der gelöschten Schlüssel.
Das Prinzip der Implementierung verteilter Sperren durch Redis:
1 Wird durch setnx(lock_timeout) implementiert. Wenn die Sperre gesetzt ist, wird 1 zurückgegeben, wenn bereits eine vorhanden ist Wert, es gibt 0 zurück, wenn es nicht erfolgreich eingestellt wurde ), um festzustellen, ob es mit get identisch ist. Dies beweist, dass die Sperre erfolgreich gesperrt wurde, da dies dazu führen kann, dass mehrere Threads gleichzeitig die getset(lock_timeout)-Methode ausführen Für den Thread, der feststellt, dass die Sperre erfolgreich ist, müssen mehrere Threads nur die Ablaufzeit „expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)“ hinzufügen, um zu verhindern, dass sich mehrere Threads gleichzeitig überlagern, wodurch sich die Alterungszeit der Sperre verdoppelt 🎜>
Code:/** * @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; } }Das Laufergebnis ist wie folgt:
Weitere technische Artikel zu Redis finden Sie in der Spalte Redis Getting Started Tutorial
, um mehr darüber zu erfahren!Das obige ist der detaillierte Inhalt vonSo implementieren Sie das Prinzip der verteilten Redis-Sperre. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!