prefacio
Este artículo y los próximos artículos utilizarán con frecuencia varios conocimientos básicos, como InnoDB记录行格式
, 页面格式
, 索引原理
, 表空间的组成
etc. Si no comprende estos aspectos a fondo, la lectura del siguiente texto puede ser particularmente laboriosa. Para garantizar su comprensión normal, asegúrese de haber dominado el conocimiento que hemos aprendido antes.
Tabla de contenido
1. ¿Qué es el registro de rehacer?
Sabemos que InnoDB
el motor de almacenamiento administra el espacio de 页
almacenamiento unidad por unidad, y que las operaciones de adición, eliminación, modificación y consulta que realizamos son en realidad acceso a páginas (incluidas operaciones como lectura de páginas, escritura de páginas y creación de páginas nuevas). páginas). Dijimos Buffer Pool
en el estudio anterior que antes de acceder realmente a la página, la página en el disco debe almacenarse en caché en la memoria Buffer Pool
antes de poder acceder a ella. Pero cuando 事务
estaba estudiando, enfaticé una supuesta 持久性
función, es decir, para una transacción confirmada, incluso si el sistema falla después de confirmar la transacción, los cambios realizados por la transacción en la base de datos no se pueden perder. Buffer Pool
Pero si solo modificamos la página en la memoria, asumiendo que una falla repentinamente ocurre después de que se confirma la transacción, causando que los datos en la memoria dejen de ser válidos, entonces los cambios realizados en la base de datos por la transacción confirmada también seguirán Entonces, ¿cómo asegurar esto 持久性
? Un enfoque muy simple es vaciar todas las páginas modificadas por la transacción en el disco antes de que se confirme la transacción, pero este enfoque simple y tosco tiene algunos problemas:
-
Refrescar una página de datos completa es demasiado despilfarro,
a veces solo modificamos un byte de una página, pero sabemos que vaInnoDB中是以页为单位
a discoIO
, es decir, tenemos que cambiar una página completa de Refresh de memoria a disco. También sabemos que una página tiene 16 KB de forma predeterminada.Obviamente, es demasiado desperdicio actualizar 16 KB de datos en el disco después de modificar solo un byte. -
Random IO es relativamente lento para actualizar.
Una transacción puede contener muchas declaraciones, e incluso una declaración puede modificar muchas páginas. Desafortunadamente, las páginas modificadas por la transacción pueden no ser adyacentes, lo que significa que cuando se modifica una transacción Cuando lasBuffer Pool
páginas en el el disco se actualiza en el disco, se requiere mucho trabajo随机IO
, especialmente para los discos duros mecánicos tradicionales, la E/S aleatoria es mucho más lenta que la E/S secuencial.
¿Qué tengo que hacer? Volviendo a nuestra intención original de nuevo: 我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来
. Por lo tanto, en realidad no necesitamos vaciar todas las páginas modificadas por la transacción en la memoria al disco cada vez que se confirma la transacción. Solo necesitamos, por ejemplo, 把修改了哪些东西记录一下就好
una transacción para compensar la página 100 en el espacio de tabla del sistema por Para cambiar el valor de el byte en 1000 de 1 a 2, solo necesitamos registrar:
Actualice el valor en el desplazamiento 1000 de la página 100 en el tablespace 0 a 2.
De esta manera, actualizamos el contenido anterior en el disco cuando se confirma la transacción. Incluso si el sistema falla más tarde, después de reiniciar, simplemente siga los pasos registrados en el contenido anterior para volver a actualizar la página de datos, luego los cambios realizados por la transacción a la base de datos puede ser restaurada.Recuperar significa cumplir con 持久性
los requisitos. Debido a que la página de datos debe volver a actualizarse de acuerdo con los pasos registrados en el contenido anterior cuando el sistema falla y se reinicia, el contenido anterior también se llama 重做日志
, el nombre en inglés redo log
, también podemos llamarlo redo日志
. En comparación con vaciar todas las páginas en memoria modificadas en el disco cuando se confirma la transacción, los redo
beneficios de vaciar solo los registros generados durante la ejecución de la transacción en el disco son los siguientes:
- El espacio ocupado por el registro de rehacer es muy pequeño.
El espacio de almacenamiento requerido para almacenar el ID del espacio de tabla, el número de página, el desplazamiento y el valor que debe actualizarse es muy pequeño.redo
Hablaremos sobre el formato del registro en detalle más adelante. Ahora solo necesitamos saber un rehacer Sería bueno si el registro no ocupa demasiado espacio. redo
El registro está顺序写入
en el disco.
En el proceso de ejecución de una transacción, cada vez que se ejecuta una declaración, se pueden generar varios registros de rehacer. Estos registros se escriben en el disco en el orden en que se generan, es decir, se utiliza IO secuencial. .
Dos, formato de registro de rehacer
Del contenido anterior, sabemos que redo
el registro esencialmente solo registra qué modificaciones realizó la transacción en la base de datos. InnoDB
Se definen varios tipos de redo
registros para diferentes escenarios de modificación de transacciones a la base de datos, pero la mayoría de los tipos de redo
registros tienen la siguiente estructura general:
La explicación detallada de cada parte es la siguiente:
type
: el tipo del registro de rehacer. InnoDBredo
ha diseñado un total53
de diferentes tipos de registros, que se presentarán en detalleredo
más adelante.space ID
: ID de espacio de tablaspage number
: número de páginadata
: El contenido específico del registro de rehacer
2.1 Tipo de registro de rehacer simple
Como mencionamos anteriormente InnoDB的记录行格式
, si no definimos explícitamente una tabla 主键
y la clave única no está definida en la tabla, InnoDB agregará automáticamente una row_id
columna oculta como la clave principal de la tabla. La forma de asignar un valor a esta columna oculta row_id es la siguiente:
row_id
El servidor mantendrá una variable global en memoria, cada vez que se inserte un registro en una tabla que contiene columnas ocultas , el valor de la variable será considerado comorow_id
el valor de la columna del nuevo registro, y la variable se incrementará automáticamente.1
- Siempre que el valor de esta variable
256
sea un múltiplo de , el valor de la variable se actualizará a表空间的页号为7的页面中
unMax Row ID
atributo llamado sistema (lo mencionamos en detalle cuando presentamos la estructura del espacio de tabla anteriormente) - Cuando el sistema se inicie, cargará los atributos mencionados anteriormente
Max Row ID
en la memoria, agregará el valor y256
lo asignará a la variable global que mencionamos anteriormente (porque el valor de la variable global puede ser mayor que elMax Row ID
valor del atributo en el último apagado)
Max Row ID
El espacio de almacenamiento ocupado por este atributo es 8
bytes Cuando una transacción row_id
inserta un registro en una tabla que contiene columnas ocultas y asigna un row_id
valor al registro que 256
es un múltiplo de Escriba el valor de 8 bytes en el desplazamiento correspondiente de la página. Pero necesitamos saber que esta escritura realmente se Buffer Pool
realiza en , necesitamos registrar un redo
registro para la modificación de esta página, de modo que después de que el sistema se bloquee, se pueda restaurar la modificación realizada por la transacción que se ha confirmado en la página. En este caso, la modificación de la página es extremadamente simple, redo
en el registro 只需要记录一下在某个页面的某个偏移量处修改了⼏个字节的值,具体被修改的内容是啥就好了
, InnoDB
este redo
registro extremadamente simple se llama 物理日志
y hay varios tipos de registros de redo diferentes según la cantidad de datos escritos en la página:
MLOG_1BYTE
(type
El número decimal correspondiente al campo es ): Indica el tipo de registro1
que escribe 1 byte en un cierto desplazamiento de la página .redo
MLOG_2BYTE
(type
El número decimal correspondiente al campo es2
): Indica el tipo de registro que escribe 2 bytes en un cierto desplazamiento de la páginaredo
.MLOG_4BYTE
(type
El número decimal correspondiente al campo es4
): Indica el tipo de registro que escribe 4 bytes en un cierto desplazamiento de la páginaredo
.MLOG_8BYTE
(type
El número decimal correspondiente al campo es ): Indica el tipo de registro8
que escribe 8 bytes en un determinado desplazamiento de la página .redo
MLOG_WRITE_STRING
(type
El número decimal correspondiente al campo es30
): Indica que una cadena de datos se escribe en un cierto desplazamiento de la página.
El atributo que mencionamos anteriormente Max Row ID
en realidad ocupa 8 bytes de espacio de almacenamiento, por lo que al modificar este atributo en la página, se registrará un registro de tipo MLOG_8BYTE
, redo
y MLOG_8BYTE
la redo
estructura del registro es la siguiente:
La estructura de registro de los otros tipos MLOG_1BYTE
, MLOG_2BYTE
y es similar a la de , excepto que los datos específicos contienen los bytes de datos correspondientes. El tipo de registro indica que se escribe una cadena de datos, pero debido a que no es posible determinar cuántos bytes ocupan los datos específicos escritos, es necesario agregar un campo a la estructura del registro:MLOG_4BYTE
redo
MLOG_8BYTE
MLOG_WRITE_STRING
redo
len
小提示:
Siempre que el campo len del registro de rehacer de tipo MLOG_WRITE_STRING esté lleno con los números 1, 2, 4 y 8, puede reemplazar los registros de rehacer de MLOG_1BYTE, MLOG_2BYTE, MLOG_4BYTE y MLOG_8BYTE respectivamente. ¿muchos tipos? No es por ahorrar espacio, si puede escribir el campo len sin escribir el campo len, un byte guardado cuenta como un byte.
2.2 Tipos de registros de rehacer más complejos
A veces, ejecutar una declaración modificará muchas páginas, incluidas las páginas de datos del sistema y las páginas de datos del usuario (los datos del usuario se refieren a 聚簇索引
y ⼆级索引
corresponden a B+树
). Tome una INSERT
declaración como ejemplo. Además de B+
insertar datos en la página del árbol, también puede actualizar Max Row ID
el valor de los datos del sistema. Sin embargo, para nuestros usuarios, generalmente nos preocupamos más por B+树
la actualización realizada por la declaración:
- Cuántos índices se incluyen en la tabla y cuántos árboles B+ se pueden actualizar con una instrucción INSERT
- Para un determinado árbol B+, es posible actualizar la página del nodo hoja, actualizar la página del nodo interno o crear una nueva página (cuando el espacio restante del nodo hoja donde se inserta el registro es relativamente pequeño y no es suficiente para almacenar el registro, la página se dividirá y los registros de entrada de directorio se agregarán a la página del nodo interno)
Durante la ejecución de la sentencia, INSERT
la modificación de todas las páginas por parte de la sentencia debe guardarse en el servidor redo日志
. Esta oración es relativamente fácil de decir, pero es más difícil hacerlo. Por ejemplo, al insertar un registro en el índice agrupado, si el espacio restante del nodo hoja ubicado es suficiente para almacenar el registro, entonces solo se actualiza la página. del nodo hoja es suficiente Ok, entonces solo registre un MLOG_WRITE_STRING
tipo de redo
registro, indicando qué datos se agregaron en un cierto desplazamiento en la página. Eso es demasiado ingenuo~ No olvide que además de almacenar los registros reales, hay otras partes como F ile Header
, Page Header
y Page Directory
así sucesivamente en una página de datos (los detalles se explican en el capítulo sobre páginas de datos de aprendizaje), por lo que cada página de datos representado por un nodo de hoja Cuando se inserta un registro en , hay muchos otros lugares que se actualizarán, como:
Page Directory
Información de slots en posibles actualizaciones .Page Header
Varias estadísticas de página en ,PAGE_N_DIR_SLOTS
la cantidad de espacios representados puede cambiar,PAGE_HEAP_TOP
la dirección mínima del espacio no utilizado representado puede cambiar,PAGE_N_HEAP
la cantidad de registros representados en esta página puede cambiar y se puede modificar información diversa.- Sabemos que los registros en la página de datos forman una lista enlazada unidireccional de acuerdo con el orden de las columnas del índice de menor a mayor Cada vez que se inserta un registro, los atributos en la información del encabezado del registro anterior deben ser actualizado para mantener esta lista enlazada unidireccional
next_record
. - Hay otros lugares para actualizar, así que no voy a molestar...
Dibuja un esquema simple como este:
Habiendo dicho tanto, solo quiero expresar: 把一条记录插入到一个页面时需要更改的地方非常多
. En este momento, si usamos el registro de rehacer físico simple presentado anteriormente para registrar estas modificaciones, hay dos soluciones:
- Solución 1: Registre un registro para cada modificación
redo
. Es decir, como se muestra en la figura anterior, escriba tantos registros de rehacer físicos como bloques en negrita haya. La desventaja de grabar registros de rehacer de esta manera es obvia, porque hay demasiados lugares modificados y el espacio ocupado por los registros de rehacer grabados puede ser mayor que el espacio ocupado por la página completa. 第一个被修改的字节到最后一个修改的字节
Solución 2: trate todos los datos entre la página completa como datos específicos en un registro de rehacer físico. También se puede ver en la figura que todavía hay una gran cantidad de datos sin modificar entre el primer byte modificado y el último byte modificado. ¿No agregaríamos estos datos sin modificar al registro de rehacer? Qué desperdicio ~
Debido a que los dos redo日志
métodos anteriores de usar métodos físicos para registrar qué cambios se han realizado en una determinada página son bastante derrochadores, al diseñar InnoDB, con la intención original de ahorro y frugalidad, se proponen algunos nuevos tipos de registros de rehacer, como:
-
MLOG_REC_INSERT
(El número decimal correspondiente es 9): indica el tipo de registro de rehacer al insertar un registro que utiliza un formato de fila no compacto. -
MLOG_COMP_REC_INSERT
(El número decimal correspondiente es 38): indica el tipo de registro de rehacer al insertar un registro utilizando el formato de fila compacto.小提示:
Redundante es un formato de fila relativamente primitivo, que no es compacto. Los formatos de fila Compacto, Dinámico y Comprimido son formatos de fila más nuevos que son compactos (ocupan menos espacio de almacenamiento) -
MLOG_COMP_PAGE_CREATE
(El número decimal correspondiente al campo de tipo es 58): indica el tipo de registro de rehacer que crea una página que almacena registros en formato de fila compacto. -
MLOG_COMP_REC_DELETE
(El número decimal correspondiente al campo de tipo es 42): Indica que se debe eliminar un tipo de registro de rehacer registrado en formato de fila compacta. -
MLOG_COMP_LIST_START_DELETE
(El número decimal correspondiente al campo de tipo es 44): Indica que se elimine una serie de tipos de registros de rehacer grabados en el formato de fila compacto de un registro dado en la página. -
MLOG_COMP_LIST_END_DELETE
(El número decimal correspondiente al campo tipo es 43):MLOG_COMP_LIST_START_DELETE
Corresponde al redo log del tipo, indicando borrar una serie de registros hastaMLOG_COMP_LIST_END_DELETE
el registro correspondiente al redo log del tipo.小提示:
Cuando hablamos sobre el formato de la página de datos de InnoDB, enfatizamos que los registros en la página de datos forman una lista vinculada unidireccional en el orden del tamaño de la columna del índice. A veces necesitamos eliminar todos los registros cuyos valores de columna de índice están dentro de un cierto rango. En este momento, si escribimos un registro de rehacer cada vez que eliminamos un registro, la eficiencia puede ser un poco baja, por lo que proponemos MLOG_COMP_LIST_START_DELETE y MLOG_COMP_LIST_END_DELETE tipos de registros de rehacer pueden reducir en gran medida la cantidad de registros de rehacer. -
MLOG_ZIP_PAGE_COMPRESS
(El número decimal correspondiente al campo de tipo es 51): Indica el tipo de registro de rehacer para comprimir una página de datos. -
······Hay muchos, muchos tipos, así que no los enumeraré aquí, hablaré de ellos cuando los use~
Estos tipos de registros de rehacer incluyen significados tanto físicos como lógicos, específicamente:
- A nivel físico, estos registros indican en qué página se ha modificado el tablespace.
- Desde una perspectiva lógica, cuando el sistema falla y se reinicia, no es posible restaurar directamente un cierto desplazamiento en la página a ciertos datos en función de los registros en estos registros. En su lugar, necesita llamar a algunas funciones preparadas previamente. después de estas funciones, se puede restaurar la página a la forma en que estaba antes de que el sistema colapsara.
Puede ver que esto puede ser un poco confuso.Tomemos el MLOG_COMP_REC_INSERT
registro de rehacer cuando se inserta un registro que usa el formato de fila compacta para este tipo como ejemplo para comprender lo que queremos decir con el nivel físico y el nivel lógico que mencionamos anteriormente. No hablemos de tonterías, basta con fijarse en MLOG_COMP_REC_INSERT
la estructura de este tipo de redo log (como son demasiados campos, es mejor verlos en vertical):
MLOG_COMP_REC_INSERT
Hay varios lugares en este tipo de estructura de registro de rehacer que requieren su atención:
-
Dijimos anteriormente, cuando estábamos aprendiendo sobre los índices, que en una página de datos, ya sea un nodo hoja o un nodo no hoja, los registros se ordenan en orden ascendente de las columnas del índice. Para los índices secundarios, cuando los valores de las columnas del índice son los mismos, los registros también deben ordenarse según el valor de la clave principal. El significado del valor de la figura
n_uniques
es que en un registro se requieren los valores de varios campos para asegurar la unicidad del registro, de manera que cuando se inserta un registro se pueden_uniques
ordenar según el campo anterior del registro. Para los índices agrupados,n_uniques
el valor es el número de columnas en la clave principal y para otros índices secundarios, el valor es el索引列数+主键列
número de columnas. Cabe señalar aquí que el valor del índice secundario único puede serNULL
, por lo que el valor sigue siendo索引列数+主键列数
. -
field1_len ~ fieldn_len
Representa el tamaño del espacio de almacenamiento que ocupan varios campos del registro, cabe señalar que, independientemente de si el tipo de campo es de longitud fija (por ejemplo)INT
o de longitud variable (por ejemploVARCHAR(M)
), el tamaño ocupado por el campo Escribir siempre en el registro de rehacer. -
offset
Representa la dirección del registro anterior de este registro en la página. ¿Por qué registrar la dirección del registro anterior? Esto se debe a que cada vez que inserta un registro en una página de datos, debe modificar la lista de registros que se mantiene en la página. La información del encabezado del registro de cada registro contiene un atributo llamado , por lo que al insertar un nuevo registro, debe modificar elnext_record
propiedades del registro anteriornext_record
. -
Sabemos que un registro en realidad se compone de
额外信息
dos真实数据
partes, y el tamaño total de estas dos partes es el tamaño total del espacio de almacenamiento ocupado por un registro. El valor pasadoend_seg_len
puede calcular indirectamente el tamaño total del espacio de almacenamiento ocupado por un registro. ¿Por qué no almacenar directamente el tamaño total del espacio de almacenamiento ocupado por un registro? Esto se debe a que escribirredo
un registro es una operación muy frecuente.InnoDB
Tratando de reducirredo
el espacio de almacenamiento ocupado por el propio registro, pensé en algunos algoritmos intrincados para lograr este objetivo.end_seg_len
Este campo se propone para ahorrar el espacio de almacenamiento del registro de rehacer de . -
mismatch_index
El valor de también se establece para guardar el tamaño del registro de rehacer, puede ignorarlo.
Obviamente, este tipo de MLOG_COMP_REC_INSERT
registro redo
no registra PAGE_N_DIR_SLOTS
para qué se modificó el valor, PAGE_HEAP_TOP
para qué se modificó el valor, PAGE_N_HEAP
para qué se modificó el valor, etc., sino que simplemente se anotan todos los elementos necesarios para insertar un registro en esta página, cuando el sistema falla y se reinicia más tarde, el servidor llamará a la función relacionada con la inserción de un registro en una página, y redo
los datos en el registro se pueden considerar como los parámetros necesarios para llamar a esta función.Después de llamar a la función, la página Los valores de PAGE_N_DIR_SLOTS
, PAGE_HEAP_TOP
, PAGE_N_HEAP
y así sucesivamente se restaurará al estado anterior a la falla del sistema. Esto es lo que se entiende por un llamado registro lógico.
2.3 Resumen del formato de registro de rehacer
Aunque una gran cantidad de contenido sobre el formato de registro se ha mencionado anteriormente redo
, si no está escribiendo una herramienta para analizar registros de rehacer o desarrollar un sistema de registro de rehacer usted mismo, entonces no hay necesidad de convertir varios tipos de registros de rehacer en InnoDB El formato es estudiado a fondo, no hay necesidad de eso. Arriba acabo de presentar simbólicamente varios tipos de formatos de registro de rehacer, el propósito es que todos entiendan: redo日志会把事务在执行过程中对数据库所做的所有修改都记录下来,在之后系统奔溃重启后可以把事务所做的任何修改都恢复出来
.
小提示:
Para ahorrar el espacio de almacenamiento ocupado por los registros de rehacer, el tío que diseñó InnoDB también puede comprimir algunos datos en los registros de rehacer. Por ejemplo, el ID de espacio y el número de página generalmente ocupan 4 bytes para el almacenamiento, pero después de la compresión, pueden usarse Menos espacio Almacenar. No hablaré sobre el algoritmo de compresión específico.
3. Minitransacción
3.1 Escribir registros de rehacer en forma de grupos
Una sentencia puede modificar varias páginas durante su ejecución. Por ejemplo, una declaración que mencionamos anteriormente INSERT
puede modificar los atributos de la página cuyo número de página es 7 en el tablespace del sistema Max Row ID
(por supuesto, también puede actualizar otras páginas del sistema, pero no las hemos enumerado todas), y también actualizará 更新聚簇索引和二级索引对应B+树
el paginas Dado que todos los cambios en estas páginas ocurren en Buffer Pool
, después de modificar las páginas, debe registrar los redo
registros correspondientes. El log generado durante la ejecución de la sentencia redo
es dividido artificialmente en varios por el tío que diseñó InnoDB 不可分割的组
, como por ejemplo:
Max Row ID
El registro de rehacer generado al actualizar los atributos es indivisible.聚簇索引对应B+树的页面中插入一条记录
El registro de rehacer generado en el tiempo de reenvío es indivisible .某个二级索引对应B+树的页面中插入一条记录
El registro de rehacer generado en el tiempo de reenvío es indivisible .- Hay otros registros de rehacer generados durante las operaciones de acceso a la página que son inseparables. . .
¿Cómo entender el significado de la indivisibilidad? Tomemos como ejemplo la inserción de un registro en el árbol B+ correspondiente a un índice. Antes de insertar este registro en el árbol B+, debemos ubicar la página de datos representada por el nodo hoja donde se debe insertar este registro y ubicar el After específico. la página de datos, hay dos situaciones posibles:
-
Situación 1: el espacio libre restante de la página de datos es suficiente para acomodar el registro que se insertará, entonces el asunto es muy simple, simplemente inserte el registro en la página de datos directamente y registre un registro de rehacer de tipo , ponemos esto
MLOG_COMP_REC_INSERT
Esto se llama situación乐观插入
. Si el árbol B+ correspondiente a un índice se ve así:Ahora queremos insertar un
10
registro con un valor de clave, que obviamente debe insertarse en页b
Dado que la página ahora tiene suficiente espacio para acomodar un registro, es buenob
insertar el registro directamente en la página , así:b
-
Situación 2: el espacio libre restante de la página de datos es insuficiente, entonces las cosas serán trágicas.Como dijimos antes, en este caso, debemos realizar el llamado, es decir, crear un nuevo nodo de hoja y luego
页分裂操作
copiar algunos registros en la página de datos original A esta nueva página de datos, luego inserte el registro, inserte este nodo de hoja en la lista vinculada de nodos de hoja y finalmente agregue un punto一条目录项记录
a esta página recién creada en el nodo interno. Obviamente, este proceso necesita modificar varias páginas, lo que significa queredo
se generarán varios registros, lo que llamamos esta situación悲观插入
. Si el árbol B+ correspondiente a un índice se ve así:Ahora queremos insertar un
10
registro con un valor clave, que obviamente debe insertarse en页b
, pero también se puede ver en la figura que en este momento页b已经塞满了记录
no hay más espacio libre para acomodar este nuevo registro, por lo que debemos realizar página La operación de división, así:
Si页a
el espacio libre restante como nodo interno no es suficiente para acomodar el aumento加一条目录项记录
, debe continuar siendo un nodo interno页a的分裂操作
, lo que significa que se modificarán más páginas y, por lo tanto, se generarán másredo
registros. Además,悲观插入
debido a la necesidad de solicitar nuevas páginas de datos, es necesario modificar algunas páginas del sistema, por ejemplo, para modificar la información estadística de varios segmentos y áreas, y la información estadística de varias listas vinculadas (como qué estamos hablando del espacio de tablaFREE链表、FSP_FREE_FRAG链表
(todo tipo de cosas introducidas en ese capítulo) y así sucesivamente, de todos modos, hay 20 o 30 registros de rehacer que deben registrarse.小提示:
De hecho, no solo insertar un registro de manera pesimista generará muchos registros de rehacer, sino que también para algunas otras funciones al diseñar InnoDB, también se pueden generar múltiples registros de rehacer al insertar de manera optimista (no diremos más sobre las funciones específicas, de lo contrario el espacio No puedo soportarlo más~).
A la hora de diseñar InnoDB
, se considera que el proceso de inserción en el árbol B+ correspondiente a un determinado índice 一条记录
debe ser atómico, y no se puede decir que se detendrá a la mitad de la inserción. Por ejemplo, en el proceso de inserción pesimista, se ha asignado la nueva página, se han copiado los datos y se ha insertado el nuevo registro en la página, pero no se ha insertado en el nodo interno. Este proceso de inserción está incompleto, que 一条目录项记录
formará un árbol B+ incorrecto. Sabemos que redo
el registro es para restaurar el estado anterior al bloqueo cuando el sistema falla y se reinicia. Si solo se registra una parte del registro de rehacer durante el proceso de inserción pesimista, entonces el árbol B+ correspondiente al índice se restaurará a un estado incorrecto. estado cuando el sistema falla y se reinicia estado, que es insoportable cuando se diseñó InnoDB. Por lo tanto, estipulan que al realizar estas operaciones que deben garantizar la atomicidad, los registros de rehacer deben registrarse en forma de grupos 针对某个组中的redo日志,要么把全部的日志都恢复掉,要么一条也不恢复
. ¿Cómo lo hiciste? Esta situación de puntuación se discute:
-
Algunas
原子性
operaciones que deben garantizarse generarán variosredo
registros. Por ejemplo, una inserción pesimista en el árbol B+ correspondiente a un índice debe generar muchos registros de rehacer. ¿Cómo dividir estos registros de rehacer en un grupo? Al diseñar InnoDB, hice un truco muy simple, que consiste en agregar un tipo especial deredo
registro después del último registro de rehacer en el grupo. El nombre del tipo esMLOG_MULTI_REC_END
, y la estructura del registrotype字段对应的十进制数字为31
de este tipo es muy simple, con un solo campo . : por lo tanto, una serie de registros de rehacer generados por una operación que debe garantizarse como atómica debe terminar con un tipo, como este:redo
type
MLOG_MULTI_REC_END
-
De esta forma, cuando el sistema falla y se reinicia para recuperarse, solo cuando se
MLOG_MULTI_REC_END
analiza el registro de rehacer de tipo, se considera que se ha analizado un conjunto completo de registros de rehacer y se realizará la recuperación. De lo contrario, abandone el registro de rehacer analizado anteriormente. -
Algunas operaciones que necesitan asegurar la atomicidad solo generan un
redo
registro, por ejemplo,Max Row ID
la operación de actualización de atributos solo genera un registro de rehacer.De hecho,
MLOG_MULTI_REC_END
también es posible seguir un registro con un tipo de registro de rehacer, peroInnoDB
es más económico y no quieren desperdiciar un poco. No olvides que aunqueredo
hay muchos tipos de registros, hay decenas de ellos, que son más pequeños que127
este número, es decir, usamos 7 bits para cubrir todosredo
los tipos de registro, y el campo de tipo en realidad ocupa1
1 palabra. En otras palabras, podemos ahorrar un poco para indicar que la operación que necesita asegurar la atomicidad solo genera un únicoredo
registro, como se muestra en el diagrama esquemático: -
Si
type
el primer bit del campo es1
, significa que la operación que necesita garantizar la atomicidad solo genera un único registro de rehacer; de lo contrario, significa que la operación que necesita garantizar la atomicidad genera una serie de registros de rehacer.
3.2 El concepto de Mini-Transacción
El proceso de acceso atómico de MySQL a la página subyacente se llama uno Mini-Transaction
, mtr
por ejemplo, el valor modificado una vez mencionado anteriormente Max Row ID
es uno Mini-Transaction
, y el proceso de insertar un registro en el árbol B+ correspondiente a un índice también es uno Mini-Transaction
. A través de la descripción anterior, también sabemos que un llamado mtr可以包含一组redo日志,在进行奔溃恢复时这一组redo日志作为一个不可分割的整体
.
Una transacción puede contener varias declaraciones, y cada declaración en realidad se compone de varias declaraciones mtr
, cada una de las cuales mtr
puede contenerlas 若干条redo日志
. Haz un dibujo para mostrar su relación de esta manera:
4. El proceso de escritura del registro de rehacer
4.1 bloque de registro de rehacer
InnoDB
Para recuperarse mejor de los bloqueos del sistema durante el diseño , colocan los registros mtr
generados por el paso en páginas con un tamaño de un byte. Para distinguirlo de las páginas del espacio de tabla que mencionamos anteriormente, llamamos aquí a las páginas utilizadas para almacenar registros (ya sabe que el significado de páginas y bloques es casi el mismo). Un diagrama esquemático es el siguiente:redo
512
redo
block
redo log block
Todos los registros reales redo
se almacenan en 496
el tamaño de los bytes , y los y almacenados log block body
en la figura son información de administración. Veamos qué son estos llamados datos de gestión:log block header
log block trailer
Los significados de varias de estas log block header
propiedades son los siguientes:
-
LOG_BLOCK_HDR_NO
: Cada bloque tiene una etiqueta única mayor que 0, y este atributo indica el valor de la etiqueta.Este atributo se
block
asigna cuando se usa por primera vez ylsn
está relacionado con el valor del sistema en ese momento. Use la siguiente fórmula para calcular elblock
valorLOG_BLOCK_HDR_NO
:((lsn / 512) & 0x3FFFFFFFUL) + 1
esta fórmula0x3FFFFFFFUL
puede confundir a todos, pero su representación binaria puede ser más amigable:Se puede ver en la figura que
0x3FFFFFFFUL
los primeros 2 bits del número binario correspondiente son 0, y los valores de los últimos 30 bits son todos 1. Cuando comenzamos a aprender computación, aprendimos que(&)
el resultado de una operación AND entre un bit binario y 0 es definitivamente 0, y(&)
el resultado de una operación AND entre un bit binario y 1 es el valor original. Hacer una0x3FFFFFFFUL
operación AND significa poner el valor de los 2 primeros bits del valor a 0, de modo que el valor debe ser menor o igual que él0x3FFFFFFFUL
. Esto también muestra que no importa cuán grande sea lsn,((lsn / 512) & 0x3FFFFFFFUL)
el valor de lsn debe estar0~0x3FFFFFFFUL
entre, y si agrega 1, debe estar1~0x40000000UL
entre. Y0x40000000UL
este valor debería ser familiar para todos, este valor representa 1GB. Es decir, el sistema puede generar como máximo un únicoLOG_BLOCK_HDR_NO
valor1GB
. El diseñoInnoDB
estipularedo
que el tamaño total de todos los archivos contenidos en el grupo de archivos de registro no debe exceder512GB
, y unblock
tamaño es512
bytes, es decir, el número máximo de bloquesredo
contenidos en el grupo de archivos de registro es , por lo que un número que no se repite el valor es suficiente.block
1GB
1GB
Además,
LOG_BLOCK_HDR_NO
el primer bit del valor es especial, por lo queflush bit
si el valor es 1, significa que este bloque es el primero que se vacía en una operación que vacía el bloque del búfer de registro al bloque del disco. -
LOG_BLOCK_HDR_DATA_LEN
: indica cuántos bytes se han utilizado en el bloque初始值为12
(porque el cuerpo del bloque de registro comienza en el byte 12). A medida que se escriben más y más registros de rehacer en el bloque, el valor de este atributo también aumenta. silog block body已经被全部写满,那么本属性的值被设置为512
-
LOG_BLOCK_FIRST_REC_GROUP
: Un registro de rehacer también se puede llamar registro de rehacer (redo logrecord
), y un mtr producirá múltiples registros de redo, y estos registros de redo se llaman一个redo日志记录组
(redo log record group
)。LOG_BLOCK_FIRST_REC_GROUP
para representar el primer grupo de registro de registromtr
generado en el bloque Offset (de hecho,redo
es el desplazamiento delblock
primer registro generado por el primer mtr aquí ).redo
-
LOG_BLOCK_CHECKPOINT_NO
: Indica el llamadocheckpoint
número de serie,checkpoint
que es el foco de nuestro contenido de seguimiento. No es necesario aclarar su significado ahora, así que no se impaciente.
log block trailer
Los significados de los atributos son los siguientes:
LOG_BLOCK_CHECKSUM
: Indica el valor de verificación del bloque, que se utiliza para la verificación de corrección, y no nos importa por el momento
4.2 búfer de registro de rehacer
Como decíamos antes, InnoDB fue diseñado para solucionar el problema 磁盘速度过慢的问题而引入了Buffer Pool
. De la misma manera, 写入redo日志时也不能直接直接写到磁盘上,实际上在服务器启动时就向操作系统申请了一大⽚称之为redo log buffer的连续内存空间
traducida al chino es redo日志缓冲区
, también podemos llamarlo de forma abreviada log buffer
. Este espacio de memoria se divide en varios contiguos redo log block
, así:
Podemos innodb_log_buffer_size
especificar log buffer
el tamaño a través del parámetro de inicio, el valor predeterminado del parámetro de inicio es 16MB
.
mysql> show variables like 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name | Value |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
1 row in set (0.01 sec)
Los búferes de registro grandes permiten que se ejecuten transacciones grandes sin escribir el registro en el disco antes de que se confirme la transacción. Por lo tanto, si tiene transacciones que actualizan, insertan o eliminan muchas filas, aumentar el búfer de registro puede ahorrar E/S de disco.
mysql> set persist innodb_log_buffer_size =33554432;
Query OK, 0 rows affected (0.04 sec)
4.3 El registro de rehacer se escribe en el búfer de registro
log buffer
El proceso de escritura de registros en redo
el medio es secuencial, es decir, block
primero se escribe en el medio anterior y luego se escribe block
en el medio siguiente cuando se agota el espacio libre del medio correspondiente . block
Cuando queremos escribir registros log buffer
en , proporcionamos especialmente una variable global llamada , que indica dónde deben escribirse los registros de rehacer subsiguientes, como se muestra en la figura:redo
第一个遇到的问题就是应该写在哪个block的哪个偏移量处
InnoDB
buf_free
log buffer
Dijimos anteriormente que se pueden generar mtr
varios registros durante un proceso de ejecución, y estos registros son un grupo inseparable, por lo que, de hecho, no se inserta en el registro cada vez que se genera un registro , pero los registros generados durante cada proceso en ejecución son los primeros. Almacénelo temporalmente en un lugar y, cuando llegue el momento de finalizar, copie todos los registros generados durante el proceso en un archivo . Supongamos ahora que hay dos transacciones denominadas , cada una de las cuales incluye , nombremos estos mtrs:redo
redo
redo
logbuffer
mtr
mtr
redo
log buffer
T1
T2
2个mtr
事务T1
Los dosmtr
se llamanmtr_T1_1
ymtr_T1_2
事务T2
Los dosmtr
se llamanmtr_T2_1
ymtr_T2_2
Cada uno mtr
generará un conjunto de redo
registros y utilizará un diagrama esquemático para describir los mtr
registros generados:
Diferentes transacciones pueden ser 并发执行
sí, por lo que el mtr entre T1 y T2 puede ser 交替执行
sí. 每当一个mtr执行完成时,伴随该mtr生成的一组redo日志就需要被复到log buffer 中
, es decir, los mtr de diferentes transacciones pueden escribirse alternativamente log buffer
Dibujemos un diagrama esquemático (por el bien de la belleza, dibujamos todos los redo logs generados en un mtr como un todo):
En el diagrama esquemático, podemos ver que mtr
el espacio de almacenamiento ocupado por diferentes conjuntos de registros de rehacer puede ser diferente. Algunos mtr generan una pequeña cantidad de registros de redo, mientras que algunos mtr generan una gran cantidad de registros de redo.
Cinco, rehacer registro
5.1 Momento de vaciar los registros de rehacer
Anteriormente dijimos que un conjunto de registros mtr
generados durante el proceso en ejecución se copiarán en el servidor al final , pero no es una buena idea mantener estos registros en la memoria. En algunos casos, se descargarán en el disco, por ejemplo. :redo
mtr
log buffer
log buffer空间不足时
: El tamaño del búfer de registro es limitado (innodb_log_buffer_size
especificado por las variables del sistema), si continúa agregando registros a este búfer de registro de tamaño limitado, se llenará pronto.InnoDB
Se considera que si es actual写入log buffer的redo日志量已经占满了log buffer总容量的大约一半左右
, estos registros deben vaciarse en el disco.事务提交时
: Como dijimos anteriormente, la razón por la queredo
se usa el registro es principalmente porque ocupa menos espacio y todavía se escribe secuencialmente. Cuando se confirma la transacción, las páginas可以不把
modificadasBuffer Pool
se vacían en el disco. Sin embargo, para garantizar la persistencia , los registros de redo correspondientes a estas páginas deben modificarse Flush to disk.后台线程不停的刷
:
Hay unMaster Thread
subproceso en segundo plano, que descarga los registros en el disco aproximadamente una vez porlog buffer
segundoredo
.- Al apagar el servidor correctamente
- Al hacer lo que se llama
checkpoint
(no hemos introducido el concepto de punto de control ahora, hablaremos de eso con cuidado más adelante, no se impaciente) - Algunas otras situaciones...
5.2 grupo de archivos de registro de rehacer
MySQL
SHOW VARIABLES LIKE 'datadir'
De forma predeterminada, hay dos archivos con nombre ib_logfile0
y en el directorio de datos (usar vista), y los inicios de sesión se actualizan en estos dos archivos de disco de forma predeterminada. Si no estamos satisfechos con el archivo de registro predeterminado, podemos ajustarlo a través de los siguientes parámetros de inicio:ib_logfile1
log buffer
redo
innodb_log_group_home_dir
: este parámetro especifica el directorio donde se encuentra el archivo de registro de rehacer y el valor predeterminado es el directorio de datos actual.innodb_log_file_size
: este parámetro especifica el tamaño de cada archivo de registro de rehacer, el valor predeterminado es48MB
innodb_log_files_in_group
: este parámetro especificaredo
el número de archivos de registro,默认值为2
,最大值为100
.
redo
Como se puede ver en la descripción anterior, no solo hay un archivo de registro en el disco , sino 一个日志文件组
en forma de . estos archivos 以ib_logfile[数字](数字可以是0、1、2...)的形式进行命名
. Al escribir el registro de rehacer en el grupo de archivos de registro, se ib_logfile0
escribe desde el principio. Si ib_logfile0
está lleno, continuará escribiendo ib_logfile1
. De manera similar, si ib_logfile1
está lleno, se escribirá ib_logfile2
y así sucesivamente. ¿Qué pasa si el último archivo está escrito? Luego regrese para ib_logfile0
continuar escribiendo, por lo que todo el proceso se muestra en la siguiente figura:
El redo
tamaño total del archivo de registro es en realidad:innodb_log_file_size × innodb_log_files_in_group
小提示:
Si los datos se escriben en el grupo de archivos de registro de rehacer de forma circular, ¿no sería final, es decir, el registro de rehacer escrito más tarde sobrescribirá el registro de redo escrito antes? ¡Claro que es posible! Así quecheckpoint
el concepto propuesto por InnoDB, nos centraremos en explicarlo más adelante~
5.3 formato de archivo de registro de rehacer
Dijimos anteriormente que log buffer
es esencialmente un espacio de memoria continuo, que se divide en varios 512
tamaños de bytes block
. La esencia de actualizar el registro en log buffer
el disco redo
al disco es block
escribir la imagen del archivo de registro en el archivo de registro, por lo que redo
el archivo de registro en realidad se compone de varios 512
bytes de tamaño block
. redo
Cada archivo del grupo de archivos de registro tiene el mismo tamaño y formato, y consta de dos partes:
- El primer
2048
byte, es decir, el primero4个block
se utiliza para almacenar alguna información de gestión 从第2048字节往后
se utiliza paralog buffer中的block镜像
el almacenamiento
Entonces, el uso circular de los archivos de registro de rehacer que mencionamos anteriormente en realidad se calcula a partir del byte 2048 de cada archivo de registro. Dibuje un diagrama esquemático como este:
Ya mencionamos el formato común block
cuando regañamos a , es decir , las tres partes de , y , por lo que no repetiremos la introducción. Aquí tenemos que presentar cada uno , es decir, cuáles son los formatos de los primeros 4 bloques especiales, no hablemos tonterías, veamos primero la imagen:log buffer
log block header
log block body
log blocktrialer
redo日志文件前2048个字节
Como puede verse en la figura, los cuatro bloques son:
encabezado del archivo de registro : describa algunas propiedades generales del archivo de registro de rehacer, echemos un vistazo a su estructura:
La interpretación específica de cada atributo es la siguiente:
Nombre del Atributo | Longitud (unidad: byte) | describir |
---|---|---|
LOG_HEADER_FORMAT | 4 | La versión del registro de rehacer, el valor siempre es 1 |
LOG_HEADER_PAD1 | 4 | Se utiliza para el llenado de bytes, no tiene ningún significado práctico, ignore ~ |
LOG_HEADER_START_LSN | 8 | Marque el valor de LSN al comienzo de este archivo de registro de rehacer, es decir, el valor de LSN correspondiente al comienzo del desplazamiento del archivo de 2048 bytes (veremos qué es LSN más adelante, ignórelo si no lo entiende) |
LOG_HEADER_CREATOR | 32 | Una cadena que identifica quién es el creador de este archivo de registro de rehacer. Este valor es el número de versión de MySQL durante el funcionamiento normal, por ejemplo: "El valor del archivo de registro de rehacer creado por MySQL mediante el comando mysqlbackup es "ibbackup" y la hora de creación. |
LOG_BLOCK_CHECKSUM | 4 | El valor de verificación de este bloque, todos los bloques lo tienen, no nos importa |
小提示:
InnoDB ha modificado el formato de bloque del registro de rehacer muchas veces. Si encuentra que los atributos anteriores son diferentes de los atributos en los libros que lee en otros libros, no entre en pánico. Esto es normal. Además, presentaremos el Valor de LSN más adelante. , Ahora no te preocupes por qué es LSN.
checkpoint1: registre algunos atributos sobre el punto de control, mire su estructura:
La interpretación específica de cada atributo es la siguiente:
Nombre del Atributo | Longitud (unidad: byte) | describir |
---|---|---|
LOG_CHECKPOINT_NO | 8 | El número del punto de control del servidor, cada vez que se realiza un punto de control, el valor se incrementa en 1. |
LOG_CHECKPOINT_LSN | 8 | El valor LSN correspondiente al final del punto de control del servidor. Cuando el sistema falla y se recupera, comenzará desde este valor. |
LOG_CHECKPOINT_OFFSET | 8 | El desplazamiento del valor LSN en el atributo anterior en el grupo de archivos de registro de rehacer |
LOG_CHECKPOINT_LOG_BUF_SIZE | 8 | El tamaño del búfer de registro correspondiente cuando el servidor realiza operaciones de punto de control |
LOG_BLOCK_CHECKSUM | 4 | El valor de verificación de este bloque, todos los bloques lo tienen, no nos importa |
小提示:
Es normal no entender las explicaciones anteriores sobre los atributos del punto de control y LSN. Solo quiero que todos estén familiarizados con los atributos anteriores, y hablaremos de ellos en detalle más adelante.
El tercer bloque : sin usar, ignorar ~
checkpoint2 : La estructura es la misma que checkpoint1
六、Número de secuencia de registro
Desde que el sistema comienza a ejecutarse, la página se modifica constantemente, lo que significa que redo
los registros se generan constantemente. redo
La cantidad de registros aumenta constantemente, al igual que la edad de una persona, ha aumentado desde su nacimiento y nunca se puede reducir. InnoDB
Para registrar la cantidad de registros que se han escrito redo
, se diseña una variable global Log Sequeue Number
, que se traduce en: 日志序列号,简称lsn
. Sin embargo, a diferencia de la edad de nacimiento de una persona que tiene 0 años, el tío que diseñó InnoDB estipuló el valor lsn inicial 8704
(es decir, cuando no se ha escrito un registro de rehacer, el valor lsn es 8704).
Sabemos que al log buffer
escribir registros en redo
el registro, no se escribe uno por uno, sino que se escribe en unidades de un mtr
conjunto de registros generado . redo
Y, de hecho, el contenido del registro está escrito en logblock body
. Sin embargo, al contar la cantidad de crecimiento, se calcula lsn
en función de la cantidad de registro escrita real más la suma ocupada log block header
. log block trailer
Veamos un ejemplo:
-
Cuando el sistema se inicializa después del primer inicio
log buffer
, ( la variable que marca la ubicación donde se debe escribirbuf_free
el siguiente registro) apuntará al primer lugar donde el desplazamiento es bytes ( tamaño), y el valor lsn seguirá aumentando en 12:redo
log buffer
block
12
log block header
Si el espacio de almacenamiento ocupado por un mtr
conjunto de registros generado redo
es relativamente pequeño, es decir, cuando el espacio libre restante del bloque que se insertará puede acomodar el mtr
registro enviado, lsn
la cantidad de aumento es la cantidad de bytes ocupados por el registro mtr
generado , como este:redo
-
Suponemos que la cantidad de registros
mtr_1
generados en la figura anteriorredo
son200
bytes, luegolsn
se incrementará8716
sobre la base de200
y se convierte en8916
. -
Si el espacio de almacenamiento ocupado por
mtr
un conjunto de registros generados es relativamente grande, es decir, cuando el espacio libre restanteredo
para insertar no es suficiente para acomodar los registros enviados, el aumento será el número de bytes ocupados por los registros generados más el bytes y ocupados adicionales , así:block
mtr
lsn
mtr
redo
log block header
log block trailer
-
Suponemos que la cantidad de registros
mtr_2
generados en la figura anterior son bytes. Para escribir los registros generados , tenemos que asignar dos más , por lo que el valor de debe aumentarse en función deredo
1000
mtr_2
redo
log buffer
block
lsn
8916
1000 + 12×2 + 4 × 2 = 1032
小提示:
¿Por qué el valor inicial de lsn es 8704? No lo sé muy bien, así estipula la gente. De hecho, también puedes estipular que se te cuenta como un año cuando naces, siempre y cuando te asegures de que tu edad sigue creciendo con el paso del tiempo.
Como se puede ver en la descripción anterior, 每一组由mtr生成的redo日志都有一个唯一的LSN值与其对应,LSN值越小,说明redo日志产生的越早
.
6.1 vaciado_a_disco_lsn
redo
El registro primero se escribe log buffer
y luego se descarga en redo
el archivo de registro en el disco. Así que InnoDB
se me ocurrió una buf_next_to_write
variable global llamada etiqueta 当前log buffer中已经有哪些日志被刷新到磁盘中了
. Haz un dibujo para mostrar que es así:
Anteriormente dijimos lsn
que indica la cantidad de registros escritos en el sistema actual redo
, que incluye log buffer
los registros que se escriben pero no se descargan en el disco. En consecuencia, InnoDB propone una redo
variable global que representa la cantidad de registros descargados en el disco, llamada flushed_to_disk_lsn
. Cuando el sistema se inicia por primera vez, el valor de esta variable es el mismo que el valor inicial de lsn, que es 8704. A medida que se ejecuta el sistema, redo
el registro se escribe continuamente log buffer
, pero no se vacía de inmediato en el disco, y el valor de lsn y flushed_to_disk_lsn
el valor de lsn amplían la brecha. Demostremos:
-
Después de que el sistema se inicia por primera vez,
log buffer
se escriben en él los tres registros generados pormtr_1
,mtr_2
y Suponga que los valores correspondientes al principio y al final de estos tres mtrs son:mtr_3
mtr
redo
lsn
mtr_1
:8716 ~ 8916mtr_2
:8916 ~ 9948mtr_3
:9948 ~ 10000
En este momento, el lsn ha crecido a
10000
, pero debido a que no hay una operación de actualización,flushed_to_disk_lsn
el valor en este momento sigue siendo8704
el que se muestra en la figura:log buffer
A continuación, realice la operación deblock
vaciar el registro enredo
el archivo de registro. Suponiendo que el registro demtr_1
y se vacíe en el disco, la cantidad de registros escritos en y debe aumentarse , por lo que el valor de aumenta a , como se muestra en la figura. :mtr_2
flushed_to_disk_lsn
mtr_1
mtr_2
flushed_to_disk_lsn
9948
En resumen, cuando redo
se escribe un nuevo registro log buffer
, el primer lsn
valor aumentará, pero flushed_to_disk_lsn
permanecerá sin cambios, y luego, a medida que los log buffer
registros en curso se vacíen en el disco, flushed_to_disk_lsn
el valor también aumentará. 如果两者的值相同时,说明log buffer中的所有redo日志都已经刷新到磁盘中了
.
小提示:
Cuando un programa de aplicación escribe un archivo en el disco, en realidad lo hace primero en el búfer del sistema operativo. Si una operación de escritura no regresa hasta que el sistema operativo confirme que se ha escrito en el disco, debe llamar al Función fsync proporcionada por el sistema operativo. De hecho只有当系统执行了fsync函数后
,flushed_to_disk_lsn
el valor de aumentará en consecuencia, cuando仅仅把log buffer中的日志写入到操作系统缓冲区却没有显式的刷新到磁盘时,另外的一个称之为write_lsn的值跟着增长
. Sin embargo, para comodidad de la comprensión de todos, confundimos los conceptos deflushed_to_disk_lsn
y al hablar de ello .write_lsn
6.2 Correspondencia entre el valor lsn y el desplazamiento del archivo de registro de rehacer
Debido a que el valor de es una suma lsn
que representa la cantidad de registros escritos por el sistema , ya que se generan muchos registros en uno, el valor de aumenta (por supuesto, a veces se agrega el tamaño de la suma ) , por lo que cuando los registros generados son escrito en el disco, es fácil Calcular el desplazamiento de valor en el grupo de archivos de registro, como se muestra en la figura:redo
mtr
lsn
log block header
log blocktrailer
mtr
lsn
redo
LSN
El valor inicial 8704
corresponde al desplazamiento del archivo 2048
, y luego el valor aumentará a medida que mtr
se escriban muchos bytes de registros en el disco .lsn
6.3 LSN en la lista de descarga
Sabemos que un mtr
acceso atómico a la página subyacente puede generar un conjunto de redo
registros indivisibles durante el proceso de acceso y, al mtr
final, este conjunto redo
de registros se escribirá en log buffer
. Además, mtr
hay otra cosa muy importante que hacer al final, que es mtr
agregar páginas que pueden haber sido modificadas durante la ejecución a Buffer Pool
la flush
lista enlazada. Para evitar que todos olviden flush
qué es una lista enlazada, veamos la imagen nuevamente:
Al modificar una página en caché por primera vez Buffer Pool
, el bloque de control correspondiente a esta página se insertará en ella flush链表的头部
, y cuando la página se modifique más tarde, porque ya está en flush
la lista enlazada, no se volverá a insertar. eso flush链表中的脏页是按照页面的第一次修改时间从大到小进行排序的
es Durante este proceso, se registrarán dos atributos sobre cuándo se modifica la página en el bloque de control correspondiente a la página de caché:
oldest_modification
: si una página se carga yBuffer Pool
modifica por primera vez, elmtr
valor lsn correspondiente al comienzo de la modificación de la página se escribirá en esta propiedadnewest_modification
: Cada vez que se modifique una página, se escribirá en esta propiedad el valormtr
correspondiente al final de la modificación de la página.lsn
Es decir, este atributo indica el valor lsn del sistema correspondiente después de la última modificación de la página.
Echemos un vistazo al molesto ejemplo anterior flushed_to_disk_lsn
:
-
Suponiendo que
mtr_1
se modifique durante la ejecución页a
, el bloque de control correspondientemtr_1
se agregará al encabezado de la lista enlazada al final de la ejecución. Y el correspondiente al principio se escribe en el atributo del bloque de control correspondiente , y el correspondiente al final se escribe en el atributo del bloque de control correspondiente . Haz un dibujo para mostrarlo (para que la imagen sea más hermosa, la ponemos ):页a
flush
mtr_1
lsn
8716
页a
oldest_modification
mtr_1
lsn
8916
页a
newest_modification
oldest_modification缩写成了o_m,把newest_modification缩写成了n_m
-
Luego, suponiendo que
mtr_2
se modifican dos páginas页b
de y durante la ejecución页c
, almtr_2
final de la ejecución, los bloques de control correspondientes de页b
y se agregarán al encabezado de la página. Y escribe lo que corresponde al principio , es decir, escríbelo en el atributo del bloque de control correspondiente , y escribe lo que corresponde al final , es decir, escríbelo en el atributo del bloque de control correspondiente . Haz un dibujo para mostrar:页c
flush链表
mtr_2
lsn
8916
页b
页c
oldest_modification
mtr_2
lsn
9948
页b
页c
newest_modification
-
Se puede ver en la figura que cada nuevo
flush
nodo insertado en la lista enlazada se coloca en la cabeza, es decir, lasflush
páginas sucias en el frente de la lista enlazada se modifican más tarde, y las páginas sucias en este último se modifican más temprano. -
Luego suponga que los y
mtr_3
se modifican durante el proceso de ejecución , pero se han modificado antes, por lo que se ha insertado su bloque de control correspondiente , por lo que al final de la ejecución, solo necesita agregar los bloques de control correspondientes al encabezado. Por lo tanto, es necesario escribir el correspondiente al principio , es decir, escribirlo en el atributo del bloque de control correspondiente , y escribir el correspondiente al final , es decir, escribirlo en el atributo del bloque de control correspondiente . Además, . Haz un dibujo para mostrar:页b
页d
页b
flush链表
mtr_3
页d
flush链表
mtr_3
lsn
9948
页d
oldest_modification
mtr_3
lsn
10000
页d
newest_modification
由于页b在mtr_3执行过程中又发生了一次修改,所以需要更新页b对应的控制块中newest_modification的值为10000
Para resumir lo que dije arriba, es: flush链表中的脏页按照修改发生的时间顺序进行排序,也就是按照oldest_modification代表的LSN值进行排序,被多次更新的页面不会重复插入到flush链表中,但是会更新newest_modification属性的值
.
6.4 punto de control
Es un hecho desafortunado que la capacidad de nuestro grupo de archivos de registro de rehacer sea limitada, tenemos que elegir 循环使用redo日志文件组中的文件
, pero esto hará que se escriba el último registro de redo y el primero redo日志追尾
, entonces debemos pensar en: redo日志只是为了系统奔溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘,也就是说即使现在系统奔溃,那么在重启后也用不着使用redo日志恢复该页面了,所以该redo日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的redo日志所重用
. Es decir: 判断某些redo 日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里
. Echemos un vistazo al ejemplo que ha estado molestando antes:
Como se muestra en la figura, aunque los mtr_1
registros mtr_2
generados redo
se han escrito en el disco, las páginas sucias modificadas por ellos todavía quedan en el disco Buffer Pool
, por lo que el espacio de los registros generados por ellos redo
en el disco no se puede sobrescribir. Luego, a medida que el sistema se ejecuta, si 页a
se vacía en el disco, se flush链表
eliminará , así:
mtr_1
Los registros generados de esta manera redo
son inútiles y el espacio en disco que ocupan se puede sobrescribir. El diseño InnoDB
es proponer una variable global para representar la cantidad total de registros checkpoint_lsn
que se pueden sobrescribir en el sistema actual , y el valor inicial de esta variable también es el mismo .redo
8704
Por ejemplo, 页a
si se vacía en el disco ahora, redo
el registro generado por mtr_1 se puede sobrescribir, por lo que podemos realizar una checkpoint_lsn
operación adicional y llamamos a este proceso una vez checkpoint
. Hacerlo una vez checkpoint
en realidad se puede dividir en dos pasos:
-
Paso 1: Calcular el valor máximo
redo
correspondiente al registro que se puede sobrescribir en el sistema actuallsn
redo
El registro se puede sobrescribir, lo que significa que su página sucia correspondiente se ha vaciado en el disco. Siempre que calculemos el valor correspondiente a la página sucia modificada más antigua en el sistema actual, todos los registros generados cuando el valor lsn del sistema es inferior a el valor del nodo puedeoldest_modification
será la página sucia a .oldest_modification
redo
oldest_modification
checkpoint_lsn
Por ejemplo, si el sistema actual
页a
se ha vaciado en el disco, entonces elflush链表
nodo de cola页c
es la primera página sucia modificada en el sistema actual. Suoldest_modification
valor es8916
8916, por lo que lo asignamos acheckpoint_lsn
(es decir, en el registro de rehacer correspondiente a Cuando el valor de lsn es inferior a 8916, se puede sobrescribir). -
Paso 2: escriba el desplazamiento del grupo de archivos de registro
checkpoint_lsn
correspondienteredo
y este número en la información de administración (es decir , o )checkpint
del archivo de registro .checkpoint1
checkpoint2
InnoDB
checkpoint
Mantiene una variable de cuántas veces ha hecho el sistema hasta el momento , y el valor de la variable se incrementacheckpoint_no
cada vez que se hace . Dijimos anteriormente que es fácil calcular el desplazamiento del grupo de archivos de registro correspondiente a un valor , por lo que podemos calcular el desplazamiento correspondiente en el grupo de archivos de registro y luego escribir estos tres valores en la gestión de la información del grupo de archivos de registro.checkpoint
1
lsn
redo
checkpoint_lsn
redo
checkpoint_offset
redo
Dijimos que cada
redo
archivo de registro tiene2048
un byte de información de administración, pero lacheckpoint
información anterior solo se escribirá en la información de administración del primer archivo de registro en el grupo de archivos de registro. Pero, ¿almacenamos encheckpoint1
ocheckpoint2
en? InnoDB especifica,当checkpoint_no的值是偶数时,就写到checkpoint1中,是奇数时,就写到checkpoint2中
Después de registrar checkpoint
la información, la relación de redo
cada valor en el grupo de archivos de registro lsn
es así:
6.5 Eliminar por lotes las páginas sucias de la lista de eliminación
Como dijimos Buffer Pool
en la introducción, en circunstancias normales, el subproceso en segundo plano está limpiando la LRU
lista vinculada y la lista vinculada.Esto se debe principalmente a que la operación de limpieza es relativamente lenta y no quiere afectar el subproceso del usuario para procesar la solicitud. flush
Sin embargo, si el sistema actual modifica las páginas con mucha frecuencia, esto dará lugar a frecuentes operaciones de escritura de registros y el valor lsn del sistema aumentará demasiado rápido. Si la página sucia de fondo no puede ser eliminada por la página sucia, el sistema no puede hacerlo a tiempo checkpoint
, y puede ser necesario que el subproceso del usuario elimine la página sucia modificada más antigua ( oldest_modification
la página sucia más pequeña) en el disco desde la descarga. list sincrónicamente, de modo que estas páginas sucias El registro de rehacer correspondiente a la página es inútil, y luego puede hacerlo checkpoint
.
6.6 Ver varios valores LSN en el sistema
Podemos utilizar SHOW ENGINE INNODB STATUS
comandos para visualizar los distintos valores InnoDB
en el motor de almacenamiento actual , como por ejemplo:LSN
LOG
---
mysql> SHOW ENGINE INNODB STATUS\G;
(...省略前边的许多状态)
Log sequence number 619362521
Log buffer assigned up to 619362521
Log buffer completed up to 619362521
Log written up to 619362521
Log flushed up to 619362521
Added dirty pages up to 619362521
Pages flushed up to 619362521
Last checkpoint at 619362521
Log minimum file id is 176
Log maximum file id is 189
80457 log i/o's done, 0.00 log i/o's/second
(...省略后边的许多状态)
en:
Log sequence number
: Representa el valor lsn en el sistema, es decir, la cantidad de registros de rehacer escritos por el sistema actual, incluidos los registros escritos en el búfer de registro.Log flushed up to
:flushed_to_disk_lsn
El valor representado, es decir, la cantidad de registros de rehacer que el sistema actual ha escrito en el disco.Pages flushed up to
: Representa el valor del atributo correspondiente a la página que se modificó por primera vez en la lista de vaciadooldest_modification
.Last checkpoint at
: Elcheckpoint_lsn
valor actual del sistema.
Siete, el uso de innodb_flush_log_at_trx_commit
Dijimos anteriormente que para garantizar la transacción 持久性
, el subproceso del usuario debe vaciar todos los registros generados durante la ejecución de la transacción redo
en el disco cuando se confirma la transacción. Este requisito es demasiado estricto y obviamente reducirá el rendimiento de la base de datos. Si algunos estudiantes no tienen requisitos tan estrictos para la persistencia de transacciones, pueden optar por modificar innodb_flush_log_at_trx_commit
el valor de una variable del sistema llamada , que tiene 3 valores opcionales:
- 0: cuando el valor de esta variable del sistema es 0, significa que el registro de rehacer no se sincroniza inmediatamente con el disco cuando se confirma la transacción, y esta tarea se entrega al subproceso en segundo plano. Obviamente, esto acelerará el procesamiento de la solicitud, pero si el servidor cuelga después de que se confirma la transacción y el subproceso en segundo plano no descarga el registro de rehacer en el disco a tiempo, se perderá la modificación de la página por parte de la transacción.
- 1: cuando el valor de esta variable del sistema es 1, significa que el registro de rehacer debe sincronizarse con el disco cuando se confirma la transacción, lo que puede garantizar la durabilidad de la transacción.
1也是innodb_flush_log_at_trx_commit的认值
. - 2: cuando el valor de esta variable del sistema es 2, significa que el registro de rehacer debe escribirse en el búfer del sistema operativo cuando se confirma la transacción, pero no es necesario garantizar que el registro se vacíe realmente en el disco. En este caso, si la base de datos está inactiva y el sistema operativo no está inactivo, aún se puede garantizar la persistencia de la transacción, pero si el sistema operativo también está inactivo, entonces no se puede garantizar la persistencia.
Ocho, recuperación de fallas
Cuando el servidor no está colgado, redo
el registro es simplemente una gran carga, no solo es inútil, sino que empeora el rendimiento. Pero por si acaso, dije por si acaso, en caso de que la base de datos se bloquee, el registro de rehacer es un tesoro.Podemos restaurar la página al estado antes de que el sistema fallara de acuerdo con los registros en el registro de rehacer al reiniciar. Echemos un vistazo más de cerca a cómo es el proceso de recuperación.
8.1 Determinación del punto de partida para la recuperación在这里插入代码片
Como decíamos antes, los logs checkpoint_lsn
anteriores redo
se pueden sobrescribir, es decir, las páginas sucias correspondientes a estos redo logs se han vaciado al disco, una vez vaciadas no hay necesidad de restaurarlas. Para los registros checkpoint_lsn
subsiguientes redo
, es posible que no se hayan vaciado las páginas sucias correspondientes o que se hayan vaciado. No podemos estar seguros, por lo que necesitamos leer checkpoint_lsn
el registro desde el principio redo
para restaurar la página. Por supuesto, hay dos datos almacenados redo
en la información de gestión del primer archivo en el grupo de archivos de registro , y ciertamente queremos seleccionar la información que sucedió más recientemente . La información que mide el tiempo de ocurrencia es la llamada .Solo necesitamos leer el valor de estos dos y comparar el tamaño.El valor que sea mayor indica en qué bloque se almacena la información más reciente . De esta forma podemos obtener el valor correspondiente más reciente y su desplazamiento en el grupo de archivos de registro de rehacer .block
checkpoint_lsn
checkpoint
checkpoint
checkpoint_no
checkpoint1
checkpoint2
block
checkpoint_no
checkpoint_no
checkpoint
checkpoint
checkpoint_lsn
checkpoint_offset
8.2 Determinación del punto final de la recuperación
Se determina el punto de partida de la recuperación del registro de rehacer, entonces, ¿cuál es el punto final? Esto tiene que empezar con la estructura del bloque. Decimos que cuando se escriben registros de rehacer, se escriben secuencialmente. Después de llenar un bloque, se escribirá en el siguiente bloque:
block
La parte común log block header
tiene un LOG_BLOCK_HDR_DATA_LEN
atributo llamado , que registra cuántos bytes de espacio se utilizan en el bloque actual. Para un bloque lleno, este valor siempre es 512. Si el valor de este atributo no es 512, entonces lo es, y es el último bloque que debe analizarse en esta recuperación de bloqueo.
8.3 Cómo recuperar
Después de determinar qué redo
registros deben escanearse para la recuperación de fallas, el siguiente paso es cómo recuperar. Supongamos que hay 5 registros de rehacer en el archivo de registro de rehacer actual, como se muestra en la figura:
Como redo0
está en checkpoint_lsn
la parte trasera, se puede dejar solo cuando se recupera. Ahora podemos redo
escanear checkpoint_lsn
los redo
registros subsiguientes en secuencia según el orden de los registros y restaurar las páginas correspondientes según el contenido registrado en los registros. No hay problema con esto, pero InnoDB
sigo pensando en algunas formas de acelerar el proceso de recuperación:
-
Use la tabla hash para calcular el valor hash
de acuerdo con r , y si hay varios registros de rehacer con el mismo ID de espacio y número de página, use una lista vinculada para conectarlos y vincularlos en el orden de generación, como se muestra en la figura Mostrar:edo日志的space ID和page number属性
space ID和page number相同的redo日志放到哈希表的同一个槽里
-
Después de eso, la tabla hash se puede atravesar, porque los registros de rehacer que modifican la misma página se colocan en una ranura, por lo que se puede reparar una página a la vez (evitando muchas E/S aleatorias para leer páginas), lo que puede acelerar velocidad de recuperación Otra cosa a tener en cuenta es que los registros de rehacer de la misma página se ordenan en el orden del tiempo de generación, por lo que también se restauran en este orden durante la recuperación. Si no se ordenan en el orden del tiempo de generación, pueden ocurrir errores. Por ejemplo, la operación de modificación original es insertar primero un registro y luego eliminar el registro. Si no se sigue este orden al restaurar, puede convertirse en un registro para eliminar primero y luego en un registro para insertar. Esto es obviamente equivocado.
-
Omitir las páginas que se han vaciado en el disco
Como dijimos antes, las páginas sucias correspondientes acheckpoint_lsn
los registros anterioresredo
deben haber sido vaciadas en el disco, pero no podemos estar seguros de si los registroscheckpoint_lsn
posterioresredo
se han vaciado en el disco, principalmente porque después del últimocheckpoint
registro, el subproceso de fondo puede continuar eliminando algunas páginas sucias del grupo de búfer de la lista vinculada de LRU y la lista vinculada de descarga. Para estoscheckpoint_lsn
registros de rehacer posteriores, si sus páginas sucias correspondientes se han vaciado en el disco cuando ocurre el bloqueo, entonces no hay necesidad de modificar la página de acuerdo con el contenido del registro de rehacer durante la recuperación.
Entonces, ¿cómo sabe redo
si las páginas sucias correspondientes a un determinado registro se han vaciado en el disco cuando se produce el bloqueo durante la recuperación? Esto tiene que empezar con la estructura de la página, como dijimos antes, cada página tiene una File Header
parte llamada , y hay Header
un FIL_PAGE_LSN
atributo llamado , que registra el valor lsn correspondiente cuando la página fue modificada por última vez (de hecho, es el valor en el bloque de control de página newest_modification
). Si checkpoint
una página sucia se descarga en el disco después de cierto tiempo, entonces el FIL_PAGE_LSN
valor lsn correspondiente a la página debe ser mayor que el checkpoint_lsn
valor de , cualquier página que cumpla con esta situación no necesita ejecutar repetidamente el registro lsn
con un valor FIL_PAGE_LSN
menor redo
que Más mejorado la velocidad de recuperación de fallos.