Caching durch In-Memory-Wörterbücher. Tun wir alles falsch?

8

Dieser Ansatz ist so ziemlich der akzeptierte Weg, um irgendetwas in unserem Unternehmen zu tun. Ein einfaches Beispiel: Wenn ein Datenelement für einen Kunden von einem Dienst angefordert wird, rufen wir alle Daten für diesen Kunden (relevanter Teil des Dienstes) ab, speichern sie in einem In-Memory-Wörterbuch und stellen sie dann bei folgenden Anforderungen von dort aus bereit (Wir betreiben Singleton-Dienste). Jedes Update geht an die Datenbank und aktualisiert dann das In-Memory-Wörterbuch. Es scheint alles einfach und harmlos zu sein, aber wenn wir kompliziertere Geschäftsregeln implementieren, ist der Cache nicht mehr synchron und wir müssen uns mit schwer zu findenden Fehlern auseinandersetzen. Manchmal verschieben wir das Schreiben in die Datenbank und behalten bis dahin neue Daten im Cache. Es gibt Fälle, in denen wir Millionen von Zeilen im Speicher speichern, weil die Tabelle viele Beziehungen zu anderen Tabellen hat und wir aggregierte Daten schnell anzeigen müssen.

All diese Cache-Handhabung ist ein großer Teil unserer Codebasis, und ich spüre, dass dies nicht der richtige Weg ist, dies zu tun. All dieses Jonglieren fügt dem Code zu viel Rauschen hinzu und macht es schwierig, die tatsächliche Geschäftslogik zu verstehen. Ich glaube jedoch nicht, dass wir Daten in angemessener Zeit bereitstellen können, wenn wir jedes Mal auf die Datenbank zugreifen müssen.

Ich bin unglücklich über die aktuelle Situation, aber ich habe keine bessere Alternative. Meine einzige Lösung wäre die Verwendung des NHibernate 2nd Level Cache, aber ich habe fast keine Erfahrung damit. Ich weiß, dass viele Unternehmen Redis oder MemCached stark nutzen, um Leistung zu erzielen, aber ich habe keine Ahnung, wie ich sie in unser System integrieren würde. Ich weiß auch nicht, ob sie eine bessere Leistung als speicherinterne Datenstrukturen und Abfragen erzielen können.

Gibt es alternative Ansätze, die ich prüfen sollte?

user73983
quelle

Antworten:

9

Erste letzte Frage: Warum Redis / memcached?

Nein, sie sind (normalerweise) nicht schneller als einfache In-Process-Wörterbücher. Der Vorteil ergibt sich, wenn Sie mehrere Arbeitsprozesse oder sogar viele Maschinen auf App-Ebene haben. In diesem Fall teilen sich nicht alle Prozesse einen eigenen kleinen Cache, sondern alle einen einzigen großen (verteilten) Cache. Mit größeren Caches erhalten Sie bessere Trefferquoten.

Wie Sie sehen können, wird die Cache-Schicht ähnlich wie die Datenbank zu einer gemeinsam genutzten Ressource, jedoch (hoffentlich) schneller.

Nun zum großen Teil: Wie vermeide ich das Durcheinander?

Es scheint, dass Ihr Problem darin besteht, den Cache konsistent zu halten und ihn gleichzeitig von der Datenbank zu entkoppeln. Ich sehe dort drei Schmerzpunkte:

  1. Cache-Ungültigmachung. Das ist nur schwer. Manchmal besteht die einfachste Lösung darin, jedem Datensatz eine Generierungs-ID hinzuzufügen und diese als Teil des Cache-Schlüssels zu verwenden. Wenn die Daten aktualisiert werden, erhalten Sie eine ID der neuen Generation, und die nächste Cache-Abfrage wird nicht ausgeführt. Gehen Sie zur Datenbank und aktualisieren Sie den Cache. Natürlich muss der (jetzt nicht verwendete) Eintrag eine vernünftige Ablaufzeit haben, damit er schließlich aus dem Cache gelöscht wird.

  2. Schreib zurück. Sie sagen, Sie arbeiten am Cache und aktualisieren die Datenbank später. Das ist gefährlich; Die meisten Architekturen vermeiden diese Idee. Ein Schritt in die richtige Richtung wäre, jeden neuen oder geänderten Eintrag im Cache als "schmutzig" zu markieren, damit er durch einen entkoppelten Prozess in die Datenbank geleert werden kann. Eine bessere Idee könnte darin bestehen, eine Nachrichtenwarteschlange zu erweitern, sobald sie geändert wird, wodurch das Schreiben in die Datenbank effektiv "inline, aber asynchron" erfolgt. Letztendlich sollten Sie sich darüber im Klaren sein, dass dies keine gültige Verwendung für einen Cache ist. Dies ist ein "Staging-Bereich", der mit einer anderen Architektur als eine Cache-Schicht behandelt werden sollte.

  3. Interprozesssynchronisation: Da Ihr In-Process-Cache für jeden Prozess privat ist, werden Änderungen dort nicht an andere Prozesse weitergegeben, bis sie in die Datenbank übertragen werden. Dies kann unter Ihrem App-Design korrekt sein (Art der Transaktionsisolation eines armen Mannes), kann jedoch zu unbeabsichtigten Ergebnissen führen. Eine viel besser verwaltbare Architektur ist eine Cache-Schicht, die nur eine schnellere API für die Datenbank darstellt, dieselben gemeinsamen Eigenschaften wie die Datenbank aufweist und genauso "autorisierend" ist wie diese. Dafür benötigen Sie Out-of-Process-Caches wie Memcached oder Redis.

Javier
quelle
8
In der Informatik gibt es nur zwei schwierige Dinge: Cache-Ungültigmachung und Benennung.
Michael Borgwardt
12
In der Informatik gibt es nur zwei schwierige Dinge: Cache-Ungültigmachung, Benennung und Fehler nacheinander.
Matthew King
2
@MatthewKing In der Informatik gibt es nur drei schwierige Dinge: Fehler durch zwei Fehler.
Jimmy Hoffa
@ MatthewKing, ich liebe den Humor. :)
Anthony Gatlin