Szenarien
Mutex wird hauptsächlich in Situationen verwendet, in denen es eine große Anzahl gleichzeitiger Zugriffe und einen Cache-Ablauf gibt, z. B.
Top 10 auf der Homepage, die für n aus der Datenbank in den Memcache-Cache geladen werden Minuten
Inhalt von Prominenten im Weibo-Cache. Sobald er nicht vorhanden ist, kann eine große Anzahl von Anfragen die Datenbank nicht erreichen und laden
Die durch mehrere E/A-Vorgänge generierten Daten müssen im Cache gespeichert werden. B. das mehrmalige Abfragen der Datenbank
Probleme
In Situationen mit großer Parallelität, wenn der Cache ausfällt Wenn eine große Anzahl gleichzeitiger Benutzer nicht gleichzeitig auf den Cache zugreifen kann, greifen sie auf die Datenbank zu und setzen den Cache zurück Gleichzeitig kann es zu Überlastungsrisiken für das System kommen. Wir haben ähnliche Fehler in unseren Online-Systemen erlebt.
Lösung
Methode 1
Fügen Sie einen Mutex-Schlüssel hinzu, bevor Sie die Datenbank laden. Wenn das Hinzufügen fehlschlägt, versuchen Sie, die ursprünglichen Cache-Daten erneut zu lesen. Um einen Deadlock zu verhindern, muss der Mutex-Schlüssel auch eine Ablaufzeit festlegen. Der Pseudocode lautet wie folgt
(Hinweis: Der Pseudocode unten dient nur zum Verständnis der Idee, es können Fehler vorhanden sein, bitte weisen Sie darauf hin.)
Java-Code
if (memcache.get(key) == null) { // 3 min timeout to avoid mutex holder crash if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } }
Methode 2
Legen Sie einen Timeout-Wert (timeout1) fest. Timeout1 ist kleiner als das tatsächliche Memcache-Timeout (timeout2). Wenn Timeout1 aus dem Cache gelesen wird und festgestellt wird, dass es abgelaufen ist, wird Timeout1 sofort verlängert und auf den Cache zurückgesetzt. Laden Sie dann die Daten aus der Datenbank und legen Sie sie im Cache ab. Der Pseudocode lautet wie folgt:
Java-Code
v = memcache.get(key); if (v == null) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } } else { if (v.timeout <= now()) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { // extend the timeout for other threads v.timeout += 3 * 60 * 1000; memcache.set(key, v, KEY_TIMEOUT * 2); // load the latest value from db v = db.get(key); v.timeout = KEY_TIMEOUT; memcache.set(key, value, KEY_TIMEOUT * 2); memcache.delete(key_mutex); } else { sleep(50); retry(); } } }
Im Vergleich zu Option eins
Vorteile: Vermeiden Sie den Fehler, einen großen Code zu erhalten Anzahl der Anfragen, wenn der Cache fehlschlägt, Mutex und Ruhezustand
Nachteile: Die Komplexität des Codes nimmt zu, sodass in allgemeinen Situationen Lösung 1 ausreicht.
Option 2 wird auch ausführlich in den Memcached-FAQs „Wie verhindert man überlastende Aktualisierungen und Stampeding-Anfragen“ vorgestellt, und Brad stellte außerdem die Methode vor, Gearman, ein weiteres seiner Lieblingstools, zu verwenden, um eine Einzelinstanz-Cache-Einstellung zu implementieren , siehe Cache miss stampedes , aber die Lösung mit Gearman fühlt sich etwas schwierig an.