Parallelitätskontrolle
durch die Verwaltung mehrerer Versionen von Datenzeilen. Diese Technologie stellt sicher, dass konsistente Lesevorgänge
unter der Transaktionsisolationsstufe von InnoDB ausgeführt werden. Mit anderen Worten: Es werden einige Zeilen abgefragt, die von einer anderen Transaktion aktualisiert werden, und Sie können die Werte sehen, bevor sie aktualisiert wurden, sodass Sie bei der Abfrage nicht darauf warten müssen, dass eine andere Transaktion die Sperre aufhebt . MVCC hat keinen formalen Standard. Die Implementierung von MVCC kann in verschiedenen DBMS unterschiedlich sein und wird nicht allgemein verwendet (siehe entsprechende DBMS-Dokumentation). Hier erklären wir den Implementierungsmechanismus von MVCC in InnoDB (andere MySQL-Speicher-Engines unterstützen ihn nicht). Behandeln Sie Lese-/Schreibkonflikte
so, dass selbst bei Lese-/Schreibkonflikten keine Sperrung
und nicht blockierendes gleichzeitiges Lesen
erreicht werden kann, und Dieser Lesevorgang bezieht sich auf den Snapshot-Lesevorgang
, nicht auf den aktuellen Lesevorgang
. Der aktuelle Lesevorgang ist eigentlich ein Sperrvorgang und eine Implementierung des pessimistischen Sperrens. Die Essenz von MVCC besteht darin, optimistisches Locking-Denken zu nutzen. MVCC (Multiversion Concurrency Control),多版本并发控制
。顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制
。这项技术使得在InnoDB的事务隔离级别下执行一致性读
.操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
MVCC没有正式的标准,在不同的DBMS中MVCC的实现方式可能是不同的,也不是普遍使用的(大家可以参考相关的DBMS文档)。这里讲解InnoDB中 MVCC的实现机制(MySQL其它的存储引擎并不支持它)
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突
,做到即使有读写冲突时,也能做到不加锁
,非阻塞并发读
,而这个读指的就是快照读
,而非当前读
。当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。
快照读又叫一致性读,读取的是快照数据。不加锁的简单的SELECT都属于快照读
2.1 Snapshot-Lesen
Einfache SELECTs ohne Sperren sind alle Snapshot-Lesevorgänge
, was bedeutet, dass es sich bei Snapshot-Lesevorgängen um nicht blockierende Lesevorgänge ohne Sperren handelt basiert auf MVCC, was in vielen Fällen Sperrvorgänge vermeidet und den Overhead reduziert. Da es auf mehreren Versionen basiert, liest der Snapshot-Leser möglicherweise nicht unbedingt die neueste Version der Daten, sondern möglicherweise die vorherige historische Version. Die Voraussetzung für das Snapshot-Lesen ist, dass die Isolationsebene nicht die serielle Ebene ist. Das Snapshot-Lesen auf serieller Ebene degeneriert zum aktuellen Lesen. 2.2 Aktueller LesevorgangDer aktuelle Lesevorgang liest die neueste Version des Datensatzes (die neuesten Daten, nicht die historische Version der Daten). Beim Lesen muss außerdem sichergestellt werden, dass andere gleichzeitige Transaktionen den aktuellen Datensatz nicht ändern können Der gelesene Datensatz wird verarbeitet. Eine gesperrte SELECT-Anweisung oder das Hinzufügen, Löschen oder Ändern von Daten führt zum aktuellen Messwert. Zum Beispiel: Die Standardisolationsstufe ist wiederholbares Lesen, wodurch das Problem des schmutzigen Lesens und des nicht wiederholbaren Lesens gelöst werden kann. Schon allein aus Sicht der Definition kann das Problem des Phantomlesens nicht gelöst werden. Wenn wir das Problem des Phantomlesens lösen möchten, müssen wir Serialisierung verwenden, dh die Isolationsstufe auf die höchste Stufe erhöhen. Dies verringert jedoch die Fähigkeit der Datenbank zur Transaktionsgleichzeitigkeit erheblich. MVCC kann die nicht wiederholbaren Lese- und Phantomleseprobleme durch optimistisches Sperren lösen, anstatt einen Sperrmechanismus zu verwenden! Es kann in den meisten Fällen Sperren auf Zeilenebene ersetzen und den Systemaufwand reduzieren.
3.2 Versteckte Felder, Undo-Log-VersionsketteWenn wir uns die Undo-Log-Versionskette noch einmal ansehen, enthalten die Clustered-Index-Datensätze für Tabellen, die die InnoDB-Speicher-Engine verwenden, zwei notwendige versteckte Spalten. trx_id: Jedes Mal, wenn eine Transaktion einen Clustered-Index-Datensatz ändert, wird die Transaktions-ID der Transaktion der ausgeblendeten Spalte trx_id zugewiesen. roll_pointer: Jedes Mal, wenn ein Clustered-Index-Datensatz geändert wird, wird die alte Version in das Rückgängig-Protokoll geschrieben. Dann entspricht diese versteckte Spalte einem Zeiger, über den die Informationen vor dem Datensatz gefunden werden können.4. MVCC-Implementierungsprinzip von ReadView
Wenn eine Transaktion den MVCC-Mechanismus verwendet, um einen Snapshot-Lesevorgang auszuführen, wird eine Leseansicht generiert, nämlich ReadView. Wenn eine Transaktion gestartet wird, wird ein aktueller Snapshot des Datenbanksystems erstellt. InnoDB erstellt für jede Transaktion ein Array, um die ID der aktuell aktiven Transaktionen des Systems aufzuzeichnen und zu verwalten. („Aktiv“ bezieht sich auf diejenigen, die bereits gestartet wurden wurden noch nicht übermittelt.
Verwenden Sie die Isolationsstufentransaktion READ UNCOMMITTED
. Da Sie die durch nicht festgeschriebene Transaktionen geänderten Datensätze lesen können, lesen Sie einfach die neueste Version des Datensatzes READ UNCOMMITTED
隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。
使用SERIALIZABLE
隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。
使用READ COMMITTED
和REPEATABLE READ
隔离级别的事务,都必须保证读到已经提交了的事务
修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。
这个ReadView中主要包含4个比较重要的内容,分别如下:
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。
如果被访问版本的trx_id属性值与ReadView中的creator_trx_id
值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值小于ReadView中的up_limit_id
值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值大于或等于ReadView中的low_limit_id
值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
如果被访问版本的trx_id属性值在ReadView的up_limit_id
和low_limit_id
之间,那就需要判断一下trx_id属性值是不是在trx_ids
列表中。如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。 4.4 MVCC整体操作流程
了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:
1.首先获取事务自己的版本号,也就是事务ID;
2.获取ReadView;
3.查询得到的数据,然后与ReadView中的事务版本号进行比较;
4.如果不符合Readview规则,就需要从Undo Log中获取历史快照;
5.最后返回符合规则的数据。
如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最近版本不可见,则该记录对该事务不可见,并且查询结果中不包含该记录。
InnoDB中,MVCC是通过Undo Log + Read View进行数据读取,Undo Log保存了历史快照,而Read View规则帮我们判断当前版本的数据是否可见。
在隔离级别为读已提交(Read Committed)时,一个事务中的每一次select查询都会重新获取一次Read View。
当隔离级别为可重读的时候,就避免了不可重复读,这是因为一个事务只在第一次select的时候会获取一次Read View,而后面所有的select都会复用这个Read View,
如下所示:
假设现在student表中只有一条由事务id
为8
的事务插入一条记录:
MVCC只能在READ COMMITTED和REPEATABLE READ两个隔离级别下工作。接下来看一下READ COMMITTED
和REPEATABLE READ
所谓的生成Readview的时机不同到底不同在哪里。
隔离级别下:
READ COMMITTED:每次读取数据前都生成一个ReadView
SERIALIZABLE
erfordert InnoDB eine Sperre für den Zugriff auf Datensätze. 🎜🎜Die Isolationsstufen READ COMMITTED
und REPEATABLE READ
müssen gewährleistet sein Dass Datensätze, die durch festgeschriebene Transaktionen
geändert wurden, gelesen werden, aber noch nicht übermittelt wurden, ist, dass die neueste Version des Datensatzes nicht direkt gelesen werden kann Bestimmen Sie, welche Version in der Versionskette für die aktuelle Transaktion sichtbar ist. 🎜🎜Dieses ReadView enthält hauptsächlich 4 wichtige Inhalte:🎜🎜 src="https://img.php.cn/upload/article/000/000/164/168576427063836.png" alt="MVCC-Instanzanalyse zur MySQL-Mehrversions-Parallelitätskontrolle" />🎜🎜4.3 ReadView-Regeln🎜 🎜Mit In dieser ReadView müssen Sie beim Zugriff auf einen Datensatz nur die folgenden Schritte ausführen, um festzustellen, ob eine bestimmte Version des Datensatzes sichtbar ist 🎜creator_trx_id
in ReadView, was bedeutet, dass die aktuelle Transaktion auf ihren eigenen geänderten Datensatz zugreift, sodass auf diese Version von der aktuellen Transaktion zugegriffen werden kann 🎜up_limit_id
-Wert in ReadView, bedeutet dies, dass die Transaktion, die diese Version generiert hat, vor der aktuellen festgeschrieben wurde Die Transaktion generiert ReadView, sodass die aktuelle Transaktion auf diese Version zugreifen kann 🎜low_limit_id
ist Wert in ReadView bedeutet, dass die Transaktion, die diese Version generiert hat, nach der aktuellen Transaktion gestartet wurde, die ReadView generiert hat, sodass diese Version nicht auf aktuelle Transaktion zugreifen kann 🎜up_limit_id
und low_limit_id
, dann müssen Sie die trx_id bestimmen. Ob sich der Attributwert in der trx_ids
-Liste befindet. Wenn dies der Fall ist, bedeutet dies, dass die Transaktion, die diese Version generiert hat, zum Zeitpunkt der Erstellung der ReadView noch aktiv war und auf diese Version nicht zugegriffen werden kann. Wenn nicht, bedeutet dies, dass die Transaktion, die diese Version beim Erstellen der ReadView generiert hat, festgeschrieben wurde und auf diese Version zugegriffen werden kann. 4.4 Gesamter MVCC-Betriebsprozess 🎜Transaktions-ID
von 8
eingefügt wurde: 🎜🎜🎜 🎜MVCC kann nur unter zwei Isolationsstufen arbeiten: READ COMMITTED und REPEATABLE READ. Schauen wir uns als Nächstes den Unterschied im sogenannten Timing der Readview-Generierung zwischen READ COMMITTED
und REPEATABLE READ
an. 🎜🎜5.1 READ COMMITTED🎜🎜Unter Isolationsstufe: 🎜🎜READ COMMITTED: Vor jedem Datenlesen eine ReadView generieren
🎜Es werden jetzt zwei Transaktionen mit den Transaktions-IDs 10 und 20 ausgeführt:
Hinweis: Während der Transaktionsausführung nur, wenn der Datensatz tatsächlich zum ersten Mal geändert wird (z. B. mit INSERT, DELETE, UPDATE). Anweisung) wird eine separate Transaktions-ID zugewiesen und diese Transaktions-ID wird inkrementiert. Aus diesem Grund haben wir in Transaktion 2 einige Datensätze in anderen Tabellen aktualisiert, damit Transaktions-IDs zugewiesen werden können.
Zu diesem Zeitpunkt lautet die vom Datensatz mit der ID 1 in der Schülertabelle erhaltene Versionsverknüpfungsliste wie folgt:
Angenommen, eine Transaktion mit der Isolationsstufe READ COMMITED beginnt mit der Ausführung:
Dies Der Ausführungsprozess von SELECT1 ist wie folgt:
Schritt 1∶Beim Ausführen der SELECT-Anweisung wird zuerst ein ReadView
generiert. Der Inhalt der trx_ids von ReadView
Liste ist [10, 20 ], up_limit_id
ist 10
, low_limit_id
ist 21
, creator_trx_id
ist 0
. ReadView
,ReadView的trx_ids
列表的内容就是[10,20],up_limit_id
为10
, low_limit_id
为21
, creator_trx_id
为0
。
步骤2:从版本链中挑选可见的记录,从图中看出,最新版本的列name
的内容是'王五'
,该版本的trx_id
值为10
,在trx_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
步骤3:下一个版本的列name
的内容是'李四'
,该版本的trx_id
值也为10
,也在trx_ids
列表内,所以也不符合要求,继续跳到下一个版本。
步骤4:下一个版本的列 name
的内容是‘张三'
,该版本的trx_id
值为8
,小于ReadView 中的up_limit_id值10,所以这个版本是符合要求的,最后返回给用户的版本就是这条列name为‘张三’的记录。
之后,我们把事务id
为10
的事务提交一下:
然后再到事务id
为20
的事务中更新一下表student
中id
为1
Schritt 2: Sichtbare Datensätze aus der Versionskette auswählen Wie aus der Abbildung ersichtlich ist, lautet der Inhalt der Spalte name
der neuesten Version '王五'
. und die Version von trx_id-Wert ist 10
, der sich in der trx_ids-Liste befindet, sodass er die Sichtbarkeitsanforderungen nicht erfüllt und entsprechend dem roll_pointer zur nächsten Version springt.
name
der nächsten Version ist '李思'
und der Wert von trx_id
dieser Version ist außerdem ist 10
auch in der trx_ids
-Liste enthalten, sodass es die Anforderungen nicht erfüllt und weiterhin zur nächsten Version springt. Schritt 4: Der Inhalt der Spalte name
der nächsten Version ist ‘Zhang San'
und der Wert von trx_id
dieser Version ist 8
, was kleiner als der up_limit_id-Wert 10 in ReadView ist, sodass diese Version die Anforderungen erfüllt. Die letztendlich an den Benutzer zurückgegebene Version ist der Datensatz mit dem Spaltennamen „Zhang San“.
id
als 10
: Gehen Sie dann zu der Transaktion, bei der die
Transaktions-ID
20 ist. code> Aktualisieren Sie den Datensatz mit <code>id
als 1
in der Tabelle student
:
In diesem Moment die Version des Datensatzes mit ID 1 in der Tabelle student Die Kette sieht so aus:
Dann suchen Sie weiter nach dem Datensatz mit ID 1 in der Transaktion, die gerade die Isolationsstufe READ COMMITTED verwendet hat, wie folgt:
Der Ausführungsprozess dieses SELECT2 ist wie folgt:
Schritt 1∶
Beim Ausführen der SELECT-Anweisung wird separat eine ReadView generiert. Der Inhalt der trx_ids-Liste von ReadView ist [20], up_limit_id ist 20, low_limit_id ist 21 und Creator_trx_id ist 0.
Schritt 3∶Der Inhalt des Spaltennamens der nächsten Version lautet „Qian Qi“. Der trx_id-Wert dieser Version ist 20, was auch in der trx_ids-Liste enthalten ist, sodass er die Anforderungen nicht erfüllt und weiter springt zur nächsten Version.
Schritt 4: Der Inhalt des Spaltennamens der nächsten Version ist „王五“. Der trx_id-Wert dieser Version ist 10, was kleiner ist als der up_limit.id-Wert 20 in ReadView, sodass diese Version die Anforderungen erfüllt und ist schließlich an den Benutzer zurückgegeben. Die Version ist der Datensatz, dessen Spaltenname „王五“ ist.Wenn analog dazu der Datensatz mit der Transaktions-ID 20 auch später übermittelt wird und der Datensatz mit dem ID-Wert 1 in der Tabelle student in einer Transaktion mit der Isolationsstufe READ CONMMITTED erneut abgefragt wird, lautet das Ergebnis „Song Ba“, wir werden es nicht tun Analysieren Sie den spezifischen Prozess.
5.2 REPEATABLE READ
Unter der Isolationsstufe: Für Transaktionen mit der Isolationsstufe REPEATABLE READ wird eine ReadView nur generiert, wenn die Abfrageanweisung zum ersten Mal ausgeführt wird, und nachfolgende Abfragen werden nicht wiederholt generiert . 🎜🎜🎜Zum Beispiel werden im System zwei Transaktionen mit den Transaktions-IDs 10 und 20 ausgeführt: 🎜🎜🎜🎜🎜此刻,表student中id为1的记录得到的版本链表如下所示:
假设现在有一个使用REPEATABLE READ隔离级别的事务开始执行:
此时执行过程与read committed相同
然后再到刚才使用REPEATABLE READ隔离级别的事务中继续查找id为1的记录,如下:
这个SELECT2的执行过程如下:
步骤1:因为当前事务的隔离级别为REPEATABLE READ,而之前在执行SELECT1时已经生成过ReadView了,所以此时直接复用之前的ReadView,之前的ReadView的trx_ids列表的内容就是[10,20],up_limit_id为10, low_limit_id为21 , creator_trx_id为0。
步骤2:然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列name的内容是’宋八’trx_id值为20,在trx_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
步骤3:下一个版本的列name的内容是’钱七’,该版本的trx_id值为20,也在trx_ids列表内合要求,继续跳到下一个版本。
步骤4:下一个版本的列name的内容是’王五’,该版本的trx_id值为10,而trx_ids列表中是包含值为10的事务id的,所以该版本也不符合要求,同理下一个列name的内容是’李四’的版本也不符合要求。继续跳到下个版本。
步聚5∶下一个版本的列name的内容是’张三’,该版本的trx_id值为80,小于Readview中的up_limit_id值10,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为‘张三’的记录。
两次SELECT查询得到的结果是重复的,记录的列c值都是’张三’,这就是可重复读的含义。如果我们之后再把事务id为20的记录提交了,然后再到刚才使用REPEATABLE READ隔离级刷的事务中继续查找这个id为1的记录,得到的结果还是’张三’,具体执行过程大家可以自己分析一下。
假设现在表student中只有一条数据,数据内容中,主键id=1,隐藏的trx_id=10,它的undo log如下图所示。
假设现在有事务A和事务B并发执行,事务A的事务id为20,事务B的事务id为30。
步骤1:事务A开始第一次查询数据,查询的SQL语句如下。
select * from student where id > 1;
在开始查询之前,MySQL会为事务A产生一个ReadView,此时ReadView的内容如下: trx_ids=[20, 30 ] ,up_limit_id=20 , low_limit_id=31 , creator_trx_id=20。
因为表student只有一条符合条件 where id>=1 的数据,所以会被查询出来。然后根据ReadView机制,发现该行数据的trx_id=10,小于事务A的ReadView里up_limit_id,这表示这条数据是事务A开启之前,其他事务就已经提交了的数据,因此事务A可以读取到。
结论:事务A的第一次查询,能读取到一条数据,id=1。
步骤2∶接着事务B(trx_id=30),往表student中新插入两条数据,并提交事务。
insert into student(id,name) values(2,'李四'); insert into student(id,name) values(3,'王五');
此时表student中就有三条数据了,对应的undo如下图所示:
Schritt 3∶ Dann startet Transaktion A die zweite Abfrage. Gemäß den Regeln der wiederholbaren Leseisolationsstufe generiert Transaktion A ReadView zu diesem Zeitpunkt nicht neu. Zu diesem Zeitpunkt erfüllen alle drei Daten in der Schülertabelle die Bedingung, dass id>=1 ist, sodass sie zuerst gefunden werden. Anschließend wird gemäß dem ReadView-Mechanismus beurteilt, ob jedes Datenelement von Transaktion A gesehen werden kann.
1) Zunächst sind die Daten mit der ID=1, wie bereits erwähnt, für Transaktion A sichtbar.
2) Dann gibt es die Daten mit id=2 und ihrer trx_id=30. Zu diesem Zeitpunkt stellt Transaktion A fest, dass dieser Wert zwischen up_limit_id und low_limit_id liegt, daher muss ermittelt werden, ob 30 in den trx_ids enthalten ist Array. Da trx_ids=[20,30] von Transaktion A im Array ist, bedeutet dies, dass die Daten mit id=2 von anderen Transaktionen übermittelt wurden, die gleichzeitig mit Transaktion A gestartet wurden, sodass diese Daten von Transaktion A nicht gesehen werden können.
3) Ebenso ist die trx_id dieser Daten mit id=3 ebenfalls 30, sodass sie von Transaktion A nicht gesehen werden kann.
Fazit: Die zweite Abfrage der letzten Transaktion A kann nur die Daten mit id=1 abfragen. Dies ist dasselbe wie das Ergebnis der ersten Abfrage von Transaktion A, daher gibt es kein Phantomlesephänomen. Daher gibt es unter der wiederholbaren Isolationsstufe von MySQL kein Phantomleseproblem.
Das obige ist der detaillierte Inhalt vonMySQL-Parallelitätskontrolle für mehrere Versionen, MVCC-Instanzanalyse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!