MVCC (Control de concurrencia de múltiples versiones)

El nombre completo es Control de concurrencia de múltiples versiones, es decir, control de concurrencia de múltiples versiones, principalmente para mejorar el rendimiento de concurrencia de la base de datos. Cuando se produce una solicitud de lectura o escritura para la misma fila de datos, se bloqueará y bloqueará. Pero MVCC utiliza una mejor manera de manejar las solicitudes de lectura y escritura, por lo que no es necesario bloquear cuando se producen conflictos de solicitud de lectura y escritura. Esta lectura se refiere a la lectura instantánea, no a la lectura actual. La lectura actual es una operación de bloqueo, que es un bloqueo pesimista. Entonces, ¿cómo logra la lectura y escritura sin bloqueo? ¿A qué se refieren la lectura instantánea y la lectura actual? Todos aprenderemos más tarde.

1. Principio de MVCC

1.1, revisar el nivel de aislamiento de transacciones

Bajo el nivel de aislamiento de LECTURA REPETIBLE, MySQL puede evitar en gran medida el problema de la lectura fantasma ( parece haberse resuelto, pero no se ha resuelto por completo ).¿Cómo lo hace MySQL?

1.2, cadena de versiones

Conceptos que se deben conocer (un dato por cada cadena de versiones):

Sabemos que para una tabla que usa el motor de almacenamiento InnoDB, sus registros de índice agrupados contienen dos columnas ocultas necesarias (row_id no es necesario, cuando la tabla que creamos tiene una clave principal o una clave ÚNICA que no es NULL, la columna row_id no se incluirá ): trx_id: cada vez que una transacción modifica un registro de índice agrupado, la identificación de la transacción se asignará a la columna oculta trx_id. roll_pointer: Cada vez que se modifica un registro de índice agrupado, la versión anterior se escribirá en el registro de deshacer, y luego esta columna oculta es equivalente a un puntero, a través del cual se puede encontrar la información antes de la modificación del registro.

(Punto complementario: registro de deshacer: para lograr la atomicidad de las transacciones, cuando el motor de almacenamiento InnoDB realmente agrega, elimina y modifica un registro, primero debe escribir el registro de deshacer correspondiente. Generalmente, cada vez que se cambia un registro , corresponderá a un registro de deshacer , pero en algunas operaciones de actualización de registros, también puede corresponder a dos registros de deshacer. Una transacción puede agregar, eliminar y actualizar varios registros durante la ejecución, es decir, muchos registros deben ser registrados Registros de deshacer correspondientes, estos registros de deshacer se numerarán desde 0, es decir, según el orden de generación, se denominan Registro de deshacer n.° 0, Registro de deshacer n.° 1, …, Registro de deshacer n.° n. , etc., esta numeración también se conoce como deshacer no.)

Para ilustrar esto, creamos una tabla de demostración

CREAR TABLA maestro ( 
número INT, 
nombre VARCHAR (100), 
dominio varchar (100), 
CLAVE PRINCIPAL (número) 
) Motor = InnoDB CHARSET = utf8;

Luego inserte un dato en esta tabla:

INSERTAR EN LOS VALORES del maestro (1, 'Li Jin', 'serie JVM');

Los datos en la tabla ahora se ven así:

Suponiendo que la identificación de la transacción de insertar este registro es 60, el diagrama esquemático de este registro en este momento es el siguiente:

Suponga que las siguientes dos transacciones cuyos ID de transacción son 80 y 120 respectivamente realizan operaciones de ACTUALIZACIÓN en este registro y el proceso de la operación es el siguiente:

Cada vez que se cambie un registro, se registrará un registro de deshacer, y cada registro de deshacer también tiene un atributo roll_pointer (el registro de deshacer correspondiente a la operación INSERTAR no tiene este atributo, porque el registro no tiene una versión anterior), y estos registros de deshacer se pueden conectar entre sí para formar una lista enlazada, por lo que la situación actual se parece a la siguiente imagen:

Cada vez que se actualice el registro, el valor anterior se colocará en un registro de deshacer Incluso si es una versión anterior del registro, a medida que aumenta el número de actualizaciones, todas las versiones se conectarán en una lista vinculada mediante el atributo roll_pointer. Ponemos esto La lista enlazada se llama cadena de versión, y el nodo principal de la cadena de versión es el valor más reciente del registro actual. Además, cada versión también contiene la identificación de transacción correspondiente cuando se generó la versión. Entonces, la cadena de versiones de este registro se puede usar para controlar el comportamiento de las transacciones simultáneas que acceden al mismo registro, entonces este mecanismo se llama Control de concurrencia de múltiples versiones (Multi-Version Concurrency Control MVCC).

1.3, vista de lectura

Conceptos que deben conocerse (para sentencias de consulta SQL)

Para las transacciones que usan el nivel de aislamiento LECTURA NO COMPROMETIDA, dado que los registros modificados por transacciones no confirmadas se pueden leer, es bueno leer directamente la última versión de los registros (por lo que habrá lecturas sucias, lecturas no repetibles y lecturas fantasma ).

Para las transacciones que utilizan el nivel de aislamiento SERIALIZABLE, InnoDB utiliza el bloqueo para acceder a los registros ( es decir, todas las transacciones son en serie y, por supuesto, no habrá lecturas sucias, lecturas no repetibles o lecturas fantasma ).

Para las transacciones que utilizan los niveles de aislamiento READ COMMITTED y REPEATABLE READ, es necesario asegurarse de que se leen los registros modificados por la transacción comprometida, es decir, si otra transacción ha modificado el registro pero aún no lo ha comprometido, no puede hacerlo directamente. leer los últimos registros de versión, la pregunta central es: ¿de dónde viene la diferencia entre los niveles de aislamiento de LECTURA COMPROMETIDA y LECTURA REPETIBLE en términos de lecturas no repetibles y lecturas fantasma? De hecho, combinado con el conocimiento previo, la clave para estos dos niveles de aislamiento es para juzgar la cadena de versión Qué versión es visible para la transacción actual . Con este fin, InnoDB propone un concepto de ReadView (que actúa sobre declaraciones de consulta SQL),

Este ReadView contiene principalmente 4 contenidos más importantes: m_ids: Indica la lista de ID de transacción de las transacciones activas de lectura y escritura en el sistema actual cuando se genera el ReadView. min_trx_id: indica el id de transacción más pequeño entre las transacciones activas de lectura y escritura en el sistema actual cuando se genera ReadView, es decir, el valor mínimo en m_ids. max_trx_id: Indica el valor de id que debe asignarse a la siguiente transacción en el sistema cuando se genera ReadView. Tenga en cuenta que max_trx_id no es el valor máximo en m_ids, y los ID de transacción se asignan de forma incremental. Por ejemplo, ahora hay tres transacciones con ID 1, 2 y 3, y luego se envía la transacción con ID 3. Luego, cuando una nueva transacción de lectura genera ReadView, m_ids incluye 1 y 2, el valor de min_trx_id es 1 y el valor de max_trx_id es 4. Creator_trx_id: Indica el id de transacción de la transacción que generó el ReadView.

1.4,LECTURA COMPROMETIDA

Resolución de problemas de lectura sucia

Las transacciones de nivel de aislamiento de LECTURA COMPROMETIDA generarán un ReadView separado al comienzo de cada consulta.

En MySQL, una gran diferencia entre los niveles de aislamiento READ COMMITTED y REPEATABLE READ es que generan ReadView en diferentes momentos. Todavía tomemos el maestro de la tabla como ejemplo. Suponiendo que solo hay un registro insertado por una transacción con una identificación de transacción de 60 en el maestro de la tabla, echemos un vistazo a la diferencia en el tiempo de generación de ReadView entre READ COMMITTED y REPEATABLE LEER. LECTURA COMPROMETIDA —— Genera un ReadView cada vez antes de leer datos

ACTUALIZAR profesor SET nombre = '马' DONDE número = 1; 
ACTUALIZAR profesor SET nombre = '连' DONDE número = 1; 
...

En este momento, la lista enlazada de versiones obtenida por el registro cuyo número es 1 en la tabla docente es la siguiente:

Suponiendo que ahora se ejecuta una transacción que utiliza el nivel de aislamiento LECTURA COMPROMETIDA:

Transacción que utiliza el nivel de aislamiento de LECTURA COMPROMETIDA 
COMENZAR
; 
SELECCIONAR 1: Transacción 80, 120 no confirmada 
SELECCIONAR
* DESDE el maestro DONDE número = 1; # El valor del nombre de columna obtenido es 'Li Jin'

El momento de la primera selección es el siguiente:

El proceso de ejecución de este SELECTE1 es el siguiente: Al ejecutar la instrucción SELECT, primero se generará un ReadView:

El contenido de la lista m_ids de ReadView es [80, 120], min_trx_id es 80, max_trx_id es 121 y created_trx_id es 0.

A continuación, seleccione los registros visibles de la cadena de versiones. Se puede ver en la figura que el contenido del nombre de la columna de la última versión es 'conectar ' , y el valor trx_id de esta versión es 80, que está en la lista m_ids, por lo que no cumple con los requisitos de visibilidad (trx_id Si el valor del atributo está entre min_trx_id y max_trx_id de ReadView, significa que la transacción que generó esta versión cuando se creó ReadView todavía está activa y no se puede acceder a esta versión; si es no, significa que se envió la transacción que generó esta versión cuando se creó ReadView, y se puede acceder a esta versión Access), salte a la siguiente versión de acuerdo con roll_pointer. El contenido del nombre de la columna en la próxima versión es ' caballo ', y el valor trx_id de esta versión también es 80, que también está en la lista m_ids, por lo que no cumple con los requisitos, así que continúe saltando a la próxima versión . El contenido del nombre de la columna en la próxima versión es ' Li Jin ', el valor trx_id de esta versión es 60, que es más pequeño que el valor min_trx_id en ReadView, por lo que esta versión cumple con los requisitos y la versión final se devuelve al usuario es el nombre de la columna del registro ' Lee Jin '.

Entonces, con este mecanismo, ¡no habrá problemas de lectura sucia! Debido a que juzgará la versión activa, debe usarse si no está en la versión activa y es imposible leer registros sin confirmaciones.

problema de lectura no repetible

Luego, enviamos la transacción con la identificación de transacción de 80, y luego actualizamos el registro con el número 1 en la tabla del maestro en la transacción con la identificación de transacción de 120:

Transacción 120 
COMENZAR
; 
Actualizar
algunos registros de otras tablas 
ACTUALIZAR
nombre del CONJUNTO del maestro = 'Yan' DONDE número = 1; 
ACTUALIZAR nombre del CONJUNTO del maestro = 'Chao' DONDE número = 1 
;

En este momento, la cadena de versiones del registro cuyo número es 1 en la tabla teacher se ve así:

Luego vaya a la transacción que acaba de usar el nivel de aislamiento LECTURA COMPROMETIDA para continuar encontrando el registro con el número 1, de la siguiente manera:

Transacciones que utilizan el nivel de aislamiento LECTURA COMPROMETIDA

COMENZAR; 
​SELECCIONAR1
: Las transacciones 80 y 120 no se envían 
​SELECCIONAR
* DEL profesor DONDE número = 1; # El valor del nombre de columna obtenido es 'Li Jin' 
​SELECCIONAR2
: La transacción 80 se envía, la transacción 120 no se envía 
SELECCIONAR
* FROM profesor DONDE número = 1; # El valor del nombre de columna obtenido está 'conectado 
'

El momento de la segunda selección es el siguiente:

El proceso de ejecución de este SELECTE2 es el siguiente:

SELECCIONE * DESDE profesor DONDE número = 1;

Cuando se ejecuta la instrucción SELECT, se generará un ReadView por separado y la información de ReadView es la siguiente:

El contenido de la lista m_ids es [120] (se ha enviado la transacción cuyo ID de transacción es 80, por lo que no estará allí cuando se vuelva a generar la instantánea), min_trx_id es 120, max_trx_id es 121 y Creator_trx_id es 0. Luego seleccione los registros visibles de la cadena de versiones. En la figura se puede ver que el contenido del nombre de la columna de la última versión es '赤', y el valor trx_id de esta versión es 120, que está en la lista m_ids, por lo que no cumple con los requisitos de visibilidad.Según roll_pointer salta a la siguiente versión. El contenido del nombre de la columna en la próxima versión es ' estricto ', y el valor trx_id de esta versión es 120, que también está en la lista de m_ids, por lo que no cumple con los requisitos, así que continúe saltando a la próxima versión. El contenido del nombre de la columna en la próxima versión es ' conectar ', el valor trx_id de esta versión es 80, que es más pequeño que el valor min_trx_id de 120 en ReadView, por lo que esta versión cumple con los requisitos y la versión final volvió a la usuario es el nombre de la columna de los registros ' pares '.

Por analogía, si el registro cuyo ID de transacción es 120 también se envía más tarde, al consultar el registro cuyo valor numérico es 1 en la tabla maestra en la transacción usando el nivel de aislamiento LECTURA COMPROMETIDA nuevamente, el resultado es '晓', el proceso específico nosotros no lo analizaré.

Pero habrá un problema de lectura no repetible.

Obviamente dos veces en la transacción anterior

1.5, LECTURA REPETIBLE

LECTURA REPETIBLE resuelve el problema de lectura no repetible

LECTURA REPETIBLE: genera un ReadView la primera vez que se leen los datos

Para las transacciones que usan el nivel de aislamiento de LECTURA REPETIBLE, solo se generará una vista de lectura cuando la instrucción de consulta se ejecute por primera vez, y las consultas posteriores no se generarán repetidamente. Usemos un ejemplo para ver cuál es el efecto.

Por ejemplo, ahora hay dos transacciones con ID de transacción de 80 y 120 en el sistema: Transacción 80

ACTUALIZAR profesor SET nombre = '马' DONDE número = 1; 
ACTUALIZAR profesor SET nombre = '连' DONDE número = 1; 
...

En este momento, la lista enlazada de versiones obtenida por el registro cuyo número es 1 en la tabla docente es la siguiente:

Suponiendo que ahora se ejecuta una transacción que utiliza el nivel de aislamiento de LECTURA REPETIBLE:

Transacción que utiliza el nivel de aislamiento de LECTURA COMPROMETIDA 
COMENZAR
; 
SELECCIONAR 1: Transacción 80, 120 no confirmada 
SELECCIONAR
* DESDE el maestro DONDE número = 1; # El valor del nombre de columna obtenido es 'Li Jin'

El proceso de ejecución de este SELECTE1 es el siguiente: Al ejecutar la instrucción SELECT, primero se generará un ReadView:

El contenido de la lista m_ids de ReadView es [80, 120], min_trx_id es 80, max_trx_id es 121 y created_trx_id es 0.

A continuación, seleccione los registros visibles de la cadena de versiones. Se puede ver en la figura que el contenido del nombre de la columna de la última versión es 'conectar ' , y el valor trx_id de esta versión es 80, que está en la lista m_ids, por lo que no cumple con los requisitos de visibilidad (trx_id Si el valor del atributo está entre min_trx_id y max_trx_id de ReadView, significa que la transacción que generó esta versión cuando se creó ReadView todavía está activa y no se puede acceder a esta versión; si es no, significa que se envió la transacción que generó esta versión cuando se creó ReadView, y se puede acceder a esta versión Access), salte a la siguiente versión de acuerdo con roll_pointer. El contenido del nombre de la columna en la próxima versión es ' caballo ', y el valor trx_id de esta versión también es 80, que también está en la lista m_ids, por lo que no cumple con los requisitos, así que continúe saltando a la próxima versión . El contenido del nombre de la columna en la próxima versión es ' Li Jin ', el valor trx_id de esta versión es 60, que es más pequeño que el valor min_trx_id en ReadView, por lo que esta versión cumple con los requisitos y la versión final se devuelve al usuario es el nombre de la columna del registro ' Lee Jin '. Después de eso, enviamos la transacción con la identificación de transacción 80 y luego actualizamos el registro con el número 1 en la tabla del maestro en la transacción con la identificación de transacción 120:

Transacción 120 
COMENZAR
; 
Actualizar
algunos registros de otras tablas 
ACTUALIZAR
nombre del CONJUNTO del maestro = 'Yan' DONDE número = 1; 
ACTUALIZAR nombre del CONJUNTO del maestro = 'Chao' DONDE número = 1 
;

En este momento, la cadena de versiones del registro cuyo número es 1 en la tabla teacher se ve así:

Luego vaya a la transacción que acaba de usar el nivel de aislamiento de LECTURA REPETIBLE para continuar encontrando el registro con el número 1, de la siguiente manera:

Transacciones que utilizan el nivel de aislamiento LECTURA COMPROMETIDA

COMENZAR; 
​SELECCIONAR1
: Las transacciones 80 y 120 no se envían 
​SELECCIONAR
* DEL profesor DONDE número = 1; # El valor del nombre de columna obtenido es 'Li Jin' 
​SELECCIONAR2
: La transacción 80 se envía, la transacción 120 no se envía 
SELECCIONAR
* FROM profesor DONDE número = 1; # El valor del nombre de columna obtenido es 'Li Jin 
'

El proceso de ejecución de este SELECTE2 es el siguiente:

Debido a que el nivel de aislamiento de la transacción actual es LECTURA REPETIBLE, y ReadView se generó antes de ejecutar SELECT1, por lo que en este momento, el ReadView anterior se reutiliza directamente. El contenido de la lista m_ids del ReadView anterior es [80, 120], y min_trx_id es 80, max_trx_id es 121, created_trx_id es 0.

Según el análisis anterior, el valor devuelto sigue siendo 'Li Jin'.

Es decir, los resultados de las dos consultas SELECT se repiten y los valores de nombre de columna de los registros son todos 'Li Jin', que es el significado de lectura repetible.

En resumen:

Reglas de comparación en ReadView (las dos primeras)

1. Si el valor del atributo trx_id de la versión a la que se accede es el mismo que el valor de created_trx_id en ReadView, significa que la transacción actual está accediendo a sus propios registros modificados, por lo que la transacción actual puede acceder a esta versión.

2. Si el valor del atributo trx_id de la versión a la que se accede es menor que el valor min_trx_id en ReadView, indica que la transacción que generó esta versión se ha confirmado antes de que la transacción actual genere ReadView, por lo que la transacción actual puede acceder a esta versión.

1.6, solución de lectura fantasma y fenómeno de lectura fantasma bajo MVCC

Ya sabemos que MVCC bajo el nivel de aislamiento de LECTURA REPETIBLE puede resolver el problema de lectura no repetible, entonces, ¿qué pasa con las lecturas fantasma? ¿Cómo se resuelve MVCC? La lectura fantasma es cuando una transacción lee registros varias veces de acuerdo con la misma condición y luego lee un registro que no se ha leído antes, y este registro proviene de un nuevo registro agregado por otra transacción. Podemos pensarlo, la transacción T1 bajo el nivel de aislamiento de LECTURA REPETIBLE primero lee múltiples registros de acuerdo con una determinada condición de búsqueda, luego la transacción T2 inserta un registro que cumple con la condición de búsqueda correspondiente y se confirma, y ​​luego la transacción T1 se ejecuta de acuerdo con la misma búsqueda condición Consultar. ¿Cuál será el resultado? Según las reglas de comparación en ReadView (las dos últimas): 3. Si el valor del atributo trx_id de la versión accedida es mayor o igual que el valor max_trx_id en ReadView, significa que la transacción que genera esta versión se inicia después de la actual. La transacción genera ReadView, por lo que la transacción actual no puede acceder a esta versión. 4. Si el valor del atributo trx_id de la versión a la que se accede está entre min_trx_id y max_trx_id de ReadView (min_trx_id < trx_id < max_trx_id), entonces debe juzgar si el valor del atributo trx_id está en la lista m_ids. Si es así, significa que se generó cuando se creó ReadView La transacción de la versión aún está activa y no se puede acceder a esta versión, si no, significa que la transacción que generó esta versión se ha confirmado cuando se creó ReadView y se puede acceder a esta versión.

Independientemente de si la transacción T2 se abre antes que la transacción T1, la transacción T1 no puede ver la confirmación de T2. Analícelo usted mismo de acuerdo con la cadena de versiones, ReadView y las reglas para juzgar la visibilidad presentadas anteriormente. Sin embargo, bajo el nivel de aislamiento de LECTURA REPETIBLE, MVCC en InnoDB puede evitar en gran medida la lectura fantasma, en lugar de prohibirla por completo. ¿Qué está sucediendo? Veamos la siguiente situación:

Primero en la transacción T1:

seleccione * del maestro donde el número = 30;

Obviamente, el registro con el número = 30 no se puede encontrar en este momento. En la transacción T2, ejecutamos:

insertar en los valores del maestro (30, 'Leopard', 'data lake');

Al ejecutar insertar en los valores del maestro (30, 'Leopard', 'Data Lake'), insertamos un registro con el número = 30 en la tabla. En este momento, regrese a la transacción T1 y ejecute:

actualice el conjunto de maestros dominio = 'RocketMQ' donde número = 30; 
seleccione * del maestro donde el número = 30;

Bueno, ¿qué está pasando? La transacción T1 obviamente tiene un fenómeno de lectura fantasma. Bajo el nivel de aislamiento de LECTURA REPETIBLE, T1 genera un ReadView cuando ejecuta una declaración SELECT normal por primera vez (pero la cadena de versión no lo hace), luego T2 inserta un nuevo registro en la tabla de profesores y lo envía, y luego T1 también realiza una declaración de actualización. ReadView no puede evitar que T1 ejecute instrucciones UPDATE o DELETE para cambiar el registro recién insertado, pero de esta manera, el valor de la columna oculta trx_id de este nuevo registro se convierte en la identificación de transacción de T1.

Posteriormente, cuando T1 usa la instrucción SELECT ordinaria para consultar este registro, puede ver este registro y devolverlo al cliente. Debido a la existencia de este fenómeno especial, también podemos pensar que MVCC no puede prohibir por completo la lectura fantasma ( es decir, si la primera lectura está vacía y los datos se modifican en su propia transacción ).

1.7, resumen de MVCC

De la descripción anterior, podemos ver que el llamado MVCC (Multi-Version Concurrency Control, control de concurrencia de múltiples versiones) se refiere al uso de dos niveles de aislamiento de READ COMMITTD y REPEATABLE READ para acceder cuando se realizan operaciones SELECT ordinarias. proceso de la cadena de versiones grabadas, de modo que las operaciones de lectura-escritura y escritura-lectura de diferentes transacciones puedan ejecutarse simultáneamente, mejorando así el rendimiento del sistema.

Una gran diferencia entre los dos niveles de aislamiento de READ COMMITTD y REPEATABLE READ es que el tiempo de generación de ReadView es diferente. READ COMMITTD generará un ReadView antes de cada operación SELECT ordinaria, mientras que REPEATABLE READ solo realiza operaciones SELECT ordinarias por primera vez. Generate un ReadView antes, y luego reutilice este ReadView para operaciones de consulta posteriores, de modo que básicamente se pueda evitar la lectura fantasma (es decir, si el ReadView está vacío por primera vez, no se puede evitar en algunos casos ).

Además, el llamado MVCC solo tiene efecto cuando realizamos consultas SEELCT ordinarias.Todas las declaraciones SELECT que hemos visto hasta ahora son consultas ordinarias.En cuanto a lo que es una consulta inusual, es (lectura bloqueada).

Supongo que te gusta

Origin blog.csdn.net/m0_70299172/article/details/130477162
Recomendado
Clasificación