Derzeit werden fast alle großen Websites und Anwendungen verteilt bereitgestellt. Das Problem der Datenkonsistenz in verteilten Szenarien war schon immer ein wichtiges Thema. Die Theorie des verteilten CAP besagt, dass „kein verteiltes System Konsistenz, Verfügbarkeit und Partitionstoleranz gleichzeitig erfüllen kann, sondern nur zwei davon gleichzeitig.“ Daher müssen viele Systeme zu Beginn ihres Entwurfs Kompromisse zwischen diesen drei Kriterien eingehen. In den meisten Szenarien im Internetbereich muss eine starke Konsistenz zugunsten einer hohen Systemverfügbarkeit geopfert werden. Das System muss häufig nur eine „eventuelle Konsistenz“ gewährleisten, solange die Endzeit für den Benutzer im akzeptablen Bereich liegt. Um die endgültige Konsistenz der Daten sicherzustellen, benötigen wir in vielen Szenarien zahlreiche technische Lösungen zur Unterstützung, beispielsweise verteilte Transaktionen und verteilte Sperren. Manchmal müssen wir sicherstellen, dass eine Methode nur vom selben Thread gleichzeitig ausgeführt werden kann. In einer eigenständigen Umgebung stellt Java tatsächlich viele APIs für die gleichzeitige Verarbeitung bereit, in verteilten Szenarien sind diese APIs jedoch machtlos. Mit anderen Worten: Eine reine Java-API kann keine verteilten Sperrfunktionen bereitstellen. Daher gibt es derzeit mehrere Lösungen für die Implementierung verteilter Sperren. Zur Implementierung verteilter Sperren werden derzeit häufig folgende Lösungen verwendet: Implementieren Sie verteilte Sperren basierend auf Datenbanken. Implementieren Sie verteilte Sperren basierend auf Caches (Redis, Memcached, Tair). Bevor wir diese Implementierungslösungen analysieren, überlegen wir, welche Art von verteilter Sperre wir benötigen. (Hier nehmen wir die Methodensperre als Beispiel, dasselbe gilt für die Ressourcensperre) Es kann sichergestellt werden, dass in einem verteilten Anwendungscluster dieselbe Methode gleichzeitig nur von einem Thread auf einer Maschine ausgeführt werden kann. Implementierung verteilter Sperren basierend auf der Datenbank Basierend auf Datenbanktabelle Der einfachste Weg zum Implementieren verteilter Sperren besteht möglicherweise darin, direkt eine Sperrtabelle zu erstellen und diese dann durch Bearbeiten der Daten in der Tabelle zu implementieren. Erstellen Sie eine Datenbanktabelle wie folgt: CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primärschlüssel', `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT 'Der gesperrte Methodenname', `desc` varchar(1024) NOT NULL DEFAULT 'Bemerkungen', `update_time` Zeitstempel NICHT NULL STANDARD CURRENT_TIMESTAMP BEI UPDATE CURRENT_TIMESTAMP KOMMENTAR ‚Datenzeit speichern, automatisch generiert‘, Primärschlüssel (`id`), EINZIGARTIGER SCHLÜSSEL `uidx_method_name` (`method_name`) MIT BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Sperrmethode'; Wenn wir eine Methode sperren möchten, führen Sie das folgende SQL aus: in methodLock(method_name,desc) Werte einfügen ('method_name','desc') Da wir eine eindeutige Einschränkung für method_name vorgenommen haben, stellt die Datenbank sicher, dass nur eine Operation erfolgreich sein kann, wenn mehrere Anforderungen gleichzeitig an die Datenbank gesendet werden. Dann können wir davon ausgehen, dass der Thread, der erfolgreich ausgeführt wird, die Sperre der Methode erhalten hat und den Inhalt des Methodenkörpers ausführen kann. Wenn Sie nach Ausführung der Methode die Sperre aufheben möchten, müssen Sie das folgende SQL ausführen: Löschen aus methodLock, wobei method_name = "method_name" Die obige einfache Implementierung weist die folgenden Probleme auf: 1. Diese Sperre hängt in hohem Maße von der Verfügbarkeit der Datenbank ab. Die Datenbank ist ein einzelner Punkt. Wenn die Datenbank ausfällt, ist das Geschäftssystem nicht mehr verfügbar. Natürlich können wir die oben genannten Probleme auch anders lösen. Ist die Datenbank ein einzelner Punkt? Erstellen Sie zwei Datenbanken und synchronisieren Sie die Daten in beide Richtungen. Sollte dies fehlschlagen, wechseln Sie schnell zur Backup-Datenbank. Basierend auf einer exklusiven Datenbanksperre Neben dem Hinzufügen und Löschen von Datensätzen in der Datentabelle können Sie auch die in den Daten integrierten Sperren verwenden, um verteilte Sperren zu implementieren. Wir verwenden auch die Datenbanktabelle, die wir gerade erstellt haben. Verteilte Sperren können durch exklusive Sperren der Datenbank implementiert werden. Basierend auf der MySQL InnoDB-Engine können Sie die folgenden Methoden verwenden, um Sperrvorgänge zu implementieren: öffentliches Boolean-Schloss(){ Verbindung.setAutoCommit(false) während(wahr){ versuchen{ Ergebnis = Auswahl * aus methodLock, wobei method_name=xxx für Update; wenn(Ergebnis==null){ gibt true zurück; } } Fang(Ausnahme e){ } Schlaf (1000); } gibt false zurück; } Fügen Sie nach der Abfrageanweisung „for update“ hinzu, und die Datenbank fügt während des Abfragevorgangs eine exklusive Sperre zur Datenbanktabelle hinzu (ich möchte hier erwähnen, dass die InnoDB-Engine beim Sperren beim Durchsuchen von Indizes nur Sperren auf Zeilenebene verwendet, andernfalls Sperren auf Tabellenebene. Hier möchten wir Sperren auf Zeilenebene verwenden, daher müssen wir dem Methodennamen einen Index hinzufügen. Es ist erwähnenswert, dass dieser Index als eindeutiger Index erstellt werden muss, da sonst das Problem auftritt, dass nicht gleichzeitig auf mehrere überladene Methoden zugegriffen werden kann. Bei überladenen Methoden wird empfohlen, auch den Parametertyp hinzuzufügen.). Nachdem einem Datensatz eine exklusive Sperre hinzugefügt wurde, können andere Threads der Datensatzzeile keine exklusiven Sperren mehr hinzufügen. Wir können davon ausgehen, dass der Thread, der die exklusive Sperre erhält, auch die verteilte Sperre erhalten kann. Nach Erhalt der Sperre kann die Geschäftslogik der Methode ausgeführt werden. Nach Ausführung der Methode kann sie mit der folgenden Methode entsperrt werden: öffentliches Void entsperren(){ Verbindung.commit(); } Geben Sie die Sperre durch die Operation connection.commit() frei. Mit dieser Methode lassen sich die oben genannten Probleme der Unmöglichkeit, Sperren freizugeben bzw. zu blockieren, wirksam lösen. Sperrschloss? Die Anweisung for update wird nach der erfolgreichen Ausführung sofort zurückgegeben und bleibt bei einem Fehlschlag der Ausführung bis zum Erfolg blockiert. Allerdings können damit die Single-Point- und Reentrancy-Probleme der Datenbank immer noch nicht direkt gelöst werden. Möglicherweise liegt hier noch ein anderes Problem vor, obwohl wir für method_name einen eindeutigen Index verwenden und für Updates ausdrücklich Zeilensperren verwenden. MySql optimiert jedoch die Abfrage. Selbst wenn das Indexfeld in der Bedingung verwendet wird, wird MySQL anhand der Kosten verschiedener Ausführungspläne entscheiden, ob der Index zum Abrufen von Daten verwendet werden soll. Wenn MySQL der Ansicht ist, dass ein vollständiger Tabellenscan effizienter ist, z. B. bei einigen sehr kleinen Tabellen, wird der Index nicht verwendet. In diesem Fall verwendet InnoDB Tabellensperren anstelle von Zeilensperren. Es wäre tragisch, wenn dies passieren würde. . . Ein weiteres Problem besteht darin, dass, wenn wir exklusive Sperren zum Sperren verteilter Sperren verwenden, die Datenbankverbindung belegt wird, wenn für längere Zeit keine exklusive Sperre übermittelt wird. Sobald es zu viele ähnliche Verbindungen gibt, kann der Datenbankverbindungspool überlaufen. Zusammenfassen Um die Möglichkeiten zur Implementierung verteilter Sperren mithilfe von Datenbanken zusammenzufassen: Beide Methoden basieren auf einer Tabelle in der Datenbank. Eine besteht darin, anhand der Existenz von Datensätzen in der Tabelle zu bestimmen, ob derzeit eine Sperre vorhanden ist, und die andere besteht darin, verteilte Sperren durch exklusive Sperren in der Datenbank zu implementieren. Vorteile verteilter Sperren in Datenbanken Verlassen Sie sich direkt auf die Datenbank, leicht zu verstehen. Nachteile verteilter Sperren in Datenbanken Es wird alle möglichen Probleme geben und im Laufe der Lösung dieser Probleme wird der gesamte Plan immer komplizierter. Implementierung verteilter Sperren basierend auf Cache Verglichen mit der Lösung, bei der verteilte Sperren auf Datenbankbasis implementiert werden, ist die Leistung der Cache-basierten Lösung besser. Darüber hinaus können viele Caches in Clustern eingesetzt werden, um Einzelpunktprobleme zu lösen. In unserem Unternehmen sind viele ausgereifte Cache-Produkte verfügbar, darunter Redis, Memcached und Tair. Hier analysieren wir anhand des Beispiels Tair die Lösung, bei der Cache zur Implementierung verteilter Sperren verwendet wird. Es gibt viele Artikel über Redis und Memcached im Internet und es gibt auch einige ausgereifte Frameworks und Algorithmen, die direkt verwendet werden können. Die auf Tair basierende verteilte Sperrimplementierung ähnelt tatsächlich Redis und die Hauptimplementierungsmethode besteht darin, die Methode TairManager.put zu verwenden. öffentliches Boolean-Trylock (String-Schlüssel) { ResultCode code = ldbTairManager.put(NAMESPACE, key, "Dies ist ein Schloss.", 2, 0); wenn (ErgebnisCode.SUCCESS.equals(code)) gibt true zurück; anders gibt false zurück; } öffentliches Boolean-Entsperren (String-Schlüssel) { ldbTairManager.invalid(NAMESPACE, Schlüssel); } Die obige Implementierung weist außerdem mehrere Probleme auf: 1. Diese Sperre hat keine Ablaufzeit. Wenn der Entsperrvorgang fehlschlägt, bleibt der Sperrdatensatz in Tair und andere Threads können die Sperre nicht mehr erhalten. Natürlich gibt es auch Möglichkeiten, dieses Problem zu lösen. Kein Ablaufdatum? Die Put-Methode von Tair unterstützt die Übergabe einer Ablaufzeit und die Daten werden nach Erreichen der Zeit automatisch gelöscht. Aber auf welchen langen Wert soll ich die Ablaufzeit einstellen? Wenn die Ablaufzeit zu kurz eingestellt ist, wird die Sperre automatisch aufgehoben, bevor die Methode ausgeführt wird, was zu Parallelitätsproblemen führt. Wenn die Zeit zu lang eingestellt ist, müssen andere Threads, die die Sperre erhalten, möglicherweise länger warten. Dieses Problem besteht auch bei der Verwendung einer Datenbank zur Implementierung verteilter Sperren Zusammenfassen Anstelle einer Datenbank kann ein Cache verwendet werden, um verteilte Sperren zu implementieren, was zu einer besseren Leistung führen kann. Gleichzeitig werden viele Cache-Dienste in Clustern bereitgestellt, um Einzelpunktprobleme zu vermeiden. Darüber hinaus bieten viele Cache-Dienste Methoden, mit denen verteilte Sperren implementiert werden können, z. B. die Put-Methode von Tair und die Setnx-Methode von Redis. Darüber hinaus unterstützen diese Cache-Dienste auch das automatische Löschen abgelaufener Daten und Sie können die Timeout-Zeit direkt festlegen, um die Freigabe der Sperre zu steuern. Vorteile der Verwendung von Cache zur Implementierung verteilter Sperren Gute Leistung und einfach zu implementieren. Nachteile der Verwendung von Cache zur Implementierung verteilter Sperren Die Steuerung des Ablaufzeitpunkts der Sperre durch ein Timeout ist nicht sehr zuverlässig. Implementierung verteilter Sperren basierend auf Zookeeper Verteilte Sperren können basierend auf temporär geordneten Knoten des Zookeepers implementiert werden. Die allgemeine Idee ist: Wenn jeder Client eine Methode sperrt, wird im Verzeichnis des angegebenen Knotens, der der Methode in ZooKeeper entspricht, ein eindeutiger, sofort geordneter Knoten generiert. Die Methode zum Bestimmen, ob das Schloss erworben werden soll, ist sehr einfach. Sie müssen nur das Schloss mit der kleinsten Seriennummer im geordneten Knoten bestimmen. Wenn die Sperre aufgehoben wird, löschen Sie einfach den vorübergehenden Knoten. Gleichzeitig können Deadlock-Probleme vermieden werden, die durch die Unfähigkeit entstehen, Sperren aufgrund von Dienstausfallzeiten freizugeben. Mal sehen, ob Zookeeper die oben genannten Probleme lösen kann. Die Sperre lässt sich nicht lösen? Die Verwendung von Zookeeper kann das Problem nicht freigegebener Sperren effektiv lösen, da der Client beim Erstellen einer Sperre einen temporären Knoten in ZK erstellt. Sobald der Client nach dem Erwerb der Sperre plötzlich auflegt (die Sitzungsverbindung wird getrennt), wird der temporäre Knoten automatisch gelöscht. Andere Clients können die Sperre dann erneut erwerben. Kein Wiedereinsteiger? Die Verwendung von Zookeeper kann auch das Problem der Nichtwiedereintrittsmöglichkeit effektiv lösen. Wenn der Client einen Knoten erstellt, werden die Host- und Thread-Informationen des aktuellen Clients direkt in den Knoten geschrieben. Wenn Sie das nächste Mal die Sperre erhalten möchten, können Sie sie einfach mit den Daten im aktuell kleinsten Knoten vergleichen. Wenn die Informationen mit Ihren eigenen übereinstimmen, erhalten Sie direkt die Sperre. Wenn sie unterschiedlich sind, erstellen Sie einen temporären sequentiellen Knoten, um an der Warteschlange teilzunehmen. Einzelner Problempunkt? Die Verwendung von Zookeeper kann das Einzelpunktproblem effektiv lösen. ZK wird in einem Cluster bereitgestellt. Solange mehr als die Hälfte der Maschinen im Cluster aktiv sind, kann es Dienste für die Außenwelt bereitstellen. Sie können den Curator-Client der Drittanbieterbibliothek von Zookeeper direkt verwenden, der einen Reentrant-Lock-Dienst kapselt. öffentliches boolean tryLock(langes Timeout, TimeUnit-Einheit) wirft InterruptedException { versuchen { gibt interProcessMutex.acquire (Timeout, Einheit) zurück; } Fang (Ausnahme e) { e.printStackTrace(); } gibt true zurück; } öffentliches Boolean-Entsperren () { versuchen { interProcessMutex.release(); } fangen (Wurfbares e) { log.error(e.getMessage(), e); Endlich executorService.schedule(neuer Cleaner(Client, Pfad), delayTimeForClean, Zeiteinheit.MILLISEKUNDEN); } gibt true zurück; } InterProcessMutex von Curator ist eine Implementierung einer verteilten Sperre. Mit der Methode „acquire“ wird die Sperre erworben, und mit der Methode „release“ wird die Sperre freigegeben. Das mit ZK implementierte verteilte Schloss scheint alle unsere Erwartungen an ein verteiltes Schloss zu Beginn dieses Artikels vollständig zu erfüllen. Dies ist jedoch nicht der Fall. Die von Zookeeper implementierte verteilte Sperre hat tatsächlich einen Nachteil, nämlich dass ihre Leistung möglicherweise nicht so hoch ist wie die des Cache-Dienstes. Denn jedes Mal, wenn eine Sperre erstellt und freigegeben wird, müssen Momentanknoten dynamisch erstellt und zerstört werden, um die Sperrfunktion zu implementieren. In ZK kann das Erstellen und Löschen von Knoten nur über den Leader-Server erfolgen, und dann können die Daten nicht auf allen Follower-Maschinen geteilt werden. Tatsächlich kann die Verwendung von Zookeeper auch Parallelitätsprobleme verursachen, diese kommen jedoch nicht häufig vor. Stellen Sie sich folgende Situation vor: Aufgrund von Netzwerkschwankungen wird die Sitzungsverbindung zwischen dem Client und dem ZK-Cluster unterbrochen. Dann glaubt ZK, dass der Client aufgelegt hat, und löscht den temporären Knoten. Zu diesem Zeitpunkt können andere Clients die verteilte Sperre erhalten. Es können Parallelitätsprobleme auftreten. Dieses Problem tritt nicht häufig auf, da zk über einen Wiederholungsmechanismus verfügt. Sobald der zk-Cluster den Heartbeat des Clients nicht erkennen kann, wird er einen erneuten Versuch starten. Der Curator-Client unterstützt mehrere Wiederholungsstrategien. Der temporäre Knoten wird nur gelöscht, wenn er nach mehreren Wiederholungsversuchen fehlschlägt. (Daher ist es auch wichtig, eine geeignete Wiederholungsstrategie zu wählen und ein Gleichgewicht zwischen der Granularität der Sperre und der Parallelität zu finden.) Zusammenfassen Vorteile der Verwendung von Zookeeper zur Implementierung verteilter Sperren Lösen Sie effektiv Einzelpunktprobleme, Nicht-Wiedereintrittsprobleme, Nicht-Blockierungsprobleme und Probleme, bei denen Sperren nicht freigegeben werden können. Die Umsetzung ist relativ einfach. Nachteile der Verwendung von Zookeeper zur Implementierung verteilter Sperren Die Leistung ist nicht so gut wie bei der Verwendung eines Caches zur Implementierung verteilter Sperren. Sie müssen ein gewisses Verständnis der Prinzipien von ZK haben. Vergleich dreier Lösungen Keine der oben genannten Methoden kann perfekt sein. Genau wie bei CAP ist es unmöglich, die Anforderungen an Komplexität, Zuverlässigkeit, Leistung usw. gleichzeitig zu erfüllen. Daher besteht der beste Weg darin, je nach Anwendungsszenario das am besten geeignete auszuwählen. Aus der Perspektive der Verständnisschwierigkeit (von niedrig bis hoch) Aus der Perspektive der Implementierungskomplexität (niedrig bis hoch) Aus Leistungssicht (hoch bis niedrig) Aus der Perspektive der Zuverlässigkeit (hoch bis niedrig) Zusammenfassen Dies ist der gesamte Inhalt dieses Artikels zur detaillierten Interpretation der Prinzipien verteilter Sperren und drei Implementierungsmethoden. Interessierte Freunde können weiterhin Folgendes nachlesen: Detaillierte Erklärung von Mutex-Sperrsemaphoren und Multithread-Wartemechanismen in Java, detailliertes Beispiel für die Verwendung von Apache Zookeeper, mehrere wichtige MySQL-Variablen und andere verwandte Themen dieser Site. Ich hoffe, es wird für alle hilfreich sein. Wenn Sie Fragen haben, hinterlassen Sie bitte jederzeit eine Nachricht. Der Herausgeber wird rechtzeitig antworten, um Ihnen ein besseres Leseerlebnis und Hilfe zu bieten. Vielen Dank, Freunde, für Ihre Unterstützung dieser Site! Das könnte Sie auch interessieren:
|
<<: Verstehen Sie die Prinzipien und Anwendungen von JSONP in einem Artikel
>>: Zusammenfassung der Verwendung von TypeScript in React-Projekten
Das Document Object Model (DOM) ist eine Plattfor...
1. Docker zieht das Image Docker Pull MySQL (stan...
Anaconda bezeichnet eine Open-Source-Python-Distr...
Um die Tabelle zu verschönern, können Sie für die...
Verwenden Sie Indizes, um Abfragen zu beschleunig...
MySQL Einführung in MySQL MySQL war ursprünglich ...
Heute lernen wir, wie man mit CSS eine coole Bild...
1. Befehlseinführung nl (Anzahl der Zeilen) fügt ...
Inhaltsverzeichnis 1. Schlaffunktion 2. setTimeou...
Was ist Textumbruch um Bilder? Dies ist die Auswi...
Eine MySQL-ähnliche PHP-Switch-Case-Anweisung. wä...
Im Allgemeinen werden Java-Lernprogramme und Bere...
Umfeld Centos 6.6 MySQL 5.7 Installieren Falls da...
Ich habe gestern gerade etwas HTML gelernt und kon...
Bei meinen letzten Studien habe ich einige Layout...