{"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 Daten in Echtzeit in SQL Server verarbeitet. Bei diesem Datenvolumen ist es nahezu unm\u00f6glich, einen schnellen und zuverl\u00e4ssigen, zudem ausfallsicheren Rechenzentrumszugang mit SQL-Datenbanken zu gew\u00e4hrleisten. \u00dcblicherweise werden in solchen F\u00e4llen eine der NoSQL-Datenbanken eingesetzt, doch nicht alles l\u00e4sst sich nach NoSQL \u00fcbertragen: Einige Entit\u00e4ten ben\u00f6tigen ACID-Transaktionsgarantien. <\/p>\n<p>Das hat uns zur Nutzung einer NewSQL-Datenbank gef\u00fchrt, d.h. einer Datenbank, die die Ausfallsicherheit, Skalierbarkeit und Geschwindigkeit von NoSQL-Systemen bietet, jedoch die gewohnten ACID-Garantien klassischer Systeme beibeh\u00e4lt. Es gibt nur wenige funktionierende Industriesysteme dieser neuen Klasse, weshalb wir ein solches System selbst entwickelt und in den produktiven Betrieb genommen haben. <\/p>\n<p>Wie es funktioniert und was dabei herausgekommen ist \u2013 lesen Sie 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 Top f\u00fcnf<\/a><\/noindex> eine der gr\u00f6\u00dften sozialen Netzwerke der Welt und geh\u00f6rt zu den zehn Websites, auf denen die Nutzer die meiste Zeit verbringen. Die Infrastruktur von \"OK\" bew\u00e4ltigt extrem hohe Lasten: mehr als eine Million HTTP-Anfragen pro Sekunde f\u00fcr die Frontends. Teile des Serverparks mit \u00fcber 8000 Servern sind nahe beieinander in vier Rechenzentren in Moskau untergebracht, was eine Netzwerklatenz von unter 1 ms zwischen ihnen erm\u00f6glicht.<\/p>\n<p>Wir verwenden Cassandra seit 2010, beginnend mit Version 0.6. Heute sind mehrere Dutzend Cluster im Einsatz. Der schnellste Cluster verarbeitet mehr als 4 Millionen Operationen pro Sekunde, w\u00e4hrend der gr\u00f6\u00dfte 260 TB speichert. <\/p>\n<p>Allerdings sind das alles gew\u00f6hnliche NoSQL-Cluster, die f\u00fcr die Speicherung verwendet werden. <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\">von schwach konsistenten<\/a><\/noindex> Daten. Wir wollten jedoch das prim\u00e4re konsistente Speichersystem, Microsoft SQL Server, ersetzen, das seit der Gr\u00fcndung von \"Odnoklassniki\" verwendet wurde. Das Speichersystem bestand aus mehr als 300 SQL Server Standard Edition Maschinen, auf denen 50 TB an Gesch\u00e4ftseinheiten gespeichert waren. Diese Daten werden im Rahmen von ACID-Transaktionen modifiziert und erfordern <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">eine hohe Konsistenz.<\/a><\/noindex>.<\/p>\n<p>F\u00fcr die Verteilung von Daten \u00fcber SQL Server-Knoten haben wir sowohl vertikale als auch horizontale <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">Partitionierung<\/a><\/noindex> (Sharding) verwendet. Historisch haben wir ein einfaches Sharding-Schema genutzt: Jeder Entit\u00e4t wurde ein Token zugewiesen \u2013 eine Funktion, die vom Entit\u00e4ts-ID abgeleitet ist. Entit\u00e4ten mit demselben Token wurden auf einen SQL-Server verschoben. Das Verh\u00e4ltnis zwischen Master- und Detaildatensatz wurde so umgesetzt, dass die Tokens des Haupt- und des erzeugten Datensatzes immer \u00fcbereinstimmten und sich auf demselben Server befanden. In sozialen Netzwerken werden fast alle Datens\u00e4tze im Namen des Benutzers erzeugt \u2013 das hei\u00dft, alle Benutzerdaten innerhalb eines funktionalen Subsystems werden auf einem Server gespeichert. Das bedeutet, dass bei Gesch\u00e4ftstransaktionen fast immer Tabellen eines SQL-Servers beteiligt waren, was die Konsistenz der Daten durch lokale ACID-Transaktionen gew\u00e4hrleistete, ohne die Notwendigkeit <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">langsamer und unzuverl\u00e4ssiger<\/a><\/noindex> verteilte ACID-Transaktionen zu verwenden.<\/p>\n<p>Dank Sharding und zur Beschleunigung der SQL-Leistung:<\/p>\n<ul>\n<li>Verwenden wir keine Foreign Key Constraints, da sich beim Sharding die ID der Entit\u00e4t auf einem anderen Server befinden kann.<\/li>\n<li>Wir verwenden keine gespeicherten Prozeduren und Trigger, da sie die CPU der Datenbank belasten.<\/li>\n<li>JOINs vermeiden wir aus denselben Gr\u00fcnden sowie aufgrund der vielen zuf\u00e4lligen Lesevorg\u00e4nge von der Festplatte.<\/li>\n<li>Um Deadlocks zu reduzieren, nutzen wir au\u00dferhalb von Transaktionen die Isolationsebene Read Uncommitted.<\/li>\n<li>Wir f\u00fchren nur kurze Transaktionen durch (im Durchschnitt unter 100 ms).<\/li>\n<li>Multireihen-UPDATEs und DELETEs vermeiden wir aufgrund der hohen Anzahl von Deadlocks \u2013 wir aktualisieren immer nur einen Datensatz.<\/li>\n<li>Abfragen f\u00fchren wir stets nur \u00fcber Indizes aus \u2013 eine Abfrage mit einem vollst\u00e4ndigen Tabellenansichtsplan bedeutet f\u00fcr uns eine \u00dcberlastung der Datenbank und deren Ausfall.<\/li>\n<\/ul>\n<p>\nDiese Ma\u00dfnahmen haben es uns erm\u00f6glicht, nahezu die maximale Leistung aus den SQL-Servern herauszuholen. Allerdings wurden die Probleme immer zahlreicher. Lassen Sie uns diese betrachten.<\/p>\n<h2>Probleme mit SQL<\/h2>\n<p><\/p>\n<ul>\n<li>Da wir eine eigene Sharding-L\u00f6sung verwendet haben, wurden zus\u00e4tzliche Shards manuell von den Administratoren hinzugef\u00fcgt. W\u00e4hrend dieser Zeit haben die skalierbaren Datenreplikate keine Anfragen bedient. <\/li>\n<li>Wenn die Anzahl der Eintr\u00e4ge in einer Tabelle w\u00e4chst, sinkt die Geschwindigkeit beim Einf\u00fcgen und Modifizieren. Bei der Hinzuf\u00fcgung von Indizes zu einer bestehenden Tabelle sinkt die Geschwindigkeit exponentiell, und die Erstellung sowie das Neuanlegen von Indizes erfolgt mit Ausfallzeiten.<\/li>\n<li>Die Pr\u00e4senz einer kleinen Anzahl von Windows-Instanzen f\u00fcr SQL Server in der Produktion erschwert das Management der Infrastruktur.<\/li>\n<\/ul>\n<p>\nDoch das Hauptproblem ist \u2014 <\/p>\n<h2>Ausfallsicherheit<\/h2>\n<p>\nEin klassischer SQL-Server hat eine mangelhafte Ausfallsicherheit. Angenommen, Sie haben nur einen Datenbankserver, der alle drei Jahre ausf\u00e4llt. W\u00e4hrend dieser Zeit ist die Website f\u00fcr 20 Minuten nicht verf\u00fcgbar, was akzeptabel ist. Bei 64 Servern ist die Website bereits alle drei Wochen nicht verf\u00fcgbar. Und bei 200 Servern ist die Website jede Woche nicht verf\u00fcgbar. Das ist ein Problem. <\/p>\n<p>Was kann getan werden, um die Ausfallsicherheit des SQL-Servers zu erh\u00f6hen? Wikipedia schl\u00e4gt vor, einen <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">hochverf\u00fcgbaren Cluster<\/a><\/noindex>einzurichten, bei dem im Falle eines Ausfalls einer der Komponenten eine redundante vorhanden ist.<\/p>\n<p>Das erfordert eine teure Hardware-Infrastruktur: umfangreiche Redundanzen, Glasfaser, gemeinsamen Speicher und auch das Einschalten der Reserve funktioniert unzuverl\u00e4ssig: etwa 10 % der Aktivierungen enden mit einem Ausfall der Reserveknoten hinter dem Hauptknoten. <\/p>\n<p>Der Hauptnachteil eines solchen hochverf\u00fcgbaren Clusters ist jedoch die vollst\u00e4ndige Unzug\u00e4nglichkeit, wenn das Rechenzentrum, in dem es betrieben wird, ausf\u00e4llt. \u201eOdnoClassniki\u201c betreibt vier Rechenzentren, und wir m\u00fcssen sicherstellen, dass der Betrieb bei einem vollst\u00e4ndigen Ausfall eines dieser Zentren aufrechterhalten wird.<\/p>\n<p>Zu diesem Zweck k\u00f6nnte man <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">Multi-Master<\/a><\/noindex> Replikation verwenden, die in SQL Server integriert ist. Diese L\u00f6sung ist aufgrund der Softwarekosten deutlich teurer und leidet unter den bekannten Problemen der Replikation \u2013 unvorhersehbaren Verz\u00f6gerungen bei Transaktionen bei synchroner Replikation und Verz\u00f6gerungen bei der Anwendung von Replikationen (und infolgedessen verloren gegangenen \u00c4nderungen) bei asynchroner Replikation. Die implizierte <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>Alle diese Probleme erforderten eine radikale L\u00f6sung, und wir begannen mit deren detaillierter Analyse. Hier m\u00fcssen wir uns mit dem vertraut machen, was SQL Server im Wesentlichen macht \u2013 Transaktionen.<\/p>\n<h2>Eine einfache Transaktion<\/h2>\n<p>\nBetrachten wir eine einfache Transaktion aus der Perspektive eines praktischen SQL-Programmierers: das Hinzuf\u00fcgen eines Fotos zu einem Album. Alben und Fotos werden in verschiedenen Tabellen gespeichert. Jedes Album hat einen Z\u00e4hler f\u00fcr \u00f6ffentliche Fotos. Somit wird diese Transaktion in die folgenden Schritte unterteilt: <\/p>\n<ol>\n<li>Sperren Sie das Album mit dem Schl\u00fcssel.<\/li>\n<li>Erstellen Sie einen Eintrag in der Fototabelle. <\/li>\n<li>Wenn das Foto den \u00f6ffentlichen Status hat, erh\u00f6hen wir den Z\u00e4hler f\u00fcr \u00f6ffentliche Fotos im Album, aktualisieren den Eintrag und beenden 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);\nPhoto photo = photos.create(\u2026);\n\nif (photo.status == PUBLIC) {\n    album.incPublicPhotosCount();\n}\nalbum.update();\n\nTX.commit();<\/code><\/pre>\n<p>\nWir sehen, dass das h\u00e4ufigste Szenario einer Gesch\u00e4ftstransaktion darin besteht, Daten aus der DB in den Arbeitsspeicher des Anwendungsservers zu lesen, etwas zu \u00e4ndern und die neuen Werte zur\u00fcck in die DB zu speichern. Normalerweise aktualisieren wir in einer solchen Transaktion mehrere Entit\u00e4ten, mehrere Tabellen. <\/p>\n<p>Bei der Durchf\u00fchrung einer Transaktion kann es zu einer gleichzeitigen Modifikation derselben Daten aus einem anderen System kommen. Beispielsweise k\u00f6nnte ein Antispam-System entscheiden, dass ein Nutzer verd\u00e4chtig ist, und damit alle Fotos des Nutzers nicht mehr \u00f6ffentlich sein sollten, was bedeutet, dass der photo.status auf einen anderen Wert ge\u00e4ndert und die entsprechenden Z\u00e4hler zur\u00fcckgesetzt werden m\u00fcssen. Offensichtlich wird das Ergebnis nicht den Anforderungen entsprechen, wenn diese Operation ohne Garantien f\u00fcr die atomare Anwendung und Isolation konkurrierender Modifikationen erfolgt, wie in <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, denn das Ergebnis k\u00f6nnte entweder einen falschen Z\u00e4hlerstand f\u00fcr die Fotos anzeigen oder es werden nicht alle Fotos zur Moderation geschickt. <\/p>\n<p>Solchen Code, der mit verschiedenen Gesch\u00e4ftseinheiten innerhalb einer Transaktion manipuliert, wurde w\u00e4hrend der gesamten Existenz von Odnoklassniki in gro\u00dfer Zahl geschrieben. Aus Erfahrung mit Migrationen zu NoSQL gibt es <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Eventual Consistency<\/a><\/noindex> Wir wissen, dass die gr\u00f6\u00dften Herausforderungen (und der damit verbundene Zeitaufwand) darin bestehen, Code zu entwickeln, der die Konsistenz der Daten sicherstellt. Daher war das Hauptaugenmerk bei der Auswahl eines neuen Speichersystems die Bereitstellung echter ACID-Transaktionen f\u00fcr die Anwendungsebene. <\/p>\n<p>Weitere ebenso wichtige Anforderungen waren:<\/p>\n<ul>\n<li>Im Falle eines Ausfalls des Rechenzentrums m\u00fcssen sowohl Lese- als auch Schreibzugriffe auf das neue Speichersystem m\u00f6glich sein.<\/li>\n<li>Die aktuelle Entwicklungsgeschwindigkeit muss beibehalten werden. Das bedeutet, dass bei der Arbeit mit dem neuen Speichersystem die Menge des Codes etwa gleich bleiben sollte und es keine Notwendigkeit entstehen darf, zus\u00e4tzliche Funktionen im Speicher zu implementieren, Algorithmen zur Konfliktl\u00f6sung zu entwickeln, sekund\u00e4re Indizes aufrechtzuerhalten usw. <\/li>\n<li>Die Leistung des neuen Speichersystems muss sowohl bei Lesevorg\u00e4ngen als auch bei der Verarbeitung von Transaktionen ausreichend hoch sein, was effektiv die Unanwendbarkeit akademisch strenger, universaler, aber langsamer L\u00f6sungen impliziert, wie z. B. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">zwei-phasige Commit-Prozesse.<\/a><\/noindex>.<\/li>\n<li>Automatisches On-the-Fly-Scaling. <\/li>\n<li>Der Einsatz normaler kosteng\u00fcnstiger Server, ohne dass exotische Hardware angeschafft werden muss. <\/li>\n<li>Die M\u00f6glichkeit, die Speicherl\u00f6sungen durch die Entwickler des Unternehmens weiterzuentwickeln. Mit anderen Worten, der Fokus lag auf eigenen 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 kamen wir zu zwei Architekturentscheidungen:<\/p>\n<p>Die erste ist, einen beliebigen SQL-Server zu verwenden und die erforderliche Fehlertoleranz, Skalierungsmechanismen, ein fehlertolerantes Cluster, Konfliktl\u00f6sungen sowie verteilte, zuverl\u00e4ssige und schnelle ACID-Transaktionen umzusetzen. Wir haben diese Option als recht komplex und arbeitsintensiv eingesch\u00e4tzt.<\/p>\n<p>Die zweite M\u00f6glichkeit besteht darin, ein fertiges NoSQL-Speicher-System zu w\u00e4hlen, das \u00fcber implementierte Skalierung, ein fehlertolerantes Cluster und Konfliktl\u00f6sungen verf\u00fcgt, w\u00e4hrend wir Transaktionen und SQL selbst umsetzen. Auf den ersten Blick scheint die Umsetzung von SQL, ganz zu schweigen von ACID-Transaktionen, eine jahrelange Aufgabe zu sein. Doch dann erkannten wir, dass der Satz von SQL-Funktionen, den wir in der Praxis nutzen, so weit vom ANSI SQL entfernt ist wie <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> weit vom ANSI SQL entfernt ist. Bei n\u00e4herer Betrachtung von CQL wurde uns klar, dass es ziemlich nah an dem ist, was wir ben\u00f6tigen.<\/p>\n<h2>Cassandra und CQL<\/h2>\n<p>\nWas macht Cassandra so interessant und welche M\u00f6glichkeiten bietet sie?<\/p>\n<p>Zun\u00e4chst k\u00f6nnen hier Tabellen mit Unterst\u00fctzung f\u00fcr verschiedene Datentypen erstellt werden, und es sind SELECT- oder UPDATE-Abfragen \u00fcber den Prim\u00e4rschl\u00fcssel m\u00f6glich.<\/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 Replikate zu gew\u00e4hrleisten, verwendet Cassandra <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">einen Quorum-Ansatz<\/a><\/noindex>. Im einfachsten Fall bedeutet dies, dass bei der Bereitstellung von drei Replikaten derselben Zeile auf verschiedenen Knoten des Clusters der Datensatz als erfolgreich betrachtet wird, wenn die Mehrheit der Knoten (d.h. zwei von drei) den Erfolg dieser Schreiboperation best\u00e4tigt hat. Die Daten der Zeile gelten als konsistent, wenn bei der Abfrage die Mehrheit der Knoten befragt wurde und dies best\u00e4tigt haben. So wird bei drei Replikaten vollst\u00e4ndige und sofortige Datenkonsistenz im Falle eines Ausfalls eines Knotens garantiert. Dieser Ansatz erm\u00f6glicht es uns, ein noch zuverl\u00e4ssigeres Schema umzusetzen: 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\u00e4tet antwortende Knoten k\u00f6nnte ernsthafte Probleme haben \u2014 Verz\u00f6gerungen, Garbage Collection in der JVM, Direct Memory Reclaim im Linux-Kernel, Hardwarefehler, Netzwerkunterbrechungen. Allerdings hat dies keine Auswirkungen auf die Client-Operationen und die Daten.<\/p>\n<p>Der Ansatz, bei dem wir drei Knoten ansprechen und Antworten von zwei erhalten, wird genannt <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">Spekulation<\/a><\/noindex>: Anfragen an \u00fcberfl\u00fcssige Replikate werden gesendet, bevor der Knoten \u00abausf\u00e4llt\u00bb. <\/p>\n<p>Ein weiterer Vorteil von Cassandra ist der Batchlog \u2013 ein Mechanismus, der entweder die vollst\u00e4ndige Anwendung oder vollst\u00e4ndige Ablehnung eines von Ihnen vorgenommenen \u00c4nderungssets garantiert. Dies erm\u00f6glicht es uns, A in ACID \u2013 die Atomarit\u00e4t \u2013 ab Werk zu gew\u00e4hrleisten.<\/p>\n<p>Das N\u00e4chste, was Transaktionen in Cassandra \u00e4hnelt, sind die sogenannten &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">lightweight transactions<\/a><\/noindex>&#171;. Doch von \u201eechten\u201c ACID-Transaktionen sind sie weit entfernt: tats\u00e4chlich handelt es sich um die M\u00f6glichkeit, <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> auf Daten von nur einem Datensatz zuzugreifen, unter Verwendung eines Konsens \u00fcber das schwergewichtige Protokoll Paxos. Daher ist die Geschwindigkeit solcher Transaktionen gering. <\/p>\n<h2>Was uns in Cassandra gefehlt hat<\/h2>\n<p>\nAlso standen wir vor der Herausforderung, echte ACID-Transaktionen in Cassandra zu implementieren. Mit deren Hilfe k\u00f6nnten wir zwei weitere praktische Funktionen klassischer DBMS einfach umsetzen: konsistente schnelle Indizes, die es uns erm\u00f6glichen w\u00fcrden, Daten nicht nur nach dem Prim\u00e4rschl\u00fcssel abzurufen, sowie einen ordentlichen Generator f\u00fcr monoton steigende Autoinkrement-IDs.<\/p>\n<h4>C*One<\/h4>\n<p>\nSo entstand ein neues DBMS <b>C*One<\/b>, das aus drei Typen von Serverknoten besteht:<\/p>\n<ul>\n<li>Speicher \u2014 (fast) standardm\u00e4\u00dfige Cassandra-Server, die f\u00fcr die Speicherung von Daten auf lokalen Festplatten verantwortlich sind. Mit zunehmender Last und Datenmenge l\u00e4sst sich ihre Anzahl problemlos auf Dutzende oder Hunderte skalieren.<\/li>\n<li>Transaktionskoordinatoren \u2014 sorgen f\u00fcr die Ausf\u00fchrung von Transaktionen. <\/li>\n<li>Clients \u2014 Anwendungsserver, die Gesch\u00e4ftsoperationen durchf\u00fchren und Transaktionen initiieren. Solcher Clients kann es Tausende geben.<\/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 zusammengeschlossen, nutzen das interne Nachrichtenprotokoll von Cassandra zur Kommunikation und <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">entfernt wurde.<\/a><\/noindex> f\u00fcr den Austausch von Clusterinformationen. Mit Hilfe von Heartbeat erfahren die Server von gegenseitigen Ausf\u00e4llen, halten ein gemeinsames Datenschema \u2014 Tabellen, deren Struktur und Replikation; das Partitionierungsschema, die Cluster-Topologie usw.<\/p>\n<h4>Windows XP, Vista, 7, 8, 10, Server 2003, Server 2008, Server 2012<\/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 \/>\nAnstelle von Standardtreibern wird der Modus Fat Client verwendet. Eine solche Node speichert keine Daten, kann jedoch als Koordinator f\u00fcr die Ausf\u00fchrung von Anfragen fungieren, d. h. der Client \u00fcbernimmt selbst 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 eine Kommunikation mit einem entfernten Koordinator erfordert, sondern erm\u00f6glicht auch eine bessere Steuerung der Anfrage\u00fcbertragung. Au\u00dferhalb einer offenen Transaktion des Clients werden die Anfragen an die Speicher gesendet. Wenn der Client jedoch eine Transaktion er\u00f6ffnet hat, werden alle Anfragen innerhalb der 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 neu implementiert haben. Er ist verantwortlich f\u00fcr die Verwaltung von Transaktionen, Sperren und Reihenfolge der Anwendungstransaktionen.<\/p>\n<p>F\u00fcr jede bearbeitete Transaktion erzeugt der Koordinator einen Zeitstempel: jeder nachfolgende ist gr\u00f6\u00dfer als der vorhergehende. Da in Cassandra das Konfliktl\u00f6sungsverfahren auf Zeitstempeln basiert (von zwei konkurrierenden Eintr\u00e4gen wird der mit dem sp\u00e4teren Zeitstempel als aktuell betrachtet), wird ein Konflikt immer zugunsten der nachfolgenden Transaktion gel\u00f6st. So haben wir realisiert <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> \u2014 eine kosteng\u00fcnstige L\u00f6sung zur Konfliktbew\u00e4ltigung in verteilten Systemen.<\/p>\n<h2>Sperren<\/h2>\n<p>\nUm die Isolation zu gew\u00e4hrleisten, haben wir uns f\u00fcr die einfachste Methode entschieden \u2014 pessimistische Sperren basierend auf dem Prim\u00e4rschl\u00fcssel des Datensatzes. Das bedeutet, dass in der Transaktion der Datensatz zuerst gesperrt werden muss, bevor er gelesen, modifiziert und gespeichert werden kann. Nur nach einem erfolgreichen Commit kann der Datensatz entsperrt werden, sodass konkurrierende Transaktionen darauf zugreifen k\u00f6nnen.<\/p>\n<p>Die Umsetzung einer solchen Sperre ist in einer nicht verteilten Umgebung einfach. In einem verteilten System gibt es zwei Hauptans\u00e4tze: entweder die Umsetzung einer verteilten Sperre im Cluster oder die Verteilung der Transaktionen so, dass Transaktionen, die einen bestimmten Datensatz 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, den Koordinatoren der Gruppen lokaler Transaktionen bestimmte Bereiche zuzuweisen: Ein Koordinator bearbeitet alle Transaktionen mit einem Token von 0 bis 9, der zweite von 10 bis 19 und so weiter. Dadurch wird jeder der Koordinatoren zum Master der Transaktionsgruppe. <\/p>\n<p>Damit k\u00f6nnen Sperren in Form einer einfachen HashMap im Speicher des Koordinators implementiert werden.<\/p>\n<h2>Ausf\u00e4lle der Koordinatoren<\/h2>\n<p>\nDa ein Koordinator ausschlie\u00dflich eine Gruppe von Transaktionen bedient, ist es sehr wichtig, schnell zu erkennen, wenn er ausf\u00e4llt, damit der erneute Versuch der Transaktionsausf\u00fchrung innerhalb des Timeouts liegt. Um dies schnell und zuverl\u00e4ssig zu erreichen, haben wir ein vollverkn\u00fcpftes Quorum-Hearbeat-Protokoll angewendet:<\/p>\n<p>In jedem Rechenzentrum werden mindestens zwei Koordinator-Knoten untergebracht. Regelm\u00e4\u00dfig sendet jeder Koordinator ein Heartbeat-Signal an die anderen Koordinatoren und informiert sie \u00fcber seinen Status sowie dar\u00fcber, von welchen Koordinatoren im Cluster er zuletzt Heartbeat-Signale empfangen 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 er \u00e4hnliche Informationen aus den Heartbeat-Signalen der anderen erh\u00e4lt, entscheidet jeder Koordinator f\u00fcr sich, welche Cluster-Knoten funktionieren und welche nicht, wobei er dem Quorum-Prinzip folgt: Wenn Knoten X von der Mehrheit der Knoten im Cluster die Best\u00e4tigung f\u00fcr den ordnungsgem\u00e4\u00dfen Empfang von Nachrichten von Knoten Y erh\u00e4lt, bedeutet dies, dass Y funktioniert. Umgekehrt gilt, sobald die Mehrheit meldet, dass keine Nachrichten mehr von Knoten Y empfangen wurden, gilt Y als ausgefallen. Interessanterweise wird Knoten X, wenn das Quorum meldet, dass es keine Nachrichten mehr von ihm erh\u00e4lt, selbst f\u00fcr ausgefallen halten.<\/p>\n<p>Heartbeat-Nachrichten werden in hoher Frequenz gesendet, etwa 20 Mal pro Sekunde, mit einem Intervall von 50 ms. In Java ist es schwierig, eine Anwendungsantwort innerhalb von 50 ms zu garantieren, da die Pausendauer, die durch die Garbage Collection verursacht wird, vergleichbar ist. Es ist uns gelungen, eine derartige Reaktionszeit mit dem G1-Garbage-Collector zu erreichen, der es erm\u00f6glicht, ein Ziel f\u00fcr die Dauer der GC-Pausen anzugeben. Gelegentlich, recht selten, k\u00f6nnen jedoch die Pausen des Collectors 50 ms \u00fcberschreiten, was zu einer Fehlinterpretation eines Ausfalls f\u00fchren kann. Um dies zu vermeiden, meldet der Koordinator den Ausfall eines Remote-Knotens nicht, sobald das erste Heartbeat-Nachricht von ihm fehlt, sondern erst wenn mehrere aufeinanderfolgende ausbleiben. So haben wir es geschafft, den Ausfall eines Koordinator-Knotens innerhalb von 200 ms zu erkennen. <\/p>\n<p>Es reicht jedoch nicht aus, schnell zu erkennen, welcher Knoten ausgefallen ist. Da muss etwas unternommen werden. <\/p>\n<h2>Redundanz<\/h2>\n<p>\nDas klassische Schema sieht im Falle eines Master-Ausfalls die Wahl eines neuen Masters durch einen 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\"> modernen<\/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. Allerdings gibt es bei solchen Algorithmen gut bekannte Probleme mit der Konvergenzzeit und der Dauer des Wahlprozesses selbst. Solche zus\u00e4tzlichen Verz\u00f6gerungen konnten wir durch das Koordinator-Ersatzschema in einem vollst\u00e4ndig vernetzten 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. Vorab definieren wir das Ersatzschema, also welche Knoten die Transaktionen der Gruppe 50 im Falle eines Ausfalls des Hauptkoordinators ausf\u00fchren werden. Unser Ziel ist es, die Funktionsf\u00e4higkeit des Systems im Falle eines Ausfalls des Rechenzentrums aufrechtzuerhalten. Wir legen fest, dass der erste Ersatz aus einem anderen Rechenzentrum und der zweite Ersatz aus einem dritten Rechenzentrum besteht. Dieses Schema wird einmal festgelegt und bleibt unver\u00e4ndert, solange sich die Topologie des Clusters nicht \u00e4ndert, also bis neue Knoten hinzukommen (was sehr selten passiert). Die Reihenfolge der Auswahl eines neuen aktiven Masters im Falle eines Ausfalls des alten wird immer so aussehen: der erste Ersatz wird zum aktiven Master, und wenn dieser nicht mehr funktioniert, wird der zweite Ersatz aktiv. <\/p>\n<p>Ein solches Schema ist zuverl\u00e4ssiger als ein universeller Algorithmus, da zur Aktivierung eines neuen Masters lediglich der Ausfall des alten festgestellt werden muss.<\/p>\n<p>Wie k\u00f6nnen die Kunden wissen, welcher der Master gerade arbeitet? In 50 ms ist es unm\u00f6glich, 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 Master bereits nicht mehr funktioniert, und die Anfrage somit im Timeout stecken bleibt. Um dies zu vermeiden, senden die Kunden spekulativ eine Anfrage zur Er\u00f6ffnung einer Transaktion gleichzeitig an den Gruppenmaster und beide seine Reserven, aber nur der Master, der gerade aktiv ist, wird auf diese Anfrage antworten. Alle weiteren Kommunikationen im Rahmen der Transaktion erfolgen nur mit dem aktiven Master.<\/p>\n<p>Die Reserve-Master platzieren erhaltene Anfragen f\u00fcr nicht eigene Transaktionen in eine Warteschlange der nicht erf\u00fcllten Transaktionen, wo sie eine gewisse Zeit gespeichert werden. Wenn der aktive Master ausf\u00e4llt, bearbeitet ein neuer Master die Anfragen zur Er\u00f6ffnung von Transaktionen aus seiner Warteschlange und antwortet dem Kunden. Wenn der Kunde bereits eine Transaktion mit dem alten Master er\u00f6ffnet hat, wird die zweite Antwort ignoriert (und diese Transaktion wird offensichtlich nicht abgeschlossen und muss vom Kunden erneut durchgef\u00fchrt werden).<\/p>\n<h2>Wie funktioniert eine Transaktion<\/h2>\n<p>\nAngenommen, ein Kunde sendet dem Koordinator eine Anfrage zur Er\u00f6ffnung einer Transaktion f\u00fcr eine bestimmte Entit\u00e4t mit einem bestimmten Prim\u00e4rschl\u00fcssel. Der Koordinator sperrt diese Entit\u00e4t und platziert sie in einer Speichertabelle f\u00fcr Sperren. Wenn n\u00f6tig, 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 Modifikation der Entit\u00e4t, und dieser platziert die neuen Daten in der Tabelle f\u00fcr Transaktionszust\u00e4nde im Speicher. Damit ist die Speicherung abgeschlossen \u2013 es erfolgt keine Aufnahme in den Speicher.<\/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 w\u00e4hrend einer aktiven Transaktion seine eigenen ge\u00e4nderten Daten anfragt, 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 entnommen; <\/li>\n<li>Falls die ID im Speicher nicht vorhanden ist, werden die fehlenden Daten aus den Node-Speichern gelesen, mit den bereits im Speicher vorhandenen kombiniert und das Ergebnis dem Kunden \u00fcbergeben. <\/li>\n<\/ul>\n<p>\nAuf diese Weise 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-Nodes noch nicht existieren.<\/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 einen Commit sendet, wird der Zustand, der im Dienst gespeichert war, vom Koordinator in einem logged batch gesichert und als logged batch an die Cassandra-Speicher gesendet. Die Speicher f\u00fchren alles Notwendige aus, damit dieses Paket atomar (vollst\u00e4ndig) angewendet wird und geben dem Koordinator eine R\u00fcckmeldung, der daraufhin die Sperren aufhebt und den Erfolg der Transaktion dem Kunden 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 die R\u00fccksetzung muss der Koordinator lediglich den Speicher freigeben, der f\u00fcr den Zustand der Transaktion verwendet wurde.<\/p>\n<p>Durch die oben beschriebenen Anpassungen haben wir die ACID-Prinzipien implementiert:<\/p>\n<ul>\n<li><b>Atomarit\u00e4t<\/b>. Dies ist eine Garantie daf\u00fcr, dass keine Transaktion teilweise im System erfasst wird; es werden entweder alle ihre Unteroperationen ausgef\u00fchrt oder keine. In unserem Fall wird dieses Prinzip durch den logged batch in Cassandra gew\u00e4hrleistet.<\/li>\n<li><b>Konsistenz<\/b>. Jede erfolgreiche Transaktion erfasst definitionsgem\u00e4\u00df nur zul\u00e4ssige Ergebnisse. Wenn nach dem \u00d6ffnen einer Transaktion und der Ausf\u00fchrung einiger Operationen festgestellt wird, dass das Ergebnis nicht zul\u00e4ssig ist, wird ein Rollback durchgef\u00fchrt.<\/li>\n<li><b>Isolierung<\/b>. Bei der Ausf\u00fchrung einer Transaktion sollten parallele Transaktionen das Ergebnis nicht beeinflussen. Wettbewerbsf\u00e4hige Transaktionen sind durch pessimistische Sperren auf dem Koordinator isoliert. F\u00fcr Lesevorg\u00e4nge au\u00dferhalb der Transaktion gilt der Isolationsgrundsatz auf der Ebene Read Committed.<\/li>\n<li><b>Widerstandsf\u00e4higkeit<\/b>. Unabh\u00e4ngig von Problemen auf unteren Ebenen \u2014 Stromausfall, Hardwarefehler \u2014 m\u00fcssen die \u00c4nderungen, die von einer erfolgreich abgeschlossenen Transaktion vorgenommen wurden, nach dem Wiederanlauf des Systems erhalten bleiben. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Lesen \u00fcber 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>\nDiese Tabelle hat eine ID (Prim\u00e4rschl\u00fcssel), einen Besitzer und ein \u00c4nderungsdatum. Wir m\u00fcssen eine sehr einfache Abfrage durchf\u00fchren \u2014 die Daten nach dem Besitzer mit dem \u00c4nderungsdatum \u201ein den letzten 24 Stunden\u201c abfragen. <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nDamit eine solche Abfrage schnell ausgef\u00fchrt wird, muss in einer klassischen SQL-Datenbank ein Index \u00fcber die Spalten (owner, modified) erstellt werden. Das k\u00f6nnen wir ziemlich einfach umsetzen, da wir nun ACID-Garantien haben!<\/p>\n<h2>Indizes in C*One<\/h2>\n<p>\nEs gibt eine Ursprungstabelle 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 den Index C*One wird eine neue Tabelle erstellt, die eine Kopie der Ausgangstabelle darstellt. Der Schl\u00fcssel entspricht dem Indexausdruck und umfasst zudem den Prim\u00e4rschl\u00fcssel des Datensatzes aus der Ausgangstabelle:<\/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 \/>\nNun kann die Abfrage nach \u00abBesitzer in den letzten 24 Stunden\u00bb wie folgt umformuliert werden: 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 Ausgangstabelle photos und des Index i1 wird automatisch vom Koordinator aufrechterhalten. Basierend auf dem Schema der Daten generiert der Koordinator bei \u00c4nderungen nicht nur das Haupttabelle-Update, sondern merkt sich auch die \u00c4nderungen bei Kopien. Es sind keine zus\u00e4tzlichen Aktionen mit der Indextabelle erforderlich, Protokolle werden nicht gelesen, Sperren werden nicht verwendet. Das Hinzuf\u00fcgen von Indizes verbraucht also fast keine Ressourcen und hat praktisch keinen Einfluss auf die Geschwindigkeit der Anwendung von Modifikationen.<\/p>\n<p>Mit ACID haben wir Indizes wie in SQL implementiert. Sie bieten Konsistenz, Skalierbarkeit, hohe Geschwindigkeiten, k\u00f6nnen zusammengesetzt werden und sind in die CQL-Abfragesprache integriert. F\u00fcr die Unterst\u00fctzung der Indizes sind keine \u00c4nderungen am Anwendungscode erforderlich. Alles ist so einfach wie in SQL. Und das Wichtigste ist, dass Indizes die Ausf\u00fchrungsgeschwindigkeit der Modifikationen der urspr\u00fcnglichen Transaktionstabelle nicht beeinflussen.<\/p>\n<h2>Was entstanden ist<\/h2>\n<p>\nWir haben C*One vor drei Jahren entwickelt und in den produktiven Einsatz gebracht. <\/p>\n<p>Was haben wir letztendlich erreicht? Lassen Sie uns das am Beispiel des Systems zur Verarbeitung und Speicherung von Fotos betrachten, einem der wichtigsten Datentypen in sozialen Netzwerken. Dabei geht es nicht um die Fotos selbst, sondern um die vielf\u00e4ltigen Metainformationen. Momentan hat \"Odnoklassniki\" etwa 20 Milliarden solcher Eintr\u00e4ge, das System verarbeitet 80.000 Leseanforderungen pro Sekunde und bis zu 8.000 ACID-Transaktionen pro Sekunde, die mit der Datenmodifikation verbunden sind. <\/p>\n<p>Als wir SQL mit einem Replikationsfaktor von 1 verwendeten (aber in RAID 10), wurde die Metainformation der Fotos in einem hochverf\u00fcgbaren Cluster aus 32 Maschinen mit Microsoft SQL Server (plus 11 Sicherungsservern) gespeichert. Zus\u00e4tzlich wurden 10 Server f\u00fcr die Sicherung von Backups bereitgestellt. Insgesamt handelt es sich um 50 kostspielige Maschinen. Dabei arbeitete das System mit einer nominalen Last, ohne Spielraum.<\/p>\n<p>Nach der Migration auf das neue System haben wir einen Replikationsfaktor von 3 erreicht \u2014 eine Kopie in jedem Rechenzentrum. Das System besteht aus 63 Knoten des Cassandra-Speichers und 6 Steuerungsmaschinen, insgesamt 69 Server. Diese Maschinen sind jedoch deutlich g\u00fcnstiger, ihre Gesamtkosten betragen etwa 30 % der Kosten des SQL-Systems. Die Last liegt konstant bei 30 %.<\/p>\n<p>Mit der Einf\u00fchrung von C*One haben sich auch die Latenzen verringert: Bei SQL ben\u00f6tigte ein Schreibvorgang etwa 4,5 ms. Bei C*One liegt die Zeit bei ungef\u00e4hr 1,6 ms. Die Dauer einer Transaktion liegt im Durchschnitt unter 40 ms, der Commit erfolgt in 2 ms, die Lese- und Schreibdauer betr\u00e4gt im Durchschnitt 2 ms. Der 99. Perzentil liegt bei gerade einmal 3-3,1 ms, die Anzahl der Timeouts ist um das 100-fache gesenkt worden \u2014 alles dank der breiten Anwendung von Spekulationen. <\/p>\n<p>Bis jetzt wurde der Gro\u00dfteil der SQL Server-Nodes stillgelegt, neue Produkte werden ausschlie\u00dflich mit C*One entwickelt. Wir haben C*One f\u00fcr den Betrieb in unserer Cloud optimiert. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, was die Bereitstellung neuer Cluster beschleunigt, die Konfiguration vereinfacht und den Betrieb automatisiert. Ohne den Quellcode w\u00e4re dies deutlich komplizierter und fehleranf\u00e4lliger gewesen. <\/p>\n<p>Aktuell arbeiten wir daran, andere Speicher in die Cloud zu migrieren \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 4.9.10 - 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 \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\" \/>\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) 4.9.10\" \/>\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 \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\" \/>\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 an in Echtzeit verarbeiteten Daten in SQL Server gespeichert. F\u00fcr ein so gro\u00dfes Volumen einen schnellen, zuverl\u00e4ssigen und ausfallsicheren Datenzentrum-Zugang mit einer SQL-Datenbank zu gew\u00e4hrleisten, ist praktisch unm\u00f6glich. In solchen F\u00e4llen wird normalerweise eines der NoSQL-Speicher verwendet, jedoch l\u00e4sst sich nicht alles in NoSQL migrieren: einige Entit\u00e4ten erfordern","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 \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","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"},"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}]}}