Optimale Speicherung der Datenstruktur für schnelle Suche und Persistenz

8

Szenario

Ich habe die folgenden Methoden:

%Vor%

Zunächst denke ich über das Speichern auf dem Formular nach:

%Vor%

und

%Vor%

AddItemSecurity basiert darauf, wie ich Daten von einer Drittanbieter-API erhalte, GetValidItemIds ist, wie ich sie zur Laufzeit verwenden möchte.

Es gibt potenziell 2000 Benutzer und 10 Millionen Elemente. Artikel-IDs sind in der Form: 2007123456, 2010001234 (10 Ziffern, wobei die ersten vier das Jahr darstellen).

AddItemSecurity muss nicht super schnell ausgeführt werden, aber GetValidIds muss untersecond sein. Wenn es eine Aktualisierung für eine vorhandene itemId gibt, muss ich diese itemId auch für Benutzer entfernen, die nicht mehr in der Liste sind.

Ich versuche darüber nachzudenken, wie ich das optimal speichern soll. Am liebsten auf Platte (mit Caching), aber ich möchte den Code pflegbar und sauber halten.

Wenn die Element-IDs bei 0 begonnen hatten, dachte ich darüber nach, ein Byte-Array mit der Länge von MaxItemId / 8 für jeden Benutzer zu erstellen und ein True / False-Bit festzulegen, wenn das Element vorhanden war oder nicht. Das würde die Array-Länge auf etwas über 1 MB pro Benutzer beschränken und schnelle Suchvorgänge sowie eine einfache Möglichkeit zum Aktualisieren der Liste pro Benutzer ermöglichen. Indem Sie dies als Memory Mapped Files mit dem .Net 4 Framework Ich denke, ich würde anständig Caching bekommen als gut (wenn die Maschine genug RAM hat), ohne Caching-Logik selbst zu implementieren. Das Analysieren der ID, das Entfernen des Jahres und das Speichern eines Arrays pro Jahr könnte eine Lösung sein.

Die ItemId - & gt; UserId [] Liste kann direkt auf Festplatte serialisiert und mit einer normalen FileStream gelesen / geschrieben werden, um die Liste persistent zu machen und bei Änderungen zu unterscheiden.

Jedes Mal, wenn ein neuer Benutzer hinzugefügt wird, müssen auch alle Listen aktualisiert werden, dies kann jedoch jede Nacht erfolgen.

Frage

Sollte ich diesen Ansatz noch einmal ausprobieren, oder gibt es noch andere Wege, die es zu erkunden gilt? Ich denke, der SQL-Server wird nicht schnell genug funktionieren und es würde einen Overhead geben (zumindest wenn es auf einem anderen Server gehostet wird), aber meine Annahmen könnten falsch sein. Jeder Gedanke oder Einblick in die Sache wird geschätzt. Und ich möchte versuchen, es zu lösen, ohne zu viel Hardware hinzuzufügen:)

[Update 2010-03-31]

Ich habe jetzt mit SQL Server 2008 unter den folgenden Bedingungen getestet.

  • Tabelle mit zwei Spalten (Benutzer-ID, Artikel-ID) beide sind Int
  • Clustered-Index für die zwei Spalten
  • Hinzugefügt ~ 800.000 Elemente für 180 Benutzer - Insgesamt 144 Millionen Zeilen
  • Zugeordneter 4 GB RAM für SQL Server
  • Dual Core 2.66 GHz Laptop
  • SSD-Festplatte
  • Verwenden Sie einen SqlDataReader, um alle Element-IDs in eine Liste
  • einzulesen
  • Schleife über alle Benutzer

Wenn ich einen Thread starte, beträgt er durchschnittlich 0,2 Sekunden. Wenn ich einen zweiten Thread hinzufüge, geht es bis zu 0,4 Sekunden, was immer noch ok ist. Von da an sinken die Ergebnisse. Hinzufügen eines dritten Threads bringt viele der Abfragen bis zu 2 Sekunden. Ein vierter Thread, bis zu 4 Sekunden, ein Fünftel Spikes einige der Abfragen bis zu 50 Sekunden.

Die CPU dichtet währenddessen, sogar an einem Thread. Meine Test-App dauert einige aufgrund der schnellen Schleife und SQL den Rest.

Was mich zu der Schlussfolgerung führt, dass es nicht sehr gut skalieren wird. Zumindest nicht auf meiner getesteten Hardware. Gibt es Möglichkeiten, die Datenbank zu optimieren, sagen wir ein Array von Int pro Benutzer anstelle von einem Datensatz pro Element zu speichern. Dies macht es jedoch schwieriger, Objekte zu entfernen.

[Update 2010-03-31 # 2]

Ich habe einen schnellen Test mit den gleichen Daten durchgeführt, indem ich sie als Bits in Speicherabbilddateien eingefügt habe. Es funktioniert viel besser. Sechs Threads ergeben Zugriffszeiten zwischen 0,02s und 0,06s. Rein speichergebunden. Die zugeordneten Dateien wurden von einem Prozess zugeordnet und von sechs anderen gleichzeitig abgerufen. Und da die sql-Basis 4 GB benötigte, benötigten die Dateien auf der Festplatte 23 MB.

    
Mikael Svenson 30.03.2010, 14:13
quelle

3 Antworten

3

Nach vielen Tests habe ich Memory Mapped Files verwendet und sie mit dem Sparse Bit (NTFS) markiert, indem ich den Code von NTFS Sparse Dateien mit C # .

Wikipedia hat eine Erklärung dafür, was eine Sparse-Datei ist.

Die Vorteile der Verwendung einer Sparse-Datei bestehen darin, dass ich mich nicht darum kümmern muss, in welchem ​​Bereich meine IDs liegen. Wenn ich nur IDs zwischen 2006000000 und 2010999999 schreibe, weist die Datei nur 625.000 Bytes vom Offset 250.750.000 in der Datei zu . Der gesamte Speicherplatz bis zu diesem Offset ist im Dateisystem nicht zugewiesen. Jede ID wird als gesetztes Bit in der Datei gespeichert. Art behandelt als ein Bit-Array. Und wenn sich die ID-Sequenz plötzlich ändert, wird sie in einem anderen Teil der Datei zugeordnet.

Um herauszufinden, welche IDs gesetzt sind, kann ich einen OS-Aufruf durchführen, um die zugewiesenen Teile der Sparse-Datei zu erhalten, und dann überprüfe ich jedes Bit in diesen Sequenzen. Auch das Überprüfen, ob eine bestimmte ID gesetzt ist, ist sehr schnell. Wenn es außerhalb der zugewiesenen Blöcke liegt, dann ist es nicht da, wenn es hinein fällt, ist es nur ein gelesenes Byte und eine Bitmaskenprüfung, um zu sehen, ob das korrekte Bit gesetzt ist.

Also für das spezielle Szenario, in dem Sie viele IDs haben, die Sie mit so viel Geschwindigkeit wie möglich überprüfen möchten, ist dies der optimale Weg, den ich bisher gefunden habe.

Und der gute Teil ist, dass die Memory-Mapped-Dateien auch mit Java geteilt werden können (was sich als etwas herausgestellt hat, was benötigt wird). Java unterstützt auch Speicherabbilddateien unter Windows, und die Implementierung der Lese- / Schreiblogik ist ziemlich trivial.

    
Mikael Svenson 15.06.2010, 06:45
quelle
1

Ich denke wirklich, dass Sie eine gute Datenbank ausprobieren sollten, bevor Sie Ihre Entscheidung treffen. So etwas wird auf lange Sicht eine Herausforderung sein. Ihre Benutzerbasis ist eigentlich ziemlich klein. SQL Server sollte in der Lage sein, das, was Sie benötigen, ohne Probleme zu handhaben.

    
ChaosPandion 30.03.2010 14:16
quelle
0

2000 Benutzer ist nicht so schlecht, aber mit 10 Mil verwandte Artikel sollten Sie wirklich darüber nachdenken, dies in eine Datenbank zu bringen. DBs führen alle erforderlichen Speicher-, Persistenz-, Indizierungs-, Caching-Funktionen usw. durch und sie funktionieren sehr gut.

Sie ermöglichen auch eine bessere Skalierbarkeit in die Zukunft. Wenn Sie plötzlich mit zwei Millionen Benutzern umgehen müssen und Milliarden von Einstellungen mit einer guten Datenbank vorhanden sind, wird Skalierung kein Problem mehr sein.

    
Paul Sasik 30.03.2010 14:28
quelle