# Mongo-DB Documents größer als 16MB best practise



## Meeresgott (12. Sep 2020)

Hallo zusammen, 

ich habe aktuell ein Datenmodell das in etwas diesem entspricht: 


```
{
   uuid: "lsdkh2408sldkj",
   version: 4,
   raws[
      {
         version: 1,
         raw: "some big big text"
      },
      {
         version: 2,
         raw: "some changed big text"
      },
      {
         version: 3,
         raw: "some corrected big text"
      },
      {
         version: 4,
         raw: "some big big text and so on"
      }
   ]
}
```

Wenn ich nun ein "Raw" immer weiter hinzufüge kommt es durchaus vor, dass ich die Dokument Größe von MongoDB überschreite. 

Was wäre der richtige Ansatz das Problem zu lösen?

Meine "raw" Dateien sind wirklich nur viel Text. 
Ich könnte diese z.B. zip komprimieren und ein binär attriubt aus "raw" machen und drauf hoffen, dass es niemals so viele Versionen gibt, dass sie selbst gezippt zusammen mehr  als 16MB erreichen.

Mann könnte auch mit mapped Document arbeiten über die @DBRef Annotation und ein Limit für das Raw - Attribut setzten, sodass man die 16MB als eigenes Dokument nicht überschreiten kann oder man limitiert die Anzahl der möglichen Versionen und nimmt keine neuen Versionen mehr an. 
Aber das klingt in meinen Ohren so als würde man da, dann eine relationale Datenbank aufbauen wollen.

Ich selber würde, wenn möglich ein Verfahren wählen, indem alle Versionen gespeichert werden um Datenverlust zu vermeiden. 
Komme aber auf keine Lösung die den beiden oben genannten nicht ähnelt.

Wie seht ihr das? Wie kann ich das Problem am besten beheben? Doch mit MappedDocuments arbeiten - oder sie lieber embedded lassen? Zippen?

LG


----------



## httpdigest (12. Sep 2020)

Ersteinmal würde ich fragen, was denn typische Zugriffsmuster für diese Collection sind. Projizierst du bei jeder Query immer auf den Content/raw aller Elemente im Array, oder benötigst du immer nur jeweils die letzte Version oder eine konkrete selektierte Version?
Falls du nicht immer auf alle Elemente projizierst, dann würde ich schonmal ein anderes Datenmodell vorschlagen, nämlich, nur ein MongoDB-Document pro Version zu haben und über ein Document-Property die Identität "desselben" fachlichen Objektes (also quasi deine UUID) abbilden. Also nicht alle Versionen imselben MongoDB-Document speichern (wüsste nicht, warum), sondern pro Version ein MongoDB Document.
Das hätte dann auch den Vorteil, dass deine Versionen immutable sein können, du also kein Mongo Document Update ausführen musst, wenn du eine neue Version eines fachlichen Dokumentes anlegst. Du kannst dann einfach ein neues MongoDB-Document zu dieser Version anlegen. Und wenn du auf die neueste Version zu einer UUID zugreifen willst, kannst du einfach eine max-Aggregation verwenden. Mit einem entsprechenden Index auf ein version-Attribut ist das extremst schnell.
Zusätzlich bei natürlichsprachlichem Text bietet jegliche LZ-basierte Kompression sehr hohe Kompressionsraten.


----------



## Meeresgott (12. Sep 2020)

Wie du bereits richtig vermutet hast, ist nur die neuste Version für die meisten Queries wichtig. 

Habe ich leider nicht direkt im Post erwähnt aber eigentlich enthält das Root-Document noch Informationen wie Autor, eine externe UUID ( ungleich der internen ) eine Document spezifische URL und noch weitere Attribute. Allerdings haben diese im Verhältnis zu dem Feld Raw vielleicht 0.1% Auswirkung auf die Größe eines Dokuments. 

Würdest du dich trotzdem noch so entscheiden? 

Ich habe deinen Vorschlag mal soweit ich es Verstanden hatte umgesetzt:

```
{
    entryId: "entryIdExample"
    version: 1,
    raw: "blub blub...",
    author: "qwertzuiopü",
},
{
    entryId: "entryIdExample2"
    version: 1,
    raw: "blub 2. blub...",
    author: "httpdigest",
},
{
    entryId: "entryIdExample"
    version: 2,
    raw: "blub blub blub...",
    author: "qwertzuiopü",
}
```


----------



## httpdigest (12. Sep 2020)

Meeresgott hat gesagt.:


> Würdest du dich trotzdem noch so entscheiden?


Ja. Auch, wenn eine MongoDB kein relationales Modell hat, gelten dennoch einige sinnvolle Prinzipien, wie Normalisierung, zumindest die 1. Normalform (also keine Properties mit mehr als einem Wert zu haben). MongoDB bietet ja auch sehr umfangreiche Query-Mechanismen, bis hin zu sehr komplexen Aggregation Pipelines. Hier hilft es immer, wenn deine Daten möglichst gut strukturiert/normalisiert sind.

Du kannst aber auch noch weitergehen. Wenn die anderen Metadaten selbst nicht versioniert sein sollen (sondern nur der Content), dann kannst du auch die nicht-versionierten Metadaten in einer separaten Collection (mit entryId als PK) halten und die einzelnen Versionen in einer anderen Collection (entryId als PK + raw + version).


----------



## Dukel (13. Sep 2020)

Geht evtl. etwas mit Sharding?


----------



## httpdigest (13. Sep 2020)

Naja, Sharding ist Lastverteilung durch horizontale Skalierung, wenn der Workload die Kapazität einer Maschine bzw. einer MongoDB Instanz übersteigt. Typischerweise durch zu hohe IOPs (I/O Operations per Second), so dass der Persistenzspeicher nicht mehr hinterherkommt oder durch CPU-Auslastung (durch Berechnungen in Queries). Bei Sharding werden die Dokumente deiner Collections einfach auf mehrere MongoDB Instanzen/Server aufgeteilt. Ich habe das Problem aber nicht als Lastproblem verstanden, sondern als Überschreitung der Limitierung der Maximalgrösse eines Documents in einer Collection. Ein Lastproblem wäre es dann noch, wenn er z.B. 20.000 Queries solcher Dokumente pro Sekunde absetzt und dadurch der Server an seine Grenzen kommt. Dann wäre Sharding sinnvoll.


----------



## Dukel (13. Sep 2020)

Wenn durch Sharding das 16 MB Limit ausgehoben wäre (das habe ich leider nicht herausfinden können), dann hättest du eine Lösung, egal ob das eigentlich für Lastverteiltung gedacht ist.

Edit: Noch einen Link gefunden, mit dem selben Problem:








						How to Store Documents Larger Than 16 MB in MongoDB
					

A simple approach without using GridFS




					medium.com
				




Edit2:


			https://docs.mongodb.com/manual/core/sharding-data-partitioning/
		

Das meinte ich mit Sharding als Lösung für mehr Daten.


----------



## httpdigest (13. Sep 2020)

Dukel hat gesagt.:


> Wenn durch Sharding das 16 MB Limit ausgehoben wäre (das habe ich leider nicht herausfinden können), dann hättest du eine Lösung, egal ob das eigentlich für Lastverteiltung gedacht ist.


So funktioniert Sharding aber nunmal nicht. Bitte informiere dich doch erstmal.
Sharding splittet nicht einzelne Dokumente auf, sondern verteilt ganze Dokumente auf unterschiedliche Knoten.

Disclaimer: Ich habe die letzten zwei Jahre damit verbracht, bei dem grössten deutschen Online-Händler die Produktdaten in der MongoDB zu optimieren.


----------



## Meeresgott (16. Sep 2020)

Entschuldigt bitte meine späte Antwort.

Danke @Dukel für den Artikel. Mir geht es aber auch um die best practise. Das Limit von MongoDB ist soweit ich es in Erfahrung bringen konnte aus einem  guten Grund da (RAM des Servers nicht voll auszulasten beim hantieren mit den Docs. Von dieser Quelle hier). Deshalb möchte ich lieber mein Konzept ändern anstelle die Regeln der MongoDB verbiegen.





httpdigest hat gesagt.:


> Du kannst aber auch noch weitergehen. Wenn die anderen Metadaten selbst nicht versioniert sein sollen (sondern nur der Content), dann kannst du auch die nicht-versionierten Metadaten in einer separaten Collection (mit entryId als PK) halten und die einzelnen Versionen in einer anderen Collection (entryId als PK + raw + version).



Das war auch mein nächster Gedanke allerdings habe ich es mir schwer getan es zu "verstehen".  Ich weiß SQL ist nur die Language aber oft werden bei sql und noSQL Datenbank verglichen was besser ist: relationale oder document based Datenbanken. Daher kommt glaube ich die Befürchtung von mir etwas falsch zu machen, wenn man dann doch anfängt Relationen in der MongoDB aufzubauen. Aber es scheint weniger eine Sünde zu sein wie ich dachte.

Auch hier wieder ein Beispiel zu den Collections damit wir (ich im speziellen) nicht aus dem Auge verlieren worum es genau geht:

```
Raw
{
    entryId: "entryIdExample"
    version: 1,
    raw: "blub blub...",
    details: "DetailsIdExample2",
},
{
    entryId: "entryIdExample2"
    version: 1,
    raw: "blub 2. blub...",
    details: "detailsIdExample1",
},
{
    entryId: "entryIdExample"
    version: 2,
    raw: "blub blub blub...",
    details: "DetailsIdExample2",
}

Details
{
    entryId: "detailsIdExample1"
    author: "httpdigest",
    ...
},
{
    entryId: "DetailsIdExample2"
    author: "qwertzuiopü",
    ...
}
```

Ich habe mal eine Auswertung über die bereits vorhanden Daten gemacht (Auf ganze Prozent gerundet:

Anzahl VersionenVerteilung19%2 - 1054%11 - 2023%21 - 308%31 - 403%41 - 501%51 >
2%

Kannst du anhand der Verteilung sagen ob es Sinn macht den Weg über die Relationen zu gehen oder nicht? Oder würdest du generell über die Relationen gehen, nach dem Motto: niemals doppelte Daten?


----------



## temi (17. Sep 2020)

Nur mal so als zusätzliche Idee: Ich weiß das z.B. PmWiki nur einmal den vollständigen Seitentext speichert und bei Änderungen von Version zu Version nur noch die Unterschiede zum Vorgänger.

Es kommt jetzt natürlich darauf an, wie umfangreich die Unterschiede in den einzelnen Textversionen sind, ob das sinnvoll ist oder nicht.


----------



## temi (20. Sep 2020)

Als Ergänzung zum vorherigen Beitrag: https://hackernoon.com/delta-compre...d-delta-file-formats-practical-guide-7v1p3uhz


----------

