Heim  >  Artikel  >  Datenbank  >  Detaillierte Erläuterung von Beispielen des RocksDB-Sperrmechanismus

Detaillierte Erläuterung von Beispielen des RocksDB-Sperrmechanismus

零下一度
零下一度Original
2017-07-03 09:31:442449Durchsuche

Als Open-Source-Speicher-Engine unterstützt RocksDB die ACID-Eigenschaften von Transaktionen. Um I (Isolation) in ACID zu unterstützen, ist die Parallelitätskontrolle unverzichtbar. In diesem Artikel wird hauptsächlich die Implementierung des Sperrmechanismus von RocksDB erläutert Ich hoffe, dass die Leser durch diesen Artikel ein tieferes Verständnis der Prinzipien der RocksDB-Parallelitätskontrolle erlangen können. Der Artikel beginnt hauptsächlich mit den folgenden vier Aspekten. Zuerst werde ich die Grundstruktur der RocksDB-Sperre vorstellen. Anschließend werde ich den Sperrraum-Overhead im Rahmen des Designs der RocksDB-Zeilensperrdatenstruktur vorstellen Abschließend werde ich die Sperre vorstellen, einen wesentlichen Mechanismus zur Erkennung von Deadlocks.

1. Struktur der Zeilensperrdaten
Für die KV-Speicherung ist das Sperrobjekt der Schlüssel und jeder Schlüssel entspricht einer LockInfo-Struktur. Alle Schlüssel werden über die Hash-Tabelle verwaltet. Wenn Sie nach einer Sperre suchen, können Sie diese direkt über die Hash-Tabelle finden, um festzustellen, ob der Schlüssel gesperrt wurde. Wenn jedoch global nur eine Hash-Tabelle vorhanden ist, führt dies zu vielen Konflikten beim Zugriff auf diese Hash-Tabelle, was sich auf die Parallelitätsleistung auswirkt. RocksDB wird zunächst nach Columnfamily aufgeteilt. Die Sperren in jeder Columnfamily werden von einer LockMap verwaltet, und jede LockMap wird in mehrere Shards aufgeteilt. Jeder Shard wird von LockMapStripe verwaltet, und die Hash-Tabelle (std::unordered_map4e5cec435943c531cd6ecf1c6ecfe3e1acquire_snapshot(false), Verzögerung Holen Sie sich einen Snapshot (machen Sie nach dem Sperren einen weiteren Snapshot)
5). Rufen Sie get_for_update erneut auf Zeit, ein erneuter Versuch wird auf jeden Fall gelingen.
6). Führen Sie den Aktualisierungsvorgang aus
7). Springen Sie zu 1 und fahren Sie mit der Ausführung fort, bis der Primärschlüssel die Bedingungen nicht mehr erfüllt.
3.3. Aktualisierung basierend auf dem Sekundärindex
Dieses Szenario ähnelt 3.2, außer dass es einen weiteren Schritt beim Auffinden des Primärschlüssels gibt aus dem Sekundärindex.

1) Erstellen Sie einen Snapshot und scannen Sie den Sekundärindex basierend auf dem Iterator
2). ruft get_row_by_rowid auf. Der Prozess versucht, den Schlüssel zu sperren
3). ). Der Unterschied zwischen RocksDB und InnoDB besteht darin, dass für Updates in diesem Szenario RocksDB immer noch ein Snapshot-Lesevorgang ist, während InnoDB ein aktueller Lesevorgang ist, was zu Verhaltensunterschieden führt. Im Bereichsaktualisierungsszenario unter der RC-Isolationsstufe muss eine Transaktion beispielsweise 1.000 Datensätze aktualisieren. Da sie beim Scannen gesperrt ist, kann beim Scannen des 999. Datensatzes festgestellt werden, dass die Reihenfolge dieses Schlüssels lautet größer als der gescannte Snapshot (dieser Schlüssel wird durch andere Transaktionen aktualisiert). Dieses Mal wird eine erneute Erfassung des Snapshots ausgelöst und dann wird der neueste Schlüsselwert basierend auf diesem Snapshot abgerufen. Bei InnoDB besteht dieses Problem nicht. Wenn der 999. Datensatz aktualisiert wird, kann InnoDB den neuesten Datensatz direkt sehen. In diesem Fall sind die von RocksDB und InnoDB erzielten Ergebnisse dieselben. In einem anderen Fall, vorausgesetzt, dass sich auch ein neu eingefügter Schlüssel im Scanbereich befindet, ist die Sequenz dieses Schlüssels zweifellos größer als der gescannte Snapshot, sodass dieser Schlüssel während des Scanvorgangs herausgefiltert wird und nicht vorhanden ist. Dieser Schlüssel wird nicht gefunden, da er als Konflikterkennung bezeichnet wird. Während des Aktualisierungsvorgangs wurden zwei Datensätze mit den IDs 1 und 900 eingefügt. Der 900. Datensatz konnte schließlich nicht aktualisiert werden, da er unsichtbar war. Da InnoDB gerade liest, kann der neu eingefügte Datensatz mit der ID 900 angezeigt und aktualisiert werden. Dies ist also der Unterschied zu InnoDB.
Zusätzlich zu dem Unterschied, dass Updates auf Snapshots basieren, ist RocksDB auch präziser beim Sperren. Alle Sperren betreffen nur eindeutige Indizes. Während des Aktualisierungsprozesses wird nur der Primärschlüssel gesperrt. Wenn eine Spalte eine eindeutige Einschränkung enthält, muss sie gesperrt werden. Gewöhnliche sekundäre Indizes müssen jedoch nicht gesperrt werden. Dies dient dazu, Konflikte mit eindeutigen Einschränkungen zu vermeiden. Wenn hier die eindeutige Einschränkung (Primärschlüssel oder eindeutiger Index) aktualisiert wird, ist eine Sperre erforderlich. InnoDB muss jeden Index sperren. Wenn Aktualisierungen beispielsweise auf der Grundlage des Sekundärindex positioniert werden, muss auch der Sekundärindex gesperrt werden. Der Grund für diesen Unterschied liegt darin, dass InnoDB die RR-Isolationsstufe implementiert. Lassen Sie uns hier ein wenig über die Isolationsstufe sprechen. Tatsächlich unterscheidet sich die in MySQL definierte RR-Isolationsstufe etwas von der durch den SQL-Standard definierten Isolationsstufe. Der SQL-Standard definiert die RR-Isolationsstufe, um das Problem nicht wiederholbarer Lesevorgänge zu lösen, und die Serializable-Isolationsstufe, um das Problem von Phantom-Lesevorgängen zu lösen. Beim nicht wiederholbaren Lesen wird betont, dass der Wert desselben Datensatzes nicht geändert wird, während beim Phantomlesen betont wird, dass die Anzahl der Datensätze, die durch zwei Lesevorgänge zurückgegeben werden, fest ist und die Anzahl der Datensätze nicht erhöht oder verringert wird. MySQL definiert die RR-Isolationsstufe, um die Probleme nicht wiederholbarer Lesevorgänge und Phantom-Lesevorgänge gleichzeitig zu lösen, während die Implementierung der RR-Isolationsstufe in InnoDB auf GAP-Sperren basiert. RocksDB unterstützt keine GAP-Sperren (unterstützt nur eindeutige Einschränkungsprüfungen und sperrt nicht vorhandene Schlüssel), da der Snapshot-basierte Mechanismus neu eingefügte Datensätze effektiv herausfiltern kann, während InnoDB andere Einfügungen durch Lückensperren aufgrund aktueller Lesevorgänge verhindern muss Der Sekundärindex muss ebenfalls gesperrt werden, hauptsächlich wegen der Sperrlücke, da sonst die Ergebnisse der beiden aktuellen Lesevorgänge unterschiedlich sein können. Für die RC-Split-Ebene müssen die gewöhnlichen Sekundärindizes von InnoDB natürlich nicht gesperrt werden.

4.死锁检测算法
      死锁检测采用DFS((Depth First Search,深度优先算法),基本思路根据加入等待关系,继续查找被等待者的等待关系,如果发现成环,则认为发生了死锁,当然在大并发系统下,锁等待关系非常复杂,为了将死锁检测带来的资源消耗控制在一定范围,可以通过设置deadlock_detect_depth来控制死锁检测搜索的深度,或者在特定业务场景下,认为一定不会发生死锁,则关闭死锁检测,这样在一定程度上有利于系统并发的提升。需要说明的是,如果关闭死锁,最好配套将锁等待超时时间设置较小,避免系统真发生死锁时,事务长时间hang住。死锁检测基本流程如下:
1.定位到具体某个分片,获取mutex
2.调用AcquireLocked尝试加锁
3.若上锁失败,则触发进行死锁检测
4.调用IncrementWaiters增加一个等待者
5.如果等待者不在被等待者map里面,则肯定不会存在死锁,返回
6.对于被等待者,沿着wait_txn_map_向下检查等待关系,看看是否成环
7.若发现成环,则将调用DecrementWaitersImpl将新加入的等待关系解除,并报死锁错误。

相关的数据结构:

class TransactionLockMgr {
// Must be held when modifying wait_txn_map_ and rev_wait_txn_map_.
std::mutex wait_txn_map_mutex_;

// Maps from waitee -> number of waiters.
HashMap<TransactionID, int> rev_wait_txn_map_;

// Maps from waiter -> waitee.
HashMap<TransactionID, autovector<TransactionID>> wait_txn_map_;

DecrementWaiters //

IncrementWaiters //
}

struct TransactionOptions {
bool deadlock_detect = false; //是否检测死锁
int64_t deadlock_detect_depth = 50; //死锁检测的深度
int64_t lock_timeout = -1; //等待锁时间,线上一般设置为5s
int64_t expiration = -1; //持有锁时间,
}




Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung von Beispielen des RocksDB-Sperrmechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn