Ich schreibe eine MMORPG-Server-Engine mit einigen eher esoterischen Elementen um (theoretisch gut, aber in der Praxis selten verwendet) und habe ein wenig Zweifel. Einige Elemente davon sind „solide“ - aber der Sinn von „Noch ein MMO-Server“ besteht darin, einige dieser Konzepte in Code auf Produktionsebene zu testen.
Ich hoffe jedoch, dass hier jemand praktische Erfahrungen mit PostgreSQL-Partitionierungsmodellen hat und in der Lage ist, diesem Projekt jedoch etwas Fachwissen zu verleihen.
Überblick
Es gibt eine TL; DR-Version unten.
- Die Kernstruktur des Betriebssystems ist ein Cluster von Linux-Instanzen, möglicherweise auf einem Cloud-System. Stubs zur Überwachung der Gesamtsystemleistung und zum Hochfahren neuer Instanzen mithilfe einer externen API sind für die spätere Implementierung geplant. Zu Testzwecken versuchen wir wahrscheinlich, Amazon zu verwenden, aber wir lassen die Tür offen, um daraus ein steckbares Modul für z. B. RackSpace und andere Anbieter zu machen oder sogar einige ungezogene Dinge mit der Neukonfiguration eines Pools von physischen, "heißen Standby" -Modulen zu tun. Server auf einem privaten Rack.
- Die Hauptlogik des MMO basiert auf einem „reinen“ Entity-Component-System-Modell. Entitäten sind lange Ganzzahl-IDs. Komponenten sind relationale Datensätze oder Sätze (Listen) von Datensätzen (JOINable-Referenzen); Systeme sind Funktionen. Systeme stellen Metadaten darüber bereit, auf welche Komponenten sie zugreifen müssen, und die Methode ← → Datenlokalität im gesamten Cluster basiert auf einem Planer, der diese Zugriffe vorwegnimmt und versucht, dieselben Systeme auf denselben Hosts auszuführen. Das heißt: Systeme, die auf dieselbe Komponente derselben Entität zugreifen, befinden sich in der Regel auf demselben Computer.
- Der relationale Datenspeicher wird von einer PostgreSQL-Datenbank unterstützt. Dies wurde ausgewählt, weil es sich um freie Software handelt und (für unsere Zwecke) in einigen Punkten „bessere“ SQL / ACID-Dienste als MySQL bietet. Nehmen wir an, dies ist unveränderlich (es wurde bereits viel darüber gestritten).
- Die Server-Host-Instanzen stellen eine einzelne Spielwelt dar, die einen zusammenhängenden koordinierten Raum einnimmt: In der Spielwelt gibt es per se keine Diskontinuitäten (z. B. Ebenen; Sternensysteme; Weltinstanzen). Daher können die Entitäten basierend auf dynamischen Regionen auf Hosts aufgeteilt werden, deren „Größe“ sich zur Laufzeit ändern kann. Es ist selbstverständlich, dass solche Regionen in SQL mehr oder weniger effizient partitioniert werden können: z. B. können wir eine „Partitionsebene bei Y = -10“ definieren und die Entitäten basierend auf ihren Y-Koordinaten oder ähnlichem aufteilen. Die genauen Mechanismen, die hierfür verwendet werden sollen, werden wahrscheinlich unter simulierten Belastungen experimentiert. Das Ändern dieser Partitionierungsregel ist ein relativ seltenes Ereignis: Möglicherweise überwacht ein 5- oder 10-Minuten-Timer die Serverlast und versucht, eine optimalere Aufteilung zu ermitteln.
- Es ist zulässig, dass ein Serverhost ein Datenbankserver (Cluster), ein Spielelogikserver oder (für Clustergröße = 1 System) beides ist. Es ist eine Selbstverständlichkeit, dass wir DB-Instanzen unter der Kontrolle des Hauptplanersystems hochfahren und auf beliebig komplexe Weise konfigurieren können, um den Beitritt zum Cluster zu verwalten und so weiter. Möglicherweise kann dies das Erstellen zugrunde liegender RAM-Discs oder Partitionierungsregeln usw. umfassen.
- Daher kann der Datensatz für einen bestimmten Host eine Kombination aus einem bestimmten Satz von Tabellen (Komponenten) sein, ist jedoch nur an einem bestimmten Pool von Entitäten interessiert, die nach einem separaten Kriterium ausgewählt wurden. Aus Gründen der Effizienz können wir entweder eine Spalte in der
entities
Tabelle speichern, die angibt, zu welchem „Serverlokalitätspool“ sie gehört, oder eine separate Tabelle, die diese Zuordnung bereitstellt, oder etwas in diesem Sinne. - Es gibt eine angeborene Annahme, dass die einzelnen Datensätze ausreichend Daten enthalten, um die Integrität aufrechtzuerhalten, solange das Back-End-Datenbankjournal nicht zerstört wird. IE: Solange eine Transaktion stattfindet
COMMIT
, ist es uns angesichts eines Absturzes nicht sonderlich wichtig, ob wir das Vorher- oder Nachher-Image einer bestimmten Transaktion erhalten. Ich glaube, ich habe dieses Konzept verbal entstellt: Anders ausgedrückt, wir sind zum Zwecke der Crash-Wiederherstellung nicht sonderlich besorgt darüber, ob ganze Transaktionen verloren gehen, solange wir ganze Transaktionen verlieren. Die Möglichkeit, einen Cluster-Host zu verlieren (z. B. Maschine fängt Feuer), wird auf Planerebene behandelt, und der Verantwortungsbereich dieses Hosts kann relativ schnell neu zugewiesen werden, wenn wir einen Herzschlagverlust feststellen. - Fast jeder Host schreibt ungefähr halb so viel wie er liest, was viel höher ist, als viele Datenbanksysteme erwarten.
Übersicht (TL; DR)
Wir haben eine Menge Linux-Boxen mit PostgreSQL. Wir haben einige Funktionen, die eine Teilmenge von Daten aufnehmen, die mithilfe eines SELECT
oder VIEW
auf diesen Hosts ausgeführt werden können, und Änderungen fast so oft ausschreiben, wie sie gelesen werden.
Reines theoretisches Modell
Hier wird es schuppig:
Die Komponenten werden direkt relationalen Tabellen und Zeilen zugeordnet. Angenommen, es gibt eine Positionskomponente. Die Entitäts-ID wäre ein Primärschlüssel in einer Tabelle und zur Konsistenzprüfung auch ein Fremdschlüssel für eine entities
Tabelle, die nur ein PK- SERIAL8
Feld enthält. Die Positionstabelle enthält dann beispielsweise x NUMERIC, y NUMERIC, z NUMERIC
Spalten.
Stellen Sie sich dann ein System für die Schwerkraft vor, das Positions-, Massen- und Trägheitskomponenten benötigt, und ein anderes System für Kollisionen, das diese Komponenten sowie eine PhysicalVolume-Komponente verwendet.
In einer perfekten Welt (dh Leistung spielt keine Rolle) wissen wir, welche Entitäten von einem System unter Verwendung eines SQL SELECT
mit einigen Kriterien angesprochen werden . Möglicherweise verfügt das PhysicalVolume über einen Begrenzungsrahmen, der das schnelle Ausmerzen von Entitäten ermöglicht, die entweder kein physisches Volume belegen oder sich eindeutig nicht in der Nähe einer anderen Entität befinden, mit der sie möglicherweise kollidieren (einige, die nicht allzu ausgefallen JOIN
für Position und PhysicalVolume sind) ). Wir haben also eine Systemdefinition, die auflistet: Welche Komponenten, von denen Daten benötigt werden, und eine SELECT
Abfrage, um diese als unveränderliche Datensatzstruktur zu einem bestimmten Zeitpunkt zu erhalten. Diese Datensätze werden einzeln in die Systeme eingespeistrun
Funktion, und es führt alle erforderlichen Änderungen. Wenn das System in eine Komponente schreibt, die es nicht liest, wird dies im Voraus deklariert, um die Datenlokalität zu erhalten.
Die Realität setzt ein
Das Problem ist natürlich, dass SQLs SELECT
von Remote-Discs nicht die Art von Dingen sind, die man in jedem „Frame“ einer Hauptsimulationsschleife ausführen möchte. Einige dieser Systeme laufen möglicherweise über 10 Hz.
Jetzt weiß ich, "keine Optimierung vor seiner Zeit", aber dies scheint ein "zum Scheitern verurteilt" -Modell zu sein, es sei denn, es gibt eine Möglichkeit, dieses theoretische Modell in Echtzeit zu verwenden.
Als weitere, notwendige Option für größere Installationen möchte das Planersystem möglicherweise auch "Pools" von Entitäten, höchstwahrscheinlich solche, die sich "physisch" in einer bestimmten Region der Spielwelt befinden, auf einzelne Hosts migrieren, um dies auszugleichen Laden und halten Sie die "Cache-Fehler", die eine allgemeine Back-End-Datenbank treffen müssen, relativ niedrig.
Die Frage (endlich!)
Gegeben
- ein Datenpool für
- eine Reihe von VIEWs und aktualisierbaren VIEWs
- … Die Eingangsquellen und Ausgangssenken bereitstellen für:
- eine bestimmte Reihe von Systemen
- (dargestellt als Liste von Tabellen)
- … Wie auf einen aufgezählten Pool von Entitäten angewendet,
- (dargestellt als nicht zusammenhängende Auswahl von
SERIAL8
REFERENCE
s)
… Gibt es einen vernünftigen Weg zu schaffen
- eine In-RAM-PostgreSQL-Instanz (oder ähnlich)
- … Die die Hauptautorität für die angegebenen Daten beibehält…
- ohne die allgemeine Transaktionsintegrität zu brechen?
Wenn dies ein unvernünftiger Entwurf ist, besteht mein Fallback-Konzept darin, im Wesentlichen den gleichen Effekt zu simulieren, indem ich versuche, die Ansichten in temporäre In-RAM-Tabellen oder ähnliches vorzuladen, obwohl ich nicht viel Zeit damit verbracht habe, darüber nachzudenken, wie schlecht das funktionieren könnte.
Jede plausible Alternative, die dem zugrunde liegenden theoretischen Modell dienen könnte, wird geschätzt.