Dies ist eine technische Studie darüber, wie wir unsere Daten auf mehrere MySQL-Server aufteilen. Wir haben diesen Sharding-Ansatz Anfang 2012 abgeschlossen und es ist immer noch das System, das wir heute zum Speichern von Kerndaten verwenden.
Bevor wir besprechen, wie die Daten aufgeteilt werden, wollen wir zunächst unsere Daten verstehen. Stimmungsvolle Beleuchtung, mit Schokolade überzogene Erdbeeren, Star Trek-Zitate ...
Pinteres ist die Entdeckungsmaschine für alles, was Sie interessiert. Aus Datensicht ist Pinterest die weltweit größte Sammlung von Bildern von menschlichem Interesse. Es gibt über 50 Milliarden Pins, die Pinner auf 1 Milliarde Boards gespeichert haben. Benutzer pinnen erneut, wie die Pins anderer Personen (grob gesagt, eine oberflächliche Kopie), folgen anderen Pinnern, Foren und Interessen und sehen sich dann alle Beiträge der abonnierten Pinner auf der Startseite an. Sehr gut! Jetzt lass es skalieren!
Wachstumsschmerzen
2011 hatten wir Erfolg. In einigen Evaluierungsberichten wachsen wir deutlich schneller als andere Startups. Im September 2011 wurde jede unserer Grundausstattungen übertroffen. Wir haben eine Reihe von NoSQL-Technologien eingesetzt, die alle zu katastrophalen Ergebnissen geführt haben. Gleichzeitig verursachte die große Anzahl der zum Lesen verwendeten MySQL-Slave-Server viele lästige Fehler, insbesondere beim Caching. Wir haben das gesamte Datenspeichermodell neu strukturiert. Damit dies gelingt, formulieren wir unsere Anforderungen sorgfältig.
Geschäftsanforderungen
Unser gesamtes System muss sehr stabil, einfach zu bedienen und leicht zu erweitern sein. Wir hoffen, dass die unterstützte Datenbank mit einer kleinen Speicherkapazität beginnen und mit der Geschäftsentwicklung erweitert werden kann.
Alle per Pin generierten Inhalte müssen auf der Website leicht zugänglich sein.
Unterstützt die Anforderung des Zugriffs auf N Pins, die in einer bestimmten Reihenfolge auf der Zeichenfläche angezeigt werden sollen (z. B. entsprechend dem Zeitpunkt der Erstellung oder in einer benutzerspezifischen Reihenfolge). Gefällt mir Pin-Freunde und die Pin-Liste der Pin-Freunde können auch in einer bestimmten Reihenfolge angezeigt werden.
Der Einfachheit halber sorgen Updates im Allgemeinen für die besten Ergebnisse. Um letztendlich Konsistenz zu erreichen, benötigen Sie etwas Zusätzliches, wie zum Beispiel ein verteiltes Transaktionsprotokoll. Das ist eine interessante und (nicht) einfache Sache.
Lösungsideen und Schlüsselpunkte
Die Lösung erfordert die Verteilung riesiger Datenabschnitte auf mehrere Datenbankinstanzen und kann keine Verbindungen, Fremdschlüssel oder Indizierung und andere Methoden verwenden, um das Ganze zu integrieren Daten. Wenn Sie darüber nachdenken, können sich korrelierte Unterabfragen nicht über verschiedene Datenbankinstanzen erstrecken.
Unsere Lösung erfordert Lastausgleichsdatenzugriff. Wir hassen die Datenmigration, insbesondere die Datensatz-für-Datensatz-Migration, die sehr fehleranfällig ist und das System aufgrund der Komplexität der Beziehungen unnötig komplexer macht. Wenn Daten migriert werden müssen, ist es am besten, den gesamten Satz logischer Knoten zu migrieren.
Um eine zuverlässige und schnelle Implementierung der Lösung zu erreichen, müssen wir die am einfachsten zu implementierende und robusteste technische Lösung auf unserer verteilten Datenplattform verwenden.
Alle Daten auf jeder Instanz werden als Datensicherung vollständig auf eine Slave-Instanz kopiert. Wir verwenden S3, eine hochverfügbare MapReduce (verteilte Computerumgebung). Unsere Front-End-Geschäftslogik greift auf Hintergrunddaten und nur auf die Hauptinstanz der Datenbank zu. Gewähren Sie Ihrem Front-End-Unternehmen niemals Lese- und Schreibzugriff auf Slave-Instanzen. Da es zu einer Verzögerung bei der Datensynchronisierung mit der Master-Instanz kommt, führt dies zu unerklärlichen Fehlern. Sobald die Daten aufgeteilt und verteilt sind, gibt es für Ihr Front-End-Unternehmen keinen Grund, Daten von der Slave-Instanz zu lesen und zu schreiben.
Schließlich müssen wir sorgfältig eine hervorragende Lösung entwerfen, um die global eindeutige Kennung (UUID) aller unserer Datenobjekte zu generieren und zu analysieren.
Unsere Slicing-Lösung
Egal was passiert, wir müssen eine Datenverteilungslösung entwerfen, die unseren Anforderungen entspricht, robust ist, eine gute Leistung bietet und wartbar ist. Mit anderen Worten: Es kann nicht naiv sein (ohne umfassende Validierung). Daher basiert unser grundlegendes Design auf MySQL. Wir haben uns für eine ausgereifte Technologie entschieden. Zu Beginn des Entwurfs würden wir natürlich vor solchen Datenbankprodukten zurückschrecken, die behaupten, über die neuen Fähigkeiten der automatischen Skalierung zu verfügen, wie zum Beispiel MongoDB, Cassandra und Membase, weil sie scheinbar einfach zu implementieren sind, aber eine schlechte Anwendbarkeit (häufig) aufweisen Es ist ein unerklärlicher Fehler aufgetreten, der einen Absturz verursacht hat.
Erzählung: Es wird dringend empfohlen, mit den Grundlagen zu beginnen und die trendigen und neuen Dinge zu meiden – MySQL auf bodenständige Weise gut zu erlernen und zu verwenden. Glauben Sie mir, jedes Wort ist voller Tränen.
MySQL ist ein ausgereiftes, stabiles und benutzerfreundliches relationales Datenbankprodukt. Nicht nur wir nutzen es, sondern auch viele namhafte Großunternehmen nutzen es als Back-End-Datenunterstützung zur Speicherung riesiger Datenmengen. (Anmerkung: Vor etwa einigen Jahren wurde MySQL zusammen mit SUN von Oracle übernommen und firmierte unter dem Namen Oracle. Viele Unternehmen wie Google, Facebook usw. waren besorgt über das Open-Source-Problem von MySQL und wechselten zu einem anderen Eine vom ursprünglichen Autor von MySQL entwickelte Open-Source-Datenbank MariaDB (unter) MySQL unterstützt unsere technischen Anforderungen für sequentielle Datenanforderungen an die Datenbank, die Abfrage eines bestimmten Datenbereichs und die Transaktionsverarbeitung auf Zeilenebene (Datensatzebene). MySQL verfügt über eine Reihe von Funktionen, die wir jedoch nicht benötigen. Da MySQL selbst eine monolithische Lösung ist, müssen wir unsere Daten aufteilen. (Anmerkung: Die Bedeutung hier ist, dass eine einzelne Instanz riesige Datenmengen verwaltet, was unweigerlich zu Leistungsproblemen führt. Um nun große Gesamtdaten in einzelne Datensätze zu zerlegen, ist eine leistungsstarke technische Lösung erforderlich, um die einzelnen Daten zu trennen. Die Monolithen sind integriert als Ganzes, um die Leistung zu verbessern, ohne etwas falsch zu machen.) Das Folgende ist unser Designplan:
Wir haben mit 8 EC2-Servern begonnen, wobei auf jedem Server eine MySQL-Instanz ausgeführt wurde:
Jeder MySQL-Server wird zur Notfallwiederherstellung Master-Master auf einen redundanten Host repliziert. Unser Front-End-Geschäft liest/schreibt nur Daten von der Hauptdienstinstanz. Ich empfehle Ihnen, dasselbe zu tun, es vereinfacht viele Dinge und vermeidet verzögerte Störungen. (Anmerkung: Master-Master-Replikation ist eine Funktion, die von der MySQL-Datenbank selbst bereitgestellt wird. Sie bezieht sich auf einen Modus, in dem sich zwei Maschinen gegenseitig sichern. Im Vergleich zu anderen Modi, wie z. B. der Master-Slave-Sicherung, sind die Daten der beiden Maschinen vollständig Bei der Hintergrundsynchronisierung verfügt jede Maschine über eine eigene unabhängige IP und kann gleichzeitig darauf zugegriffen werden. Der Autor des Originalartikels hat jedoch wiederholt betont, dass die beiden Maschinen zwar redundant sind und eine primäre Sicherung verwenden, aber logisch unterschieden werden können Master und Slave lesen/schreiben immer von einem von ihnen. Wie in der Abbildung gezeigt, gibt es beispielsweise ein Master-Master-Backup zwischen MySQL001A und MySQL001B, aber Sie lesen/schreiben nur von MySQL001A. Ein anderer: Sie verwenden 16 Maschinen. , die anderen 8 Slave-Maschinen sind möglicherweise nicht EC2 oder nicht)
Jede MySQL-Instanz kann mehrere Datenbanken haben:
Beachten Sie, dass jede Datenbank einzigartig ist Name db00000, db00001, bis dbNNNN. Jede Datenbank ist ein Fragment unserer Datenbank. Wir haben das Design so gestaltet, dass, sobald ein Datenelement einem Shard zugeordnet ist, es nicht aus diesem Shard verschoben wird. Sie können jedoch mehr Kapazität erhalten, indem Sie Shards auf andere Maschinen verschieben (wir werden später darauf eingehen).
Wir pflegen eine Konfigurationsdatenbanktabelle, die aufzeichnet, auf welchem Computer sich die Slice-Datenbank befindet:
[ {“range”: (0,511), “master”: “MySQL001A”, “slave”: “MySQL001B”}, {“range”: (512, 1023), “master”: “MySQL002A”, “slave”: “MySQL002B”}, ... {“range”: (3584, 4095), “master”: “MySQL008A”, “slave”: “MySQL008B”} ]
Diese Konfigurationstabelle wird nur geändert, wenn die Slice-Datenbank migriert oder der Host ersetzt wird. Wenn beispielsweise ein Master-Instanzhost ausfällt, befördern wir seinen Slave-Instanzhost zur Master-Instanz und ersetzen ihn dann so schnell wie möglich durch eine neue Maschine als Slave-Instanzhost. Das Konfigurationsskript wird auf ZooKeeper gespeichert. Wenn die oben genannten Änderungen vorgenommen werden, wird das Skript an den Computer gesendet, der den Slicing-Dienst für Konfigurationsänderungen verwaltet. (Anmerkung: Sie können den Vorteil darin finden, dass der ursprüngliche Autor immer betont hat, dass das Front-End-Geschäft nur Daten von der logischen Master-Instanz liest und schreibt.)
Jede Slice-Datenbank verwaltet die gleiche Datenbanktabelle und Tabellenstruktur, wie z. B. Pins, Boards, User_has_Pins, User_Likes_Pins, Pin_Likes_by_User und andere Datenbanktabellen. Erstellen Sie synchron bei der Bereitstellung.
Entwurfsplan für die Verteilung von Daten an Slice-Server
Wir kombinieren die Shard-ID (Shard-ID), die Datentypidentifikation und die lokale ID (lokale ID) zu einer 64 -bit global eindeutige Identifikation (ID). Die Slice-ID (Shard-ID) belegt 16 Bit (Bit), die Datentypkennung 10 Bit (Bit) und die lokale ID (lokale ID) 36 Bit (Bit). Wer ein anspruchsvolles Auge hat, wird sofort bemerken, dass es sich hier nur um 62 Bit handelt. Meine bisherigen Erfahrungen mit der Verteilung und Integration von Daten zeigen mir, dass es von unschätzbarem Wert ist, einige wenige für die Erweiterung zu behalten. Deshalb habe ich 2 Bits behalten (auf 0 gesetzt). (Anmerkung: Lassen Sie es mich hier erklären. Gemäß den folgenden Operationen und Erklärungen beträgt die eindeutige Identifikations-ID eines Objekts 64 Bit, die höchsten 2 Bits sind immer 0, gefolgt von einer 36-Bit-lokalen Identifikation, gefolgt von einer 10-Bit-ID Typidentifikation und schließlich eine 16-Bit-Slice-ID kann 2^36 bis zu mehr als 60 Milliarden IDs darstellen. Der Datentyp kann 2^10 bis zu 1024 Objekttypen darstellen, und die Slice-ID kann in 2^16 unterteilt werden Bis zu 65536 Slice-Datenbanken. 4096 Slice-Datenbanken ausschneiden.
ID = (shard ID << 46) | (type ID << 36) | (local ID<<0) 以 Pin: https://www.pinterest.com/pin/241294492511... 为例,让我们解构这个 Pin 对象的 全局 ID 标识 241294492511762325 : Shard ID = (241294492511762325 >> 46) & 0xFFFF = 3429 Type ID = (241294492511762325 >> 36) & 0x3FF = 1 Local ID = (241294492511762325 >> 0) & 0xFFFFFFFFF = 7075733
Es ist ersichtlich, dass sich dieses Pin-Objekt in der 3429 Slice-Datenbank befindet. Angenommen, die Datentypkennung des Pin-Objekts ist 1 und sein Datensatz befindet sich in der Datensatzzeile 7075733 in der Pin-Datentabelle in der 3429-Slice-Datenbank. Unter der Annahme, dass sich die Slice-3429-Datenbank beispielsweise in MySQL012A befindet, können wir die folgende Anweisung verwenden, um ihren Datensatz abzurufen: (Anmerkung: Der ursprüngliche Autor gibt hier ein allgemeines Beispiel. Gemäß dem vorherigen Beispiel sollte sich 3429 auf MySQL007A befinden.)
conn = MySQLdb.connect(host=”MySQL012A”) conn.execute(“SELECT data FROM db03429.pins where local_id=7075733”)
有两种类型的数据:对象或关系。对象包含对象本身细节。 如 Pin 。
存储对象的数据库表
对象库表中的每个记录,表示我们前端业务中的一个对象,诸如:Pins(钉便签), users(用户),boards(白板)和 comments(注释),每个这样的记录在数据库表中设计一个标识 ID 字段(这个字段在表中作为记录的 自增主键「auto-incrementing primary key」 ,也就是我们前面提到的 局部 ID「 local ID」 ),和一个 blob 数据字段 -- 使用 JSON 保存对象的具体数据 --。
CREATE TABLE pins ( local_id INT PRIMARY KEY AUTO_INCREMENT, data TEXT, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB;
举例,一个 Pin 对象形状如下:
{“details”: “New Star Wars character”, “link”: “http://webpage.com/asdf”, “user_id”: 241294629943640797, “board_id”: 241294561224164665, …}
创建一个 Pin 对象,收集所有的数据构成 JSON blob 数据。然后,确定它的 切片 ID「 shard ID」 (我们更乐意把 Pin 对象的切片数据放到跟其所在 白板「 board」 对象相同的切片数据库里,这不是强制设计规则)。Pin 对象的数据类型标识为 1。连接到 切片 ID 指示的切片数据库,插入(insert)Pin 对象的 JOSON 数据到 Pin 对象数据库表中,MySQL 操作成功后将会返回 自增主键「auto-incrementing primary key」 给你,这个作为此 Pin 对象的 局部 ID「 local ID」。现在,我们有了 shard 、类型值、local ID 这些必要信息,就可以构建出此 Pin 对象的 64 位 ID 。(译注:原作者提到的,他们的前端业务所用到的每种对象都保存在一个对象数据库表里,每个对象记录都通过一个全局唯一 ID 去找到它,但这个全局唯一 ID 并不是数据库表中的 局部 ID,由于切片的缘故。原作者一直在讲这个设计及其原理。这样设计的目的为了海量数据切片提高性能,还要易用,可维护,可扩展。后面,作者会依次讲解到)
编辑一个 Pin 对象,使用 MySQL 事务「transaction」 在 Pin 对象的数据记录上 读出 -- 修改 -- 写回「read-modify-write」 Pin 对象的 JOSON 数据字段:
> BEGIN > SELECT blob FROM db03429.pins WHERE local_id=7075733 FOR UPDATE [修改 json blob] > UPDATE db03429.pins SET blob=’<修改后的 blob>’ WHERE local_id=7075733 > COMMIT
编辑一个 Pin 对象,您当然可以直接删除这个对象在 MySQL 数据库表中的数据记录。但是,请仔细想一下,是否在对象的 JSON 数据上加个叫做「 active」的域,把剔除工作交由前端中间业务逻辑去处理或许会更好呢。
(译注:学过关系数据库的应知道,自增主键在记录表中是固实,在里面删除记录,会造成孔洞。当多了,势必造成数据库性能下降。数据库只负责保存数据和高性能地查询、读写数据,其数据间的关系完全靠设计精良的对象全局 ID 通过中间件逻辑去维护 这样的设计理念一直贯穿在作者的行文中。只有理解了这点您才能抓住这篇文章的核心)
关系映射数据库表
关系映射表表示的是前端业务对象间的关系。诸如:一个白板(board)上有哪些钉便签(Pin), 一个钉便签(Pin)在哪些白板(board)上等等。表示这种关系的 MySQL 数据库表包括 3 个字段:一个 64 位的「from」ID, 一个 64 位的「to」ID 和一个顺序号。每个字段上都做索引方便快速查询。其记录保存在根据「from」字段 ID 解构出来的切片 ID 指示出的切片数据库上。
CREATE TABLE board_has_pins ( board_id INT, pin_id INT, sequence INT, INDEX(board_id, pin_id, sequence) ) ENGINE=InnoDB;
(译注:这里的关系映射指前端业务对象间的关系用数据库表来运维,并不指我上节注释中说到的关系数据库的关系映射。作者开篇就讲到,由于切片,不能做关系数据库表间的关系映射的,如一对一,一对多,多对多等关系关联)
关系映射表是单向的,如 board_has_pins(板含便签)表方便根据 board (白板)ID 查询其上有多少 Pin(钉便签)。若您需要根据 Pin(钉便签)ID 查询其都在哪些 board(白板)上,您可另建个表 pin_owned_by_board(便签属于哪些白板)表,其中 sequence 字段表示 Pin 在 board 上的顺序号。(由于数据分布在切片数据库上,我们的 ID 本身无法表示其顺序)我们通常将一个新的 Pin 对象加到 board 上时,将其 sequence 设为当时的系统时间。sequence 可被设为任意整数,设为当时的系统时间,保证新建的对象的 sequence 总是大于旧对象的。这是个方便易行的方法。您可通过下面的语句从关系映射表中查询对象数据集:
SELECT pin_id FROM board_has_pins WHERE board_id=241294561224164665 ORDER BY sequence LIMIT 50 OFFSET 150
语句会查出 50 个 pin_ids(便签 ID ), 随后可用这些对象 ID 查询其具体信息。
Wir bilden diese Beziehungen nur auf der Ebene der Geschäftsanwendung ab, z. B. Board_ID -> Pin_IDs -> Pin-Objekte (von Whiteboard-ID -> Notiz-IDs -> Notizobjekte). Ein großartiges Merkmal dieses Designs ist, dass Sie diese relationalen Kartenpaare separat zwischenspeichern können. Beispielsweise speichern wir die Beziehungszuordnung pin_id –> Mapping-Cache auf Redis auf dem Cluster-Server. Auf diese Weise kann es sehr gut für unsere Optimierungs-Caching-Technologiestrategie geeignet sein.
Servicefähigkeiten steigern
In unserem System gibt es drei Hauptmöglichkeiten zur Verbesserung der Serviceverarbeitungsfähigkeiten. Am einfachsten ist es, die Maschine zu aktualisieren (größerer Speicherplatz, schnellere Festplattengeschwindigkeit, mehr Speicher, welches Upgrade auch immer den Systemengpass beheben kann)
Eine andere Möglichkeit besteht darin, den Slicing-Bereich zu erweitern. Ursprünglich haben wir nur 4096-Slices für Datenbanken entwickelt. Im Vergleich zu der von uns entworfenen 16-Bit-Slice-ID ist immer noch viel Platz vorhanden, da 16 Bits 65536 Zahlen darstellen können. Wenn wir zu einem bestimmten Zeitpunkt acht weitere Maschinen zum Ausführen von acht MySQL-Datenbankinstanzen bereitstellen und Slicing-Datenbanken von 4096 bis 8192 bereitstellen, werden neue Daten nur in den Slicing-Datenbanken in diesem Bereich gespeichert. Es gibt 16 parallele Computerdatenbanken, und die Servicefunktionen werden sich zwangsläufig verbessern.
Die letzte Möglichkeit besteht darin, den Slicing-Datenbankhost auf einen neuen Slicing-Host (lokale Slicing-Erweiterung) zu migrieren, um die Funktionen zu verbessern. Beispielsweise möchten wir den Slicing-Host MySQL001A im vorherigen Beispiel (auf dem sich die Slicing-Datenbanken befinden, die von 0 bis 511 nummeriert sind) erweitern und auf 2 Slicing-Hosts verteilen. Wie von uns entworfen, erstellen wir ein neues gegenseitiges Master-Master-Backup-Hostpaar als neue Slice-Hosts (mit den Namen MySQL009A und B) und kopieren die Daten vollständig von MySQL001A.
Nachdem die Datenkopie abgeschlossen ist, ändern wir die Slicing-Konfiguration von 0 auf 255, und MySQL009A ist nur für die Slicing-Datenbank verantwortlich von 256 bis 511. Jetzt ist jeder der beiden Hosts nur noch für die Hälfte der Aufgaben verantwortlich, für die der vorherige Host verantwortlich war, und die Servicefähigkeit wurde verbessert.
Einige Funktionsbeschreibungen
Für die vom alten System generierten Geschäftsobjektdaten müssen die Geschäftsobjekte gemäß generiert werden Design Bei UUIDs im neuen System sollten Sie sich darüber im Klaren sein, dass es Ihnen überlassen ist, wohin sie gehen (welche Kacheldatenbank). (Anmerkung: Sie können die Verteilung alter Daten in der Slice-Datenbank planen.) Beim Einfügen in die Slice-Datenbank gibt die Datenbank jedoch nur beim Einfügen eines Datensatzes die lokale ID des eingefügten Objekts zurück. Damit ist die UUID von Das Objekt kann konstruiert werden.
(Anmerkung: Bei der Migration müssen Sie die Einrichtung von Beziehungen zwischen Geschäftsobjekten über UUID berücksichtigen)
Für Datenbanktabellen, die bereits eine große Datenmenge enthalten, habe ich die Änderungstabelle verwendet Strukturbefehl (ALTERs) – wie das Hinzufügen eines Feldes – Sie wissen, dass das ein sehr langer und schmerzhafter Prozess ist. Unser Design besteht darin, niemals Befehle auf ALTERs-Ebene unter MySQL zu verwenden (wenn Daten bereits verfügbar sind). Auf unserem Geschäftssystem Pinterest war die letzte von uns verwendete ALTER-Anweisung etwa drei Jahre her. Wenn Sie für Objekte in der Objekttabelle ein Objektattributfeld hinzufügen müssen, fügen Sie es dem JOSON-Blobfeld der Objektdaten hinzu. Sie können einen Standardwert für die Eigenschaften des neuen Objekts festlegen und beim Zugriff auf die Daten des alten Objekts einen neuen Eigenschaftsstandardwert hinzufügen, wenn das alte Objekt keine neuen Eigenschaften hat. Erstellen Sie für relationale Zuordnungstabellen einfach eine neue relationale Zuordnungstabelle entsprechend Ihren Anforderungen. Du weißt das alles! Lassen Sie Ihr System abheben!
Mod-Datenbank-Shard
Der Name des Mod-Shards ist genau wie „Mod Squad“, aber tatsächlich ist er völlig anders.
Auf einige Geschäftsobjekte muss über eine Nicht-ID-Abfrage zugegriffen werden. (Übersetzung: Diese ID bezieht sich auf die 64-Bit-UUID in der vorherigen Designbeschreibung) Zum Beispiel, wenn sich ein Pinner mit seinem bei Facebook registrierten Konto auf unserer Geschäftsplattform registriert und anmeldet. Wir müssen ihre Facebook-ID mit der ID unseres Pinners abgleichen. Für unser System besteht die Facebook-ID nur aus einer Reihe binärer Ziffern. (Anmerkung: Dies impliziert, dass wir die IDs anderer Plattformen nicht wie das Design unserer Systemplattform dekonstruieren können, noch können wir darüber sprechen, wie Slices entworfen werden. Wir speichern sie einfach und entwerfen sie so, dass sie unseren IDs zugeordnet werden.) Daher müssen wir dies tun Um sie zu speichern, müssen sie jeweils auch in der Slice-Datenbank gespeichert werden. Wir nennen dies einen Mod-Shard. Weitere Beispiele sind IP-Adressen, Benutzernamen und Benutzer-E-Mails.
模转数据切片(mod shard)类似前述我们业务系统的数据切片设计。但是,你需要按照其输入的原样进行查询。如何确定其切片位置,需要用到哈希和模数运算。哈希函数将任意字串转换成定长数值,而模数设为系统已有切片数据库的切片数量,取模后,其必然落在某个切片数据库上。结果是其数据将保存在已有切片数据库上。举例:
shard = md5(“1.2.3.4") % 4096
(译注:mod shard 这个词,我网上找遍了,试图找到一个较准确权威的中文翻译!无果,因为 mod 这个词有几种意思,最近的是 module 模块、模组,同时它也是模运算符(%)。我根据原文意思,翻译为 模转 。或可翻译为 模式,但个人感觉意思模糊。不当之处,请指正。另,原作者举的例子是以 IP 地址举例的,哈希使用的是 md5,相比其它,虽老但性能最好)
在这个例子中分片是 1524。 我们维护一个类似于 ID 分片的配置文件:
[{“range”: (0, 511), “master”: “msdb001a”, “slave”: “msdb001b”}, {“range”: (512, 1023), “master”: “msdb002a”, “slave”: “msdb002b”}, {“range”: (1024, 1535), “master”: “msdb003a”, “slave”: “msdb003b”}, …]
因此,为了找到 IP 为 1.2.3.4 的数据,我们将这样做:
conn = MySQLdb.connect(host=”msdb003a”) conn.execute(“SELECT data FROM msdb001a.ip_data WHERE ip='1.2.3.4'”)
你失去了一些分片好的属性,例如空间位置。你必须从一开始就设置分片的密钥(它不会为你制作密钥)。最好使用不变的 id 来表示系统中的对象。这样,当用户更改其用户名时,您就不必更新许多引用。
最后的提醒
这个系统作为 Pinterest 的数据支撑已良好运行了 3.5 年,现在看来还会继续运行下去。设计实现这样的系统是直观、容易的。但是让它运行起来,尤其迁移旧数据却太不易了。若您的业务平台面临着急速增长的痛苦且您想切片自己的数据库。建议您考虑建立一个后端集群服务器(优先建议 pyres)脚本化您迁移旧数据到切片数据库的逻辑,自动化处理。我保证无论您想得多周到,多努力,您一定会丢数据或丢失数据之间的关联。我真的恨死隐藏在复杂数据关系中的那些捣蛋鬼。因此,您需要不断地迁移,修正,再迁移... 你需要极大的耐心和努力。直到您确定您不再需要为了旧数据迁移而往您的切片数据库中再操作数据为止。
这个系统的设计为了数据的分布切片,已尽最大的努力做到最好。它本身不能提供给你数据库事务 ACID 四要素中的 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)哇呕!听起来很坏呀,不用担心。您可能不能利用数据库本身提供的功能很好地保证这些。但是,我提醒您,一切尽在您的掌握中,您只是让它运行起来,满足您的需要就好。设计简单直接就是王道,(译注:也许需要您做许多底层工作,但一切都在您的控制之中)主要是它运行起来超快! 如果您担心 A(原子性)、I(隔离性)和 C(一致性),写信给我,我有成堆的经验让您克服这些问题。
还有最后的问题,如何灾难恢复,啊哈? 我们创建另外的服务去维护着切片数据库,我们保存切片配置在 ZooKeeper 上。当单点主服务器宕掉时,我们有脚本自动地提升主服务器对应的从服务器立即顶上。之后,以最快的速度运行新机器顶上从服务器的缺。直至今日,我们从未使用过类似自动灾难恢复的服务。
推荐教程:《MySQL教程》
Das obige ist der detaillierte Inhalt vonWie MySQL Sharding nutzt, um das Speicherproblem von 50 Milliarden Daten zu lösen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!