
Hallo! Mein Name ist Maxim Matyukhin, ich bin ein PHP-Programmierer bei
Badoo . In unserer Arbeit setzen wir MySQL aktiv ein. Aber manchmal fehlt uns die Leistung, deshalb suchen wir ständig nach Wegen, um die Arbeit zu beschleunigen.
Im Jahr 2010 führte Yoshinori Matsunobu das NoSQL MySQL-Plugin namens HandlerSocket ein. Es wurde behauptet, dass Sie mit diesem Plugin mehr als 750.000 Anfragen pro Sekunde ausführen können. Wir wurden neugierig und begannen fast sofort, diese Lösung zu verwenden. Das Ergebnis hat uns so gut gefallen, dass wir angefangen haben,
Präsentationen zu machen und
Artikel zu schreiben
, die für HandlerSocket werben.
Anscheinend waren wir einer der wenigen Benutzer dieses Plugins - seit MySQL 5.7 funktioniert es nicht mehr. In dieser Version erschien jedoch ein anderes Oracle-Plugin - das Memo-Plugin von InnoDB, das ähnliche Funktionen versprach.
Trotz der Tatsache, dass das Memcached-Plugin 2013 bereits in MySQL 5.6 veröffentlicht wurde, gibt es nicht so viele Artikel darüber und zum größten Teil wiederholen sie die Dokumentation: Es wird ein einfaches Label erstellt und über den Memcached-Client werden Anforderungen an das Plugin gestellt.
Wir haben umfangreiche Erfahrungen mit Memcached und sind es gewohnt, einfach damit zu interagieren. Von InnoDB memcached Plugin erwarteten wir die gleiche Einfachheit. Tatsächlich stellte sich jedoch heraus, dass, wenn sich die Muster für die Verwendung des Plug-Ins zumindest geringfügig von den in der Dokumentation und den Artikeln beschriebenen unterscheiden, viele Nuancen und Einschränkungen auftauchen, die auf jeden Fall eine Überlegung wert sind, wenn Sie das Plug-In verwenden.
MySQL HandlerSocket
In diesem Artikel werden wir das neue memcached Plugin auf die eine oder andere Weise mit dem alten HandlerSocket vergleichen. Daher erinnere ich mich, dass es das letztere war.
Nach der Installation des HandlerSocket-Plugins hörte MySQL zwei zusätzliche Ports ab:
- Der erste Port erhielt Clientanforderungen zum Lesen von Daten.
- Der zweite Port empfing Clientanforderungen zur Datenaufzeichnung.
Der Client musste eine normale TCP-Verbindung an einem dieser Ports herstellen (es wurde keine Authentifizierung unterstützt), und danach musste der Befehl "open index" gesendet werden (ein spezieller Befehl, mit dem der Client informierte, welche Tabelle von welchem Index welche Felder wir wollten lesen (oder schreiben)).
Wenn der Befehl "open index" erfolgreich funktioniert hat, können Sie je nach Port, an dem die Verbindung hergestellt wurde, GETs oder INSERT / UPDATE / DELETE-Befehle senden.
Mit HandlerSocket konnten nicht nur GETs für den Primärschlüssel ausgeführt werden, sondern auch einfache Beispiele aus einem nicht eindeutigen Index, Bereichsbeispiele, unterstützte Multigets und LIMIT. Gleichzeitig war es möglich, mit der Tabelle sowohl aus normalem SQL als auch über das Plugin zu arbeiten. Auf diese Weise konnten Sie beispielsweise einige Änderungen an Transaktionen über SQL vornehmen und diese Daten dann über HandlerSocket lesen.
Es ist wichtig, dass HandlerSocket alle Verbindungen mit einem begrenzten Pool von Threads über epoll verarbeitet. Daher war es einfach, Zehntausende von Verbindungen zu unterstützen, während in MySQL selbst für jede Verbindung ein Thread erstellt wurde und deren Anzahl sehr begrenzt war.
Gleichzeitig ist es immer noch ein gewöhnlicher MySQL-Server - eine uns bekannte Technologie. Wir wissen, wie man es repliziert und überwacht. Die Überwachung von HandlerSocket ist schwierig, da keine spezifischen Metriken bereitgestellt werden. Einige der Standardmetriken für MySQL und InnoDB sind jedoch nützlich.
Es gab natürlich Unannehmlichkeiten, insbesondere unterstützte dieses Plugin die Arbeit mit dem Zeitstempeltyp nicht. Nun, das HandlerSocket-Protokoll ist schwerer zu lesen und daher schwerer zu debuggen.
Lesen Sie
hier mehr über HandlerSocket. Sie können sich auch
eine unserer Präsentationen ansehen.
InnoDB memcached Plugin
Was bietet uns das neue memcached Plugin?
Wie der Name schon sagt, besteht seine Idee darin, den memcached-Client zu verwenden, um mit MySQL zu arbeiten und Daten über memcached-Befehle zu empfangen und zu speichern.
Über die Hauptvorteile des Plugins können Sie
hier lesen .
Wir sind am meisten an Folgendem interessiert:
- Geringer CPU-Verbrauch.
- Die Daten werden in InnoDB gespeichert, was bestimmte Garantien gibt.
- Sie können mit Daten sowohl über Memcached als auch über SQL arbeiten. Sie können mit den in MySQL integrierten Tools repliziert werden.
Sie können dieser Liste folgende Pluspunkte hinzufügen:
- Schnelle und günstige Verbindung. Eine reguläre MySQL-Verbindung wird von einem Thread verarbeitet, und die Anzahl der Threads ist begrenzt. Im Memcached-Plugin verarbeitet ein Thread alle Verbindungen in der Ereignisschleife.
- Die Möglichkeit, mehrere Schlüssel mit einer GET-Anforderung anzufordern.
- Wenn Sie mit MySQL HandlerSocket vergleichen möchten, müssen Sie im memcached Plugin nicht den Befehl "Open Table" verwenden, und alle Lese- und Schreibvorgänge werden an einem Port ausgeführt.
Weitere Details zum Plugin finden Sie in der offiziellen
Dokumentation . Für uns waren die nützlichsten Seiten:
- InnoDB memcached Architektur .
- InnoDB memcached Plugin Internals .
Nach der Installation des Plugins akzeptiert MySQL Verbindungen über Port 11211 (Standard-Memcached-Port). Eine spezielle Datenbank (Schema) innodb_memcache wird ebenfalls angezeigt, in der Sie den Zugriff auf Ihre Tabellen konfigurieren.
Einfaches Beispiel
Angenommen, Sie haben bereits eine Tabelle, mit der Sie über das zwischengespeicherte Protokoll arbeiten möchten:
CREATE TABLE `auth` ( `email` varchar(96) NOT NULL, `password` varchar(64) NOT NULL, `type` varchar(32) NOT NULL DEFAULT '', PRIMARY KEY (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
und Sie möchten Daten auf dem Primärschlüssel empfangen und ändern.
Sie müssen zuerst die Entsprechung zwischen dem zwischengespeicherten Schlüssel und der SQL-Tabelle in der Tabelle innodb_memcache.containers beschreiben. Diese Tabelle sieht ungefähr so aus (ich habe die Codierungsbeschreibung entfernt, um das Lesen zu erleichtern):
CREATE TABLE `containers` ( `name` varchar(50) NOT NULL, `db_schema` varchar(250) NOT NULL, `db_table` varchar(250) NOT NULL, `key_columns` varchar(250) NOT NULL, `value_columns` varchar(250) DEFAULT NULL, `flags` varchar(250) NOT NULL DEFAULT '0', `cas_column` varchar(250) DEFAULT NULL, `expire_time_column` varchar(250) DEFAULT NULL, `unique_idx_name_on_key` varchar(250) NOT NULL, PRIMARY KEY (`name`) ) ENGINE=InnoDB DEFAULT
Die wichtigsten Felder:
- Name - Präfix Ihres Memcached-Schlüssels;
- db_schema - Name der Basis (Schaltung);
- db_table ist Ihre Tabelle;
- key_columns - der Name des Feldes in der Tabelle, nach dem gesucht wird (normalerweise ist dies Ihr Primärschlüssel);
- value_columns - eine Liste von Feldern aus der Tabelle, die dem memcached Plugin zur Verfügung stehen;
- unique_idx_name_on_key ist der Index, nach dem gesucht werden soll (obwohl Sie bereits key_columns angegeben haben, können sie sich in verschiedenen Indizes befinden und Sie müssen den Index explizit angeben).
Die restlichen Felder sind für den Anfang nicht sehr wichtig.
Fügen Sie innodb_memcache.containers eine Beschreibung unserer Tabelle hinzu:
INSERT INTO innodb_memcache.containers SET name='auth', db_schema='test', db_table='auth', key_columns='email', value_columns='password|type', flags='0', cas_column='0', expire_time_column='0', unique_idx_name_on_key='PRIMARY';
In diesem Beispiel ist name = 'auth' das Präfix unseres zwischengespeicherten Schlüssels. In der Dokumentation wird es oft als table_id bezeichnet, und später im Artikel werde ich diesen Begriff verwenden.
Stellen Sie nun eine Verbindung zwischen TELNET und dem zwischengespeicherten Plugin her und versuchen Sie, die Daten zu speichern und abzurufen:
[21:26:22] maxm@localhost: ~> telnet memchached-mysql.dev 11211 Trying 127.0.0.1... Connected to memchached-mysql.dev. Escape character is '^]'. get @@auth.max@example.com END set @@auth.max@example.com 0 0 10 1234567|89 STORED get @@auth.max@example.com VALUE @@auth.max@example.com 0 10 1234567|89 END
Zuerst haben wir eine GET-Anfrage gesendet, die uns nichts zurückgegeben hat. Dann haben wir die Daten mit einer SET-Anfrage gespeichert, danach haben wir sie mit einem GET zurückbekommen.
GET gab die folgende Zeile zurück: 1234567 | 89. Dies sind die Werte der Felder "Passwort" und "Typ", die durch das Symbol "|" getrennt sind. Felder werden in der Reihenfolge zurückgegeben, in der sie in innodb_memcache.containers.value_columns beschrieben wurden.
Vielleicht fragen Sie sich jetzt: "Was passiert, wenn das Symbol" | "im" Passwort "vorkommt?" Ich werde unten darüber sprechen.
Über SQL sind diese Daten auch verfügbar:
MySQL [(none)]> select * from auth where email='max@example.com'; +-----------------+----------+------+ | email | password | type | +-----------------+----------+------+ | max@example.com | 1234567 | 89 | +-----------------+----------+------+ 1 row in set (0.00 sec)
Standard table_id
Es gibt auch eine solche Betriebsart:
get @@auth VALUE @@auth 0 21 test/auth END get max@example.com VALUE max@example.com 0 10 1234567|99 END set ivan@example.com 0 0 10 qwerty|xxx STORED get ivan@example.com VALUE ivan@example.com 0 10 qwerty|xxx END
In diesem Beispiel machen wir mit get @@ auth table_id auth zum Standardpräfix für diese Verbindung. Danach können alle nachfolgenden Abfragen ohne Angabe von table_id durchgeführt werden.
Bisher ist alles einfach und logisch. Aber wenn Sie anfangen zu verstehen, dann gibt es viele Nuancen. Ich werde Ihnen sagen, was wir gefunden haben.
Nuancen
Zwischenspeichern der Tabelle innodb_memcache.containers
Das zwischengespeicherte Plugin liest die Tabelle innodb_memcache.containers beim Start einmal. Wenn eine unbekannte table_id über das Memcached-Protokoll eintrifft, sucht das Plugin in der Tabelle danach. Daher können Sie problemlos neue Schlüssel (table_id) hinzufügen. Wenn Sie jedoch die Einstellungen einer vorhandenen table_id ändern möchten, müssen Sie das zwischengespeicherte Plugin neu starten:
mysql> UNINSTALL PLUGIN daemon_memcached; mysql> INSTALL PLUGIN daemon_memcached soname "libmemcached.so";
Zwischen diesen beiden Anforderungen funktioniert die Memcached-Schnittstelle nicht. Aus diesem Grund ist es oft einfacher, eine neue table_id zu erstellen, als die vorhandene zu ändern und das Plugin neu zu starten.
Es war eine Überraschung für uns, dass eine so wichtige Nuance des Plug-In-Vorgangs auf der Seite
Anpassen einer zwischengespeicherten Anwendung für das Memo-Plugin-Plugin von InnoDB beschrieben wird , was für solche Informationen kein sehr logischer Ort ist.
Flags, cas_column, expire_time_column
Diese Felder werden benötigt, um einige Funktionen von Memcached zu simulieren. Die Dokumentation für sie ist inkonsistent. Die meisten Beispiele veranschaulichen die Arbeit mit Tabellen, in denen sich diese Felder befinden. Möglicherweise müssen Sie sie zu Ihren Tabellen hinzufügen (und dies sind mindestens drei INT-Felder). Aber nein. Wenn Sie keine solchen Felder in den Tabellen haben und keine Memcached-Funktionen wie CAS, Ablauf oder Flags verwenden möchten, müssen Sie diese Felder nicht zu den Tabellen hinzufügen.
Wenn Sie die Tabelle in innodb_memcache.containers konfigurieren, müssen Sie '0' in diese Felder eingeben und genau die Zeile mit Null machen:
INSERT INTO innodb_memcache.containers SET name='auth', db_schema='test', db_table='auth', key_columns='email', value_columns='password|type', flags='0', cas_column='0', expire_time_column='0', unique_idx_name_on_key='PRIMARY';
Es ist ärgerlich, dass cas_column und expire_time_column den Standardwert NULL haben. Wenn Sie INSERT INTO innodb_memcache.containers ausführen, ohne für diese Felder den Wert '0' anzugeben, wird NULL in ihnen gespeichert und dieses Memcache-Präfix funktioniert einfach nicht.
Datentypen
Aus der Dokumentation geht nicht klar hervor, welche Datentypen bei der Arbeit mit dem Plugin verwendet werden können. An mehreren Stellen wird gesagt, dass das Plugin nur mit Textfeldern (CHAR, VARCHAR, BLOB) arbeiten kann. Hier:
Anpassen eines vorhandenen MySQL-Schemas für das InnoDB-Memcached-Plugin bietet die Möglichkeit, Zahlen in Zeichenfolgenfeldern zu speichern. Wenn Sie dann mit diesen Zahlenfeldern aus SQL arbeiten müssen, erstellen Sie eine ANSICHT, in der VARCHAR-Felder mit Zahlen in INTEGER-Felder konvertiert werden ::
CREATE VIEW numbers AS SELECT c1 KEY, CAST(c2 AS UNSIGNED INTEGER) val FROM demo_test WHERE c2 BETWEEN '0' and '9999999999';
An einigen Stellen in der Dokumentation steht jedoch noch, dass Sie mit Zahlen arbeiten können. Bisher haben wir nur echte Produktionserfahrung mit Textfeldern, aber die experimentellen Ergebnisse zeigen, dass das Plugin auch mit Zahlen funktioniert:
CREATE TABLE `numbers` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `counter` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB INSERT INTO innodb_memcache.containers SET name='numbers', db_schema='test', db_table='numbers', key_columns='id', value_columns='counter', flags='0', cas_column='0',expire_time_column='0',unique_idx_name_on_key='PRIMARY';
Danach über das Memcached-Protokoll:
get @@numbers.1 END set @@numbers.1 0 0 2 12 STORED get @@numbers.1 VALUE @@numbers.1 0 2 12 END
Wir sehen, dass das zwischengespeicherte Plugin alle Datentypen zurückgeben kann. Er gibt sie jedoch in der Form zurück, in der sie in InnoDB liegen. Im Fall von timestamp / datetime / float / decimal / JSON wird beispielsweise eine Binärzeichenfolge zurückgegeben. Ganzzahlen werden jedoch so zurückgegeben, wie wir sie über SQL sehen.
Multiget
Mit dem zwischengespeicherten Protokoll können Sie mehrere Schlüssel mit einer einzigen Anforderung anfordern:
get @@numbers.2 @@numbers.1 VALUE @@numbers.2 0 2 12 VALUE @@numbers.1 0 2 13 END
Die Tatsache, dass Multiget funktioniert, ist bereits gut. Aber es funktioniert im Rahmen einer table_id:
get @@auth.ivan@example.com @@numbers.2 VALUE @@auth.ivan@example.com 0 10 qwerty|xxx END
Dieser Punkt wird in der Dokumentation hier beschrieben:
https://dev.mysql.com/doc/refman/8.0/en/innodb-memcached-multiple-get-range-query.html . Es stellt sich heraus, dass Sie in Multiget table_id nur für den ersten Schlüssel angeben können, wenn alle anderen Schlüssel aus der Standard-table_id stammen (Beispiel aus der Dokumentation):
get @@aaa.AA BB VALUE @@aaa.AA 8 12 HELLO, HELLO VALUE BB 10 16 GOODBYE, GOODBYE END
In diesem Beispiel wird der zweite Schlüssel aus der Standard-Tabellen-ID übernommen. Wir könnten viel mehr Schlüssel aus der Standard-Tabellen-ID angeben, und für den ersten Schlüssel haben wir eine separate Tabellen-ID angegeben, und dies ist nur im Fall des ersten Schlüssels möglich.
Wir können sagen, dass Multiget im Rahmen einer Tabelle funktioniert, da Sie sich nicht auf eine solche Logik im Produktionscode verlassen möchten: Es ist nicht offensichtlich, es ist leicht, sie zu vergessen und einen Fehler zu machen.
Im Vergleich zu HandlerSocket arbeitete auch dort Multiget in derselben Tabelle. Diese Einschränkung sah jedoch natürlich aus: Der Client öffnet den Index in der Tabelle und fordert einen oder mehrere Werte von ihm an. Wenn Sie jedoch mit dem Multiget-Memcached-Plugin an mehreren Schlüsseln mit unterschiedlichen Präfixen arbeiten, ist dies normale Praxis. Und Sie erwarten dasselbe vom MySQL-Memcached-Plugin. Aber nein :(
INCR, DEL
Ich habe bereits Beispiele für GET / SET-Anfragen gegeben. INCR- und DEL-Abfragen haben eine Funktion. Es liegt in der Tatsache, dass sie nur funktionieren, wenn die Standard-Tabellen-ID verwendet wird:
DELETE @@numbers.1 ERROR get @@numbers VALUE @@numbers 0 24 test/numbers END delete 1 DELETED
Eingeschränkte Protokollbeschränkungen
Memcached verfügt über ein Textprotokoll, das einige Einschränkungen auferlegt. Beispielsweise sollten zwischengespeicherte Schlüssel keine Leerzeichen (Leerzeichen, Zeilenvorschub) enthalten. Wenn Sie sich die Beschreibung der Tabelle aus unserem Beispiel noch einmal ansehen:
CREATE TABLE `auth` ( `email` varchar(96) NOT NULL, `password` varchar(64) NOT NULL, `type` varchar(32) NOT NULL DEFAULT '', PRIMARY KEY (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Dies bedeutet, dass im Feld "E-Mail" keine solchen Zeichen vorhanden sein sollten.
Außerdem müssen zwischengespeicherte Schlüssel kleiner als 250 Byte sein (Bytes, keine Zeichen). Wenn Sie mehr senden, erhalten Sie eine Fehlermeldung:
"CLIENT_ERROR bad command line format"
Außerdem muss berücksichtigt werden, dass das memcached Plugin dem memcached Protokoll eine eigene Syntax hinzufügt. Beispielsweise wird das Zeichen "|" verwendet. als Feldtrennzeichen in der Antwort. Sie müssen sicherstellen, dass dieses Symbol in Ihrer Tabelle nicht verwendet wird. Das Trennzeichen kann konfiguriert werden, die Einstellungen gelten jedoch für alle Tabellen auf dem gesamten MySQL-Server.
Feldtrennzeichen value_columns
Wenn Sie mehrere Spalten über das zwischengespeicherte Protokoll zurückgeben müssen, wie in unserem ersten Beispiel:
get @@auth.max@example.com VALUE @@auth.max@example.com 0 10 1234567|89 END
dann werden die Spaltenwerte durch das Standardtrennzeichen "|" getrennt. Es stellt sich die Frage: "Was passiert, wenn sich beispielsweise das Zeichen" | "im ersten Feld der Zeile befindet?" Das zwischengespeicherte Plugin gibt in diesem Fall die Zeichenfolge unverändert zurück: 1234 | 567 | 89. Im allgemeinen Fall ist es unmöglich zu verstehen, wo welches Feld ist.
Daher ist es wichtig, sofort das richtige Trennzeichen auszuwählen. Und da es für alle Schlüssel aller Tabellen verwendet wird, sollte es ein universelles Symbol sein, das in keinem Feld gefunden wird, mit dem Sie das zwischengespeicherte Protokoll bearbeiten.
Zusammenfassung
Dies bedeutet nicht, dass das zwischengespeicherte Plugin schlecht ist. Man hat jedoch den Eindruck, dass es für ein bestimmtes Arbeitsschema geschrieben wurde: einen MySQL-Server mit einer Tabelle, auf die über das memcached-Protokoll zugegriffen werden kann, und diese table_id wird als Standard festgelegt. Clients stellen eine dauerhafte Verbindung mit dem Memcached-Plugin her und stellen Anforderungen an die Standard-Tabellen-ID. Wahrscheinlich wird in einem solchen Schema alles einwandfrei funktionieren. Wenn Sie sich davon entfernen, stoßen Sie auf verschiedene Unannehmlichkeiten.
Möglicherweise haben Sie einige Plugin-Leistungsberichte erwartet. Wir haben uns jedoch noch nicht entschieden, es an stark belasteten Orten einzusetzen. Wir haben es nur in einigen nicht sehr ausgelasteten Systemen verwendet und dort funktioniert es ungefähr mit der gleichen Geschwindigkeit wie das HandlerSocket, aber wir haben keine ehrlichen Benchmarks erstellt. Trotzdem bietet das Plugin eine solche Schnittstelle, mit der der Programmierer leicht einen Fehler machen kann - Sie müssen viele Nuancen berücksichtigen. Daher sind wir noch nicht bereit, dieses Plugin in großen Mengen zu verwenden.
Wir haben im MySQL-Bug-Tracker einige Feature-Anfragen gestellt:
https://bugs.mysql.com/bug.php?id=95091https://bugs.mysql.com/bug.php?id=95092https://bugs.mysql.com/bug.php?id=95093https://bugs.mysql.com/bug.php?id=95094Hoffen wir, dass das Memcached Plugin-Entwicklungsteam sein Produkt verbessern wird.