{"id":37956,"date":"2019-10-31T22:20:47","date_gmt":"2019-10-31T19:20:47","guid":{"rendered":"https:\/\/prohoster.info\/blog\/newsql-nosql-acid\/"},"modified":"2019-10-31T22:20:47","modified_gmt":"2019-10-31T19:20:47","slug":"newsql-nosql-acid","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/newsql-nosql-acid","title":{"rendered":"NewSQL = NoSQL+ACID","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/973145efa566bca10ffe7ba95733a1d5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nBis vor kurzem wurden in Odnoklassniki etwa 50 TB an in Echtzeit verarbeiteten Daten in SQL Server gespeichert. Bei einem solch gro\u00dfen Datenvolumen ist es nahezu unm\u00f6glich, schnellen und zuverl\u00e4ssigen, ausfallsicheren Zugang zu einem Rechenzentrum zu gew\u00e4hrleisten, wenn SQL-Datenbanken verwendet werden. In solchen F\u00e4llen verwendet man normalerweise eine der NoSQL-Datenbanken, doch nicht alles l\u00e4sst sich in NoSQL \u00fcbertragen: Einige Entit\u00e4ten erfordern ACID-Transaktionsgarantien. <\/p>\n<p>Das hat uns zur Nutzung eines NewSQL-Speichers gef\u00fchrt, also einer Datenbank, die die Ausfallsicherheit, Skalierbarkeit und Geschwindigkeit von NoSQL-Systemen bietet, aber gleichzeitig die f\u00fcr klassische Systeme gewohnten ACID-Garantien beibeh\u00e4lt. Es gibt nur wenige funktionierenden Industriel\u00f6sungen dieser neuen Klasse, weshalb wir selbst ein solches System entwickelt und in den produktiven Betrieb genommen haben. <\/p>\n<p>Wie das funktioniert und was wir erreicht haben \u2013 lese weiter unten.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nHeute hat die monatliche Nutzerzahl von \u201eOdnoklassniki\u201c mehr als 70 Millionen einzigartige Besucher. Wir <noindex><a rel=\"nofollow\" href=\"https:\/\/www.similarweb.com\/top-websites\/category\/internet-and-telecom\/social-network\">geh\u00f6ren zu den f\u00fcnf gr\u00f6\u00dften<\/a><\/noindex> Sozialnetzwerken der Welt und zu den zwanzig Webseiten, auf denen Nutzer die meiste Zeit verbringen. Die Infrastruktur von \u201eOK\u201c verarbeitet sehr hohe Lasten: \u00fcber eine Million HTTP-Anfragen pro Sekunde an die Frontend-Server. Teile des Serverparks mit \u00fcber 8000 Einheiten sind eng beieinander in vier Rechenzentren in Moskau untergebracht, was eine Netzwerkverz\u00f6gerung von weniger als 1 ms zwischen ihnen erm\u00f6glicht.<\/p>\n<p>Wir nutzen Cassandra seit 2010, beginnend mit Version 0.6. Heute betreiben wir mehrere Dutzend Cluster. Der schnellste Cluster verarbeitet mehr als 4 Millionen Operationen pro Sekunde, und der gr\u00f6\u00dfte speichert 260 TB. <\/p>\n<p>Allerdings sind das alles g\u00e4ngige NoSQL-Cluster, die zur Speicherung <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%BE%D0%B3%D0%BB%D0%B0%D1%81%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%B2_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D0%BC_%D1%81%D1%87%D1%91%D1%82%D0%B5\">wenig konsistenter<\/a><\/noindex> Daten verwendet werden. Wir wollten jedoch das prim\u00e4re konsistente Speichersystem, Microsoft SQL Server, ersetzen, das seit der Gr\u00fcndung von \u201eOdnoklassniki\u201c genutzt wurde. Das Speicher System bestand aus \u00fcber 300 SQL Server Standard Edition-Rechnern, die 50 TB an Gesch\u00e4ftsdaten enthielten. Diese Daten werden im Rahmen von ACID-Transaktionen modifiziert und erfordern <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">hohe Konsistenz.<\/a><\/noindex>.<\/p>\n<p>F\u00fcr die Verteilung der Daten \u00fcber die Knoten von SQL Server haben wir sowohl vertikale als auch horizontale <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">Partitionierung verwendet.<\/a><\/noindex> (Sharding). Historisch gesehen haben wir ein einfaches Schema zur Datenpartitionierung verwendet: Jeder Entit\u00e4t wurde ein Token zugewiesen \u2013 eine Funktion der Entit\u00e4ts-ID. Entit\u00e4ten mit demselben Token wurden auf einen SQL-Server platziert. Die master-detail-Beziehung wurde so realisiert, dass die Tokens des Haupt- und des abgeleiteten Datensatzes immer \u00fcbereinstimmten und sich auf demselben Server befanden. In sozialen Netzwerken werden fast alle Datens\u00e4tze im Namen eines Benutzers erstellt \u2013 das bedeutet, dass alle Benutzerdaten innerhalb eines funktionalen Teilsystems auf demselben Server gespeichert werden. Das hei\u00dft, in Gesch\u00e4ftstransaktionen waren nahezu immer Tabellen eines SQL-Servers beteiligt, was die Konsistenz der Daten durch lokale ACID-Transaktionen ohne die Notwendigkeit einer Nutzung gew\u00e4hrleistete <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">langsamen und unzuverl\u00e4ssigen<\/a><\/noindex> verteilten ACID-Transaktionen.<\/p>\n<p>Dank des Shardings und zur Beschleunigung der SQL-Arbeiten:<\/p>\n<ul>\n<li>Verwenden wir keine Foreign Key Constraints, da die ID einer Entit\u00e4t beim Sharding auf einem anderen Server sein kann.<\/li>\n<li>Verwenden wir keine gespeicherten Prozeduren und Trigger aufgrund der zus\u00e4tzlichen CPU-Belastung der Datenbankmanagementsysteme.<\/li>\n<li>Verwenden wir keine JOINs aufgrund all dessen und der Vielzahl an zuf\u00e4lligen Lesevorg\u00e4ngen von der Festplatte.<\/li>\n<li>Au\u00dferhalb von Transaktionen verwenden wir das Isolationsniveau Read Uncommitted, um Deadlocks zu reduzieren.<\/li>\n<li>Wir f\u00fchren nur kurze Transaktionen aus (im Durchschnitt k\u00fcrzer als 100 ms).<\/li>\n<li>Verwenden wir keine Mehrzeilen-UPDATE- und DELETE-Anweisungen aufgrund der hohen Anzahl an Deadlocks \u2013 wir aktualisieren immer nur einen Datensatz.<\/li>\n<li>Abfragen werden immer nur \u00fcber Indizes ausgef\u00fchrt \u2013 eine Abfrage mit einem vollst\u00e4ndigen Tabellen-Scan bedeutet f\u00fcr uns eine \u00dcberlastung der Datenbank und ihren Ausfall.<\/li>\n<\/ul>\n<p>\nDiese Schritte haben es erm\u00f6glicht, nahezu die maximale Leistung aus den SQL-Servern herauszuholen. Dennoch wurden die Probleme immer zahlreicher. Lass uns diese betrachten.<\/p>\n<h2>Probleme mit SQL<\/h2>\n<p><\/p>\n<ul>\n<li>Da wir ein selbst entwickeltes Sharding verwendet haben, wurden neue Shards manuell von den Administratoren hinzugef\u00fcgt. W\u00e4hrend dieser ganzen Zeit bedienten skalierbare Datenreplikate keine Anfragen. <\/li>\n<li>Mit dem Anstieg der Anzahl der Datens\u00e4tze in der Tabelle verringert sich die Geschwindigkeit von Einf\u00fcgungen und Modifikationen, beim Hinzuf\u00fcgen von Indizes zu bestehenden Tabellen sinkt die Geschwindigkeit exponentiell, die Erstellung und Rekonstruktion von Indizes erfolgt mit Ausfallzeiten.<\/li>\n<li>Das Vorhandensein einer kleinen Anzahl von Windows f\u00fcr SQL Server in der Produktion erschwert das Management der Infrastruktur.<\/li>\n<\/ul>\n<p>\nAber das Hauptproblem ist \u2014 <\/p>\n<h2>Fehlertoleranz<\/h2>\n<p>\nEin klassischer SQL-Server hat eine schlechte Fehlertoleranz. Angenommen, Sie haben nur einen Datenbankserver, und dieser f\u00e4llt alle drei Jahre aus. In dieser Zeit funktioniert die Website 20 Minuten lang nicht, was akzeptabel ist. Wenn Sie jedoch 64 Server haben, funktioniert die Website bereits alle drei Wochen nicht. Und wenn Sie 200 Server haben, funktioniert die Website jede Woche nicht. Das ist ein Problem. <\/p>\n<p>Was kann man tun, um die Fehlertoleranz eines SQL-Servers zu erh\u00f6hen? Wikipedia schl\u00e4gt uns vor, einen <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">Hochverf\u00fcgbarkeitscluster<\/a><\/noindex>: zu erstellen, wo im Fall eines Ausfalls eines der Komponenten eine redundante vorhanden ist.<\/p>\n<p>Das erfordert einen Park teurer Hardware: zahlreiche Redundanzen, Glasfaser, gemeinsame Speicher, und auch die Aktivierung des Backups funktioniert unzuverl\u00e4ssig: etwa 10 % der Aktivierungen enden mit dem Ausfall der Backup-Node hinter der Haupt-Node. <\/p>\n<p>Aber der gr\u00f6\u00dfte Nachteil eines solchen Hochverf\u00fcgbarkeitsclusters ist die Nullverf\u00fcgbarkeit bei einem Ausfall des Rechenzentrums, in dem er steht. Bei \u201eOdnoklassniki\u201c gibt es vier Rechenzentren, und wir m\u00fcssen den Betrieb bei einem kompletten Ausfall eines davon sicherstellen.<\/p>\n<p>Daf\u00fcr k\u00f6nnte man <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">Multi-Master<\/a><\/noindex> Replikation nutzen, die in SQL Server integriert ist. Diese L\u00f6sung ist aufgrund der Softwarekosten deutlich teurer und leidet unter gut bekannten Problemen mit der Replikation \u2013 unvorhersehbaren Verz\u00f6gerungen von Transaktionen bei synchroner Replikation und Verz\u00f6gerungen bei der Anwendung von Replikationen (und damit verloren gegangenen Modifikationen) bei asynchroner Replikation. Die implizite <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/replication\/transactional\/peer-to-peer-conflict-detection-in-peer-to-peer-replication\">manuelle Konfliktl\u00f6sung<\/a><\/noindex> macht diese Option f\u00fcr uns v\u00f6llig unbrauchbar.<\/p>\n<p>All diese Probleme erforderten eine radikale L\u00f6sung, und wir begannen mit ihrer detaillierten Analyse. Hier m\u00fcssen wir uns mit dem vertrautmachen, was SQL Server im Wesentlichen macht \u2013 Transaktionen.<\/p>\n<h2>Einfache Transaktion<\/h2>\n<p>\nBetrachten wir die einfachste Transaktion aus Sicht eines SQL-Anwendungsprogrammierers: das Hinzuf\u00fcgen eines Fotos zu einem Album. Alben und Fotos werden in verschiedenen Tabellen gespeichert. Ein Album hat einen Z\u00e4hler f\u00fcr \u00f6ffentliche Fotos. Dann wird eine solche Transaktion in die folgenden Schritte unterteilt: <\/p>\n<ol>\n<li>Wir sperren das Album nach Sch\u00fcssel.<\/li>\n<li>Wir erstellen einen Eintrag in der Fototabelle. <\/li>\n<li>Wenn das Foto einen \u00f6ffentlichen Status hat, erh\u00f6hen wir im Album den Z\u00e4hler f\u00fcr \u00f6ffentliche Fotos, aktualisieren den Eintrag und committen die Transaktion.<\/li>\n<\/ol>\n<p>\nOder in Form von Pseudocode:<\/p>\n<pre><code>TX.start(\"Alben\", id);\nAlbum album = albums.lock(id);\nFoto foto = photos.create(\u2026);\n\nif (foto.status == PUBLIC ) {\n    album.incPublicPhotosCount();\n}\nalbum.update();\n\nTX.commit();<\/code><\/pre>\n<p>\nWir sehen, dass das h\u00e4ufigste Szenario f\u00fcr Gesch\u00e4ftstransaktionen darin besteht, Daten aus der Datenbank in den Arbeitsspeicher der Anwendungsserver zu lesen, etwas zu \u00e4ndern und die neuen Werte zur\u00fcck in die Datenbank zu speichern. In der Regel aktualisieren wir in einer solchen Transaktion mehrere Entit\u00e4ten, mehrere Tabellen. <\/p>\n<p>W\u00e4hrend der Ausf\u00fchrung einer Transaktion kann es zu konkurrierenden Modifizierungen derselben Daten aus einem anderen System kommen. Zum Beispiel kann ein Antispam-System entscheiden, dass ein Benutzer verd\u00e4chtig ist, und daher sollten alle Fotos des Benutzers nicht mehr \u00f6ffentlich sein, sondern zur Moderation gesendet werden, was bedeutet, dass photo.status auf einen anderen Wert ge\u00e4ndert und entsprechende Z\u00e4hler zur\u00fcckgesetzt werden m\u00fcssen. Offensichtlich wird das Ergebnis, wenn diese Operation ohne Garantien f\u00fcr die Atomarit\u00e4t der Anwendung und die Isolation konkurrierender Modifikationen erfolgt, nicht das sein, was ben\u00f6tigt wird \u2014 entweder wird der Fotoz\u00e4hler einen falschen Wert anzeigen oder nicht alle Fotos werden zur Moderation geschickt. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, dann wird das Ergebnis nicht den Anforderungen entsprechen \u2014 entweder zeigt der Fotocounter einen falschen Wert an oder nicht alle Fotos werden zur Moderation geschickt. <\/p>\n<p>Eine derartige Codierung, die mit verschiedenen Gesch\u00e4ftseinheiten im Rahmen einer einzigen Transaktion umgeht, wurde seit der Gr\u00fcndung von Odnoklassniki in gro\u00dfer Zahl geschrieben. Nach unseren Erfahrungen mit Migrationen zu NoSQL mit <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Eventual Consistency<\/a><\/noindex> wissen wir, dass die gr\u00f6\u00dften Schwierigkeiten (und der Zeitaufwand) darin bestehen, Code zu entwickeln, der darauf abzielt, die Datenkonsistenz aufrechtzuerhalten. Daher hielten wir die Bereitstellung echter ACID-Transaktionen f\u00fcr die Anwendungsebene f\u00fcr die wichtigste Anforderung an den neuen Speicher. <\/p>\n<p>Andere, nicht weniger wichtige Anforderungen waren:<\/p>\n<ul>\n<li>Bei einem Ausfall des Rechenzentrums m\u00fcssen sowohl Lese- als auch Schreibzugriffe auf den neuen Speicher m\u00f6glich sein.<\/li>\n<li>Die aktuelle Geschwindigkeit der Entwicklung muss beibehalten werden. Das bedeutet, dass die Menge an Code bei der Arbeit mit dem neuen Speicher ungef\u00e4hr die gleiche sein sollte, es sollte nicht erforderlich sein, etwas im Speicher hinzuzuf\u00fcgen, Algorithmen zur Konfliktl\u00f6sung, zur Unterst\u00fctzung von Sekund\u00e4rindizes usw. zu entwickeln. <\/li>\n<li>Die Geschwindigkeit des neuen Speichers sollte sowohl beim Lesen von Daten als auch bei der Verarbeitung von Transaktionen ausreichend hoch sein, was effektiv die Unanwendbarkeit akademisch strenger, universeller, aber langsamer L\u00f6sungen wie zum Beispiel bedeutet, <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">Zwei-Phasen-Commit<\/a><\/noindex>.<\/li>\n<li>Automatische Skalierung in Echtzeit. <\/li>\n<li>Verwendung gew\u00f6hnlicher, kosteng\u00fcnstiger Server, ohne dass der Kauf exotischer Hardware erforderlich ist. <\/li>\n<li>Die M\u00f6glichkeit, das Speichersystem mit den Kr\u00e4ften der Unternehmensentwickler weiterzuentwickeln. Mit anderen Worten, die Priorit\u00e4t lag auf internen oder Open-Source-L\u00f6sungen, vorzugsweise in Java.<\/li>\n<\/ul>\n<p><\/p>\n<h2>L\u00f6sungen, L\u00f6sungen<\/h2>\n<p>\nBei der Analyse m\u00f6glicher L\u00f6sungen sind wir zu zwei m\u00f6glichen Architekturentscheidungen gekommen:<\/p>\n<p>Die erste \u2013 einen beliebigen SQL-Server zu verwenden und die ben\u00f6tigte Fehlertoleranz, einen Skalierungsmechanismus, einen ausfallsicheren Cluster, Konfliktl\u00f6sung und verteilte, zuverl\u00e4ssige und schnelle ACID-Transaktionen zu implementieren. Wir haben diese Option als ziemlich untrivial und arbeitsintensiv eingesch\u00e4tzt.<\/p>\n<p>Die zweite Option \u2013 ein fertiges NoSQL-Speichersystem mit implementierter Skalierung und ausfallsicherem Cluster zu verwenden und die Transaktionen und SQL selbst zu implementieren. Auf den ersten Blick erscheint die Aufgabe, SQL zu implementieren, ganz zu schweigen von ACID-Transaktionen, wie eine Aufgabe f\u00fcr Jahre. Doch sp\u00e4ter erkannten wir, dass das Set von SQL-Funktionen, das wir in der Praxis verwenden, so weit von ANSI SQL entfernt ist wie <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> entfernt von ANSI SQL. Bei genauerer Betrachtung von CQL wurde uns klar, dass es ziemlich nah an dem ist, was wir brauchen.<\/p>\n<h2>Cassandra und CQL<\/h2>\n<p>\nAlso, was macht Cassandra so interessant, welche M\u00f6glichkeiten bietet sie?<\/p>\n<p>Erstens kann man hier Tabellen mit Unterst\u00fctzung f\u00fcr verschiedene Datentypen erstellen und kann SELECT oder UPDATE anhand des Prim\u00e4rschl\u00fcssels durchf\u00fchren.<\/p>\n<pre><code>CREATE TABLE photos (id bigint KEY, owner bigint,\u2026);\nSELECT * FROM photos WHERE id=?;\nUPDATE photos SET \u2026 WHERE id=?;<\/code><\/pre>\n<p>\nUm die Konsistenz der Datenreplikate zu gew\u00e4hrleisten, verwendet Cassandra <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">den Quorum-Ansatz<\/a><\/noindex>. Im einfachsten Fall bedeutet dies, dass beim Platzieren von drei Replikaten derselben Zeile auf verschiedenen Knoten des Clusters der Schreibvorgang als erfolgreich gilt, wenn die Mehrheit der Knoten (d.h. zwei von drei) den Erfolg dieser Schreiboperation best\u00e4tigt hat. Die Daten der Zeile werden als konsistent betrachtet, wenn bei der Lesung die Mehrheit der Knoten befragt wurde und dies best\u00e4tigt haben. So wird bei Vorliegen von drei Replikaten vollst\u00e4ndige und sofortige Datenkonsistenz bei Ausfall eines Knotens gew\u00e4hrleistet. Dieser Ansatz erm\u00f6glichte es uns, ein noch zuverl\u00e4ssigeres Schema zu implementieren: Immer Anfragen an alle drei Replikate zu senden und auf die Antworten der zwei schnellsten zu warten. Die versp\u00e4tete Antwort des dritten Replikats wird in diesem Fall verworfen. Der versp\u00e4tete Knoten k\u00f6nnte dabei ernsthafte Probleme haben \u2013 Verz\u00f6gerungen, Garbage Collection in der JVM, Direct Memory Reclaim im Linux-Kernel, Hardwareausf\u00e4lle, Netzwerkausf\u00e4lle. Dies hat jedoch keinerlei Einfluss auf die Operationen des Clients und auf die Daten.<\/p>\n<p>Der Ansatz, bei dem wir auf drei Knoten zugreifen und von zwei eine Antwort erhalten, wird <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">Spekulation<\/a><\/noindex>: die Anfrage an zus\u00e4tzliche Replikate wird gesendet, bevor ein \"Ausfall\" auftritt. <\/p>\n<p>Ein weiterer Vorteil von Cassandra ist das Batchlog \u2013 ein Mechanismus, der entweder die vollst\u00e4ndige Anwendung oder die vollst\u00e4ndige Nicht-Anwendung des von Ihnen vorgenommenen \u00c4nderungssets garantiert. Dies erm\u00f6glicht es uns, A in ACID \u2013 atomar aus der Box \u2013 zu l\u00f6sen.<\/p>\n<p>Das N\u00e4chste, was Transaktionen in Cassandra \u00e4hnelt, ist das sogenannte &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">lightweight transactions<\/a><\/noindex>&#171;. Aber von \u201eechten\u201c ACID-Transaktionen sind sie weit entfernt: In Wirklichkeit ist es die M\u00f6glichkeit, eine <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> auf den Daten eines einzelnen Datensatzes durch Konsens mit dem schwergewichtigen Paxos-Protokoll durchzuf\u00fchren. Daher ist die Geschwindigkeit solcher Transaktionen gering. <\/p>\n<h2>Was uns in Cassandra fehlte<\/h2>\n<p>\nWir mussten also in Cassandra echte ACID-Transaktionen implementieren. Mit deren Hilfe k\u00f6nnten wir zwei weitere n\u00fctzliche Funktionen klassischer DBMS einfach umsetzen: konsistente schnelle Indizes, was es uns erm\u00f6glichen w\u00fcrde, Datenabfragen nicht nur nach dem Prim\u00e4rschl\u00fcssel durchzuf\u00fchren, und normalerweise einen Generator f\u00fcr monotone Autoinkrement-ID.<\/p>\n<h4>C*One<\/h4>\n<p>\nSo entstand ein neues DBMS <b>C*One<\/b>, das aus drei Arten von Serverknoten besteht:<\/p>\n<ul>\n<li>Speicher \u2014 (fast) Standard-Cassandra-Server, die f\u00fcr die Speicherung von Daten auf lokalen Laufwerken verantwortlich sind. Mit zunehmender Last und Datenvolumen kann die Anzahl leicht auf Dutzende oder Hunderte skaliert werden.<\/li>\n<li>Die Transaktionskoordinatoren sorgen f\u00fcr die Ausf\u00fchrung von Transaktionen. <\/li>\n<li>Clients sind Anwendungsserver, die Gesch\u00e4ftsoperationen umsetzen und Transaktionen initiieren. Es k\u00f6nnen Tausende solcher Clients existieren.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/f3809d89404ae596c62b642dbdb9e431.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nServer aller Typen sind in einem gemeinsamen Cluster verbunden, verwenden das interne Messaging-Protokoll von Cassandra zur Kommunikation untereinander und <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">das<\/a><\/noindex> zum Austausch von Clusterinformationen. Durch Heartbeat erfahren die Server von gegenseitigen Ausf\u00e4llen und halten ein einheitliches Datenschema \u2013 Tabellen, deren Struktur und Replikation; das Partitionierungsschema, die Cluster-Topologie usw.<\/p>\n<h4>Clients<\/h4>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/80f83358f215cf59db3b657ba1083420.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nStatt der Standardtreiber wird der Fat Client-Modus verwendet. Knoten in diesem Modus speichern keine Daten, k\u00f6nnen aber als Koordinator der Anfrageausf\u00fchrung fungieren, das hei\u00dft, der Client selbst \u00fcbernimmt die Funktion des Koordinators seiner Anfragen: Er befragt die Replikate des Speichers und l\u00f6st Konflikte. Dies ist nicht nur zuverl\u00e4ssiger und schneller als der Standardtreiber, der Kommunikation mit einem entfernten Koordinator erfordert, sondern erm\u00f6glicht auch die Verwaltung der \u00dcbertragung von Anfragen. Anfragen au\u00dferhalb einer offenen Transaktion auf dem Client werden an die Speicher geleitet. Wenn der Client jedoch eine Transaktion er\u00f6ffnet hat, werden alle Anfragen innerhalb dieser Transaktion an den Transaktionskoordinator gesendet.<br \/>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/709bf227eeb8f71abeff703a72780efd.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h2>Transaktionskoordinator C*One<\/h2>\n<p>\nDer Koordinator ist das, was wir f\u00fcr C*One von Grund auf umgesetzt haben. Er ist verantwortlich f\u00fcr die Verwaltung von Transaktionen, Sperren und der Reihenfolge, in der Transaktionen angewendet werden.<\/p>\n<p>F\u00fcr jede verwaltete Transaktion generiert der Koordinator einen Zeitstempel: jeder nachfolgende ist gr\u00f6\u00dfer als der des vorherigen. Da das Konfliktl\u00f6sungsverfahren in Cassandra auf Zeitstempeln basiert (von zwei konfligierenden Datens\u00e4tzen wird der mit dem sp\u00e4teren Zeitstempel als aktuell betrachtet), wird jeder Konflikt immer zugunsten der nachfolgenden Transaktion gel\u00f6st. So haben wir <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A7%D0%B0%D1%81%D1%8B_%D0%9B%D1%8D%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0\">Lamport-Uhren<\/a><\/noindex> \u2013 eine kosteng\u00fcnstige Methode zur Konfliktl\u00f6sung in verteilten Systemen.<\/p>\n<h2>Sperren<\/h2>\n<p>\nZur Gew\u00e4hrleistung der Isolation haben wir uns f\u00fcr die einfachste Methode entschieden \u2013 pessimistische Sperren nach dem Prim\u00e4rschl\u00fcssel des Datensatzes. Mit anderen Worten, in der Transaktion muss der Datensatz zun\u00e4chst gesperrt werden, bevor er gelesen, modifiziert und gespeichert werden kann. Erst nach einem erfolgreichen Commit kann der Datensatz entsperrt werden, damit konkurrierende Transaktionen ihn nutzen k\u00f6nnen.<\/p>\n<p>Die Umsetzung einer solchen Sperrung ist in einer nicht verteilten Umgebung einfach. In einem verteilten System gibt es zwei Hauptans\u00e4tze: Entweder implementiert man eine verteilte Sperrung im Cluster oder man verteilt die Transaktionen so, dass Transaktionen, die eine bestimmte Datens\u00e4tze betreffen, immer von demselben Koordinator bearbeitet werden.<\/p>\n<p>Da in unserem Fall die Daten bereits auf Gruppen lokaler Transaktionen in SQL verteilt sind, wurde beschlossen, die Gruppen lokaler Transaktionen Koordinatoren zuzuweisen: Ein Koordinator f\u00fchrt alle Transaktionen mit einem Token von 0 bis 9 aus, der zweite Koordinator die Transaktionen mit einem Token von 10 bis 19 und so weiter. Infolgedessen wird jeder der Koordinatorinstanzen zum Master der Transaktionsgruppe. <\/p>\n<p>In diesem Fall k\u00f6nnen Sperren in Form eines banalen HashMap im Speicher des Koordinators implementiert werden.<\/p>\n<h2>Ausf\u00e4lle von Koordinatoren<\/h2>\n<p>\nDa ein Koordinator ausschlie\u00dflich eine Gruppe von Transaktionen betreut, ist es sehr wichtig, schnell den Ausfall dieses Koordinators zu erkennen, damit der erneute Versuch, die Transaktion auszuf\u00fchren, innerhalb des Timeouts liegt. Um dies schnell und zuverl\u00e4ssig zu gestalten, haben wir ein voll verkn\u00fcpftes quorum-basiertes Heartbeat-Protokoll verwendet:<\/p>\n<p>In jedem Rechenzentrum gibt es mindestens zwei Koordinator-Knoten. Periodisch sendet jeder Koordinator Heartbeat-Nachrichten an die anderen Koordinatoren und informiert diese \u00fcber seine Funktionsweise sowie dar\u00fcber, von welchen Koordinatoren im Cluster er zuletzt Heartbeat-Nachrichten erhalten hat. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5c337e53815cad0a2071b160d98d42fa.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIndem sie \u00e4hnliche Informationen von den anderen in ihren Heartbeat-Nachrichten erhalten, entscheidet jeder Koordinator f\u00fcr sich selbst, welche Knoten im Cluster funktionieren und welche nicht, basierend auf dem Quorum-Prinzip: Wenn Knoten X von der Mehrheit der Knoten im Cluster Informationen \u00fcber den normalen Erhalt von Nachrichten von Knoten Y erh\u00e4lt, funktioniert Y. Umgekehrt, sobald die Mehrheit meldet, dass sie keine Nachrichten von Knoten Y mehr erh\u00e4lt, ist Y ausgefallen. Interessanterweise wird, wenn das Quorum Knoten X dar\u00fcber informiert, dass es keine Nachrichten mehr von ihm erh\u00e4lt, Knoten X sich selbst als ausgefallen betrachten.<\/p>\n<p>Heartbeat-Nachrichten werden mit einer hohen Frequenz gesendet, etwa 20 Mal pro Sekunde, mit einem Intervall von 50 ms. In Java ist es schwierig, die Anwendungsantwort innerhalb von 50 ms zu garantieren, aufgrund der vergleichbaren Dauer von Pausen, die durch den Garbage Collector verursacht werden. Wir haben es geschafft, eine solche Reaktionszeit mit dem G1-Garbage Collector zu erreichen, der es erm\u00f6glicht, ein Ziel f\u00fcr die Dauer der GC-Pausen anzugeben. Allerdings kommen gelegentlich, ziemlich selten, die Pausen des Collectors \u00fcber 50 ms hinaus, was zu einem falschen Ausfallnachweis f\u00fchren kann. Um dies zu vermeiden, meldet der Koordinator den Ausfall eines entfernten Knotens nicht bei Verlust der ersten Heartbeat-Nachricht, sondern nur, wenn mehrere hintereinander verloren gehen. So konnten wir die Ausfallsicherung des Koordinatorknotens in 200 ms erreichen. <\/p>\n<p>Es reicht jedoch nicht aus, schnell herauszufinden, welcher Knoten nicht mehr funktioniert. Wir m\u00fcssen etwas dagegen unternehmen. <\/p>\n<h2>Reservierung<\/h2>\n<p>\nDas klassische Schema sieht vor, im Falle eines Ausfalls des Masters eine Wahl f\u00fcr einen neuen mit Hilfe eines der<noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_Raft\"> modischen<\/a><\/noindex> <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D0%B0%D0%BA%D1%81%D0%BE%D1%81\">universellen<\/a><\/noindex> Algorithmen zu starten. Solche Algorithmen haben jedoch wohlbekannte Probleme mit der Konvergenz \u00fcber die Zeit und der Dauer des gesamten Wahlprozesses. Solche zus\u00e4tzlichen Verz\u00f6gerungen konnten wir durch das Koordinatoren-Substitutionsschema in einem vollverketteten Netzwerk vermeiden:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5e2db258fa2200026390a73a414dfe81.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAngenommen, wir m\u00f6chten eine Transaktion in der Gruppe 50 durchf\u00fchren. Wir definieren im Voraus das Substitutionsschema, also welche Knoten die Transaktionen der Gruppe 50 im Falle eines Ausfalls des prim\u00e4ren Koordinators ausf\u00fchren werden. Unser Ziel ist es, die Betriebsf\u00e4higkeit des Systems im Falle eines Ausfalls des Rechenzentrums zu erhalten. Wir legen fest, dass der erste Ersatzknoten aus einem anderen Rechenzentrum kommen wird und der zweite Ersatz aus dem dritten. Dieses Schema wird einmal ausgew\u00e4hlt und \u00e4ndert sich nicht, bis sich die Topologie des Clusters \u00e4ndert, also bis neue Knoten hinzukommen (was sehr selten passiert). Die Reihenfolge zur Auswahl eines neuen aktiven Masters im Falle eines Ausfalls des alten wird immer so sein: Der erste Ersatz wird der aktive Master und falls auch dieser nicht mehr funktioniert, wird der zweite Ersatz aktiv. <\/p>\n<p>Dieses Schema ist zuverl\u00e4ssiger als der universelle Algorithmus, da zur Aktivierung eines neuen Masters lediglich der Ausfall des alten festgestellt werden muss.<\/p>\n<p>Wie k\u00f6nnen die Kunden jedoch wissen, welcher Meister gerade arbeitet? Es ist unm\u00f6glich, innerhalb von 50 ms Informationen an Tausende von Kunden zu senden. Es kann vorkommen, dass ein Kunde eine Anfrage zur Er\u00f6ffnung einer Transaktion sendet, ohne zu wissen, dass dieser Meister nicht mehr funktioniert, und die Anfrage an einem Timeout h\u00e4ngen bleibt. Um dies zu vermeiden, senden die Kunden spekulativ Anfragen zur Er\u00f6ffnung von Transaktionen gleichzeitig an den Gruppenmeister und beide Reserven, aber nur derjenige, der derzeit aktiver Meister ist, wird auf diese Anfrage antworten. Alle nachfolgenden Kommunikationen im Rahmen der Transaktion werden der Kunde nur mit dem aktiven Meister f\u00fchren.<\/p>\n<p>Reservemeister platzieren empfangene Anfragen f\u00fcr nicht ihre Transaktionen in der Warteschlange der ungeborenen Transaktionen, wo sie eine Zeit lang gespeichert werden. Wenn der aktive Meister stirbt, bearbeitet der neue Meister die Anfragen zur Er\u00f6ffnung von Transaktionen aus seiner Warteschlange und antwortet dem Kunden. Wenn der Kunde bereits eine Transaktion mit dem alten Meister er\u00f6ffnet hat, wird die zweite Antwort ignoriert (und offensichtlich wird eine solche Transaktion nicht abgeschlossen und vom Kunden wiederholt).<\/p>\n<h2>Wie funktioniert eine Transaktion<\/h2>\n<p>\nAngenommen, der Kunde hat dem Koordinator eine Anfrage zur Er\u00f6ffnung einer Transaktion f\u00fcr eine bestimmte Entit\u00e4t mit einem bestimmten Prim\u00e4rschl\u00fcssel gesendet. Der Koordinator sperrt diese Entit\u00e4t und platziert sie in der Sperrtabelle im Speicher. Falls erforderlich, liest der Koordinator diese Entit\u00e4t aus dem Speicher und speichert die erhaltenen Daten im Transaktionszustand im Speicher des Koordinators.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/246b4a5c68a4cc886b87af784c16b9dc.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWenn der Kunde die Daten in der Transaktion \u00e4ndern m\u00f6chte, sendet er dem Koordinator eine Anfrage zur Modifizierung der Entit\u00e4t, und dieser platziert die neuen Daten in der Tabelle des Transaktionszustands im Speicher. An dieser Stelle ist der Eintrag abgeschlossen \u2013 ein Eintrag im Speicher wird nicht vorgenommen.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/4400a91af30eb71134978ff27474f745.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWenn der Kunde im Rahmen einer aktiven Transaktion seine eigenen ge\u00e4nderten Daten anfordert, handelt der Koordinator wie folgt: <\/p>\n<ul>\n<li>Wenn die ID bereits in der Transaktion vorhanden ist, werden die Daten aus dem Speicher genommen; <\/li>\n<li>Wenn die ID nicht im Speicher vorhanden ist, werden die fehlenden Daten aus den Knoten-Speichern ausgelesen, mit bereits im Speicher vorhandenen Daten kombiniert und das Ergebnis dem Kunden \u00fcbergeben. <\/li>\n<\/ul>\n<p>\nSo kann der Kunde seine eigenen \u00c4nderungen lesen, w\u00e4hrend andere Kunden diese \u00c4nderungen nicht sehen, da sie nur im Speicher des Koordinators gehalten werden und in den Cassandra-Knoten noch nicht vorhanden sind.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/c93fd7e8393f431a976f1f56b95e227c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWenn ein Kunde ein Commit sendet, wird der Zustand, der im Speicher des Services vorhanden war, vom Koordinator im logged batch gespeichert und als logged batch an die Cassandra-Speicher gesendet. Die Speicher sorgen daf\u00fcr, dass dieses Paket atomar (vollst\u00e4ndig) angewendet wird, und senden eine Antwort an den Koordinator zur\u00fcck, der dann die Sperren freigibt und dem Kunden den Erfolg der Transaktion best\u00e4tigt.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ab20487ba53ed88e2f560a2249b5d6f9.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nF\u00fcr einen Rollback reicht es dem Koordinator aus, nur den im Speicher belegten Zustand der Transaktion freizugeben.<\/p>\n<p>Durch die oben beschriebenen Anpassungen haben wir die ACID-Prinzipien umgesetzt:<\/p>\n<ul>\n<li><b>Atomarit\u00e4t<\/b>. Das ist die Garantie, dass keine Transaktion teilweise im System festgehalten wird, sondern entweder werden alle ihre Unteroperationen ausgef\u00fchrt oder keine einzige. Dieses Prinzip wird bei uns durch die logged batch in Cassandra gew\u00e4hrleistet.<\/li>\n<li><b>Konsistenz<\/b>. Jede erfolgreiche Transaktion fixiert definitionsgem\u00e4\u00df nur zul\u00e4ssige Ergebnisse. Wenn nach dem \u00d6ffnen der Transaktion und der Ausf\u00fchrung einiger Operationen festgestellt wird, dass das Ergebnis unzul\u00e4ssig ist, wird ein Rollback durchgef\u00fchrt.<\/li>\n<li><b>Isolierung<\/b>. Bei der Ausf\u00fchrung der Transaktion d\u00fcrfen parallele Transaktionen keinen Einfluss auf ihr Ergebnis haben. Konkurrenzierende Transaktionen sind durch pessimistische Sperren beim Koordinator isoliert. F\u00fcr Lesevorg\u00e4nge au\u00dferhalb der Transaktion wird das Isolationsprinzip auf Ebene von Read Committed eingehalten.<\/li>\n<li><b>Best\u00e4ndigkeit<\/b>. Unabh\u00e4ngig von Problemen auf den unteren Ebenen \u2013 wie Stromausf\u00e4llen, Hardwarefehlern \u2013 m\u00fcssen die \u00c4nderungen, die von einer erfolgreich abgeschlossenen Transaktion vorgenommen wurden, nach der Wiederherstellung des Betriebs erhalten bleiben. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Lesen nach Indizes<\/h2>\n<p>\nNehmen wir eine einfache Tabelle: <\/p>\n<pre><code>CREATE TABLE photos (\nid bigint primary key,\nowner bigint,\nmodified timestamp,\n\u2026)<\/code><\/pre>\n<p>\nSie hat eine ID (Prim\u00e4rschl\u00fcssel), einen Besitzer und ein \u00c4nderungsdatum. Es muss eine sehr einfache Abfrage durchgef\u00fchrt werden \u2013 die Daten nach dem Besitzer mit dem \u00c4nderungsdatum \u201ein den letzten 24 Stunden\u201c auszuw\u00e4hlen. <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nDamit eine solche Abfrage schnell ausgef\u00fchrt werden kann, muss in einer klassischen SQL-Datenbank ein Index \u00fcber die Spalten (owner, modified) erstellt werden. Dies k\u00f6nnen wir recht einfach tun, da wir jetzt ACID-Garantien haben!<\/p>\n<h2>Indizes in C*One<\/h2>\n<p>\nEs gibt eine Ausgangstabelle mit Fotos, in der die ID des Datensatzes der Prim\u00e4rschl\u00fcssel ist. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/9f5d7c5f66882666a7ae8219f710beeb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nF\u00fcr C*One wird eine neue Tabelle erstellt, die eine Kopie der urspr\u00fcnglichen ist. Der Schl\u00fcssel entspricht dem Indexausdruck, wobei auch der Prim\u00e4rschl\u00fcssel des Datensatzes aus der urspr\u00fcnglichen Tabelle einbezogen wird:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ce6df1c8407bca7ddc61fd88141455e6.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJetzt kann die Abfrage nach \u201eBesitzer der letzten 24 Stunden\u201c umgeschrieben werden als select aus einer anderen Tabelle:<\/p>\n<pre><code>SELECT * FROM i1_test\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nDie Konsistenz der Daten der urspr\u00fcnglichen Tabelle photos und des Index i1 wird automatisch vom Koordinator aufrechterhalten. Basierend auf dem Datenschema generiert der Koordinator bei \u00c4nderungen und speichert nicht nur die \u00c4nderungen der Haupttabelle, sondern auch die \u00c4nderungen der Kopien. Es werden keine zus\u00e4tzlichen Ma\u00dfnahmen mit der Indextabelle durchgef\u00fchrt, Protokolle werden nicht gelesen, und es werden keine Sperren verwendet. Das Hinzuf\u00fcgen von Indizes verbraucht also nahezu keine Ressourcen und beeintr\u00e4chtigt die Geschwindigkeit der Modifizierung nicht.<\/p>\n<p>Mit Hilfe von ACID ist es uns gelungen, Indizes \u201ewie in SQL\u201c zu implementieren. Sie besitzen Konsistenz, sind skalierbar, arbeiten schnell, k\u00f6nnen zusammengesetzt sein und sind in die CQL-Abfragesprache integriert. F\u00fcr die Unterst\u00fctzung von Indizes m\u00fcssen keine \u00c4nderungen am Anwendungscode vorgenommen werden. Alles ist so einfach wie in SQL. Und das Wichtigste ist, dass die Indizes die Geschwindigkeit der Ausf\u00fchrung von Modifikationen der Ursprungsdatenbank nicht beeintr\u00e4chtigen.<\/p>\n<h2>Was ist herausgekommen?<\/h2>\n<p>\nWir haben C*One vor drei Jahren entwickelt und in den produktiven Betrieb genommen. <\/p>\n<p>Was haben wir also erreicht? Lassen Sie es uns am Beispiel des Systems zur Verarbeitung und Speicherung von Fotos bewerten, einer der wichtigsten Datentypen im sozialen Netzwerk. Es geht nicht um die Fotos selbst, sondern um die verschiedenen Metainformationen. Derzeit gibt es in \u201eOdnoklassniki\u201c etwa 20 Milliarden solcher Datens\u00e4tze, das System verarbeitet 80.000 Leseanfragen pro Sekunde, bis zu 8.000 ACID-Transaktionen pro Sekunde, die mit der Modifikation von Daten verbunden sind. <\/p>\n<p>Als wir SQL mit einem Replikationsfaktor von 1 (aber in RAID 10) verwendeten, wurden die Metainformationen der Fotos in einem hochverf\u00fcgbaren Cluster von 32 Maschinen mit Microsoft SQL Server (plus 11 Backup-Maschinen) gespeichert. Au\u00dferdem wurden 10 Server f\u00fcr die Speicherung von Backups bereitgestellt. Zusammen also 50 teure Maschinen. Dabei lief das System unter nominaler Last, ohne Puffer.<\/p>\n<p>Nach der Migration auf das neue System haben wir einen Replikationsfaktor von 3 erhalten \u2013 je eine Kopie in jedem Rechenzentrum. Das System besteht aus 63 Cassandra-Storage-Nodes und 6 Koordinator-Maschinen, insgesamt 69 Servern. Diese Maschinen sind jedoch erheblich g\u00fcnstiger, ihre Gesamtkosten betragen etwa 30 % der Kosten des SQL-Systems. Die Auslastung bleibt dabei auf einem Niveau von 30 %.<\/p>\n<p>Mit der Einf\u00fchrung von C*One sanken auch die Latenzen: Bei SQL betrug die Schreiboperation etwa 4,5 ms. Bei C*One sind es etwa 1,6 ms. Die Dauer von Transaktionen liegt im Durchschnitt unter 40 ms, ein Commit dauert 2 ms, und die Dauer f\u00fcr Lese- und Schreibvorg\u00e4nge betr\u00e4gt im Durchschnitt 2 ms. Der 99. Perzentil liegt bei lediglich 3-3,1 ms, die Anzahl der Zeit\u00fcberschreitungen hat sich um das 100-fache reduziert \u2013 alles dank der umfassenden Nutzung von Spekulationen. <\/p>\n<p>Bis zu diesem Zeitpunkt wurden die meisten SQL Server-Nodes stillgelegt, neue Produkte werden ausschlie\u00dflich mit C*One entwickelt. Wir haben C*One f\u00fcr die Arbeit in unserer Cloud angepasst. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, was die Bereitstellung neuer Cluster beschleunigte, die Konfiguration vereinfachte und den Betrieb automatisierte. Ohne den Quellcode w\u00e4re dies erheblich komplizierter und fehleranf\u00e4lliger gewesen. <\/p>\n<p>Derzeit arbeiten wir an der Migration unserer anderen Speichersysteme in die Cloud \u2014 aber das ist eine ganz andere Geschichte.<br \/>\n<br \/>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/odnoklassniki\/blog\/417593\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28484,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-37956","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","description":"Bis vor kurzem wurden in Odnoklassniki etwa 50 TB Daten verarbeitet.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/newsql-nosql-acid","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","og:description":"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/newsql-nosql-acid","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:20:47+00:00","article:modified_time":"2019-10-31T19:20:47+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"37956","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 19:55:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:17:23","updated":"2026-01-23 19:55:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/37956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=37956"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/37956\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/28484"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=37956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=37956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=37956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}