Este artículo lo lleva a comprender el registro de rehacer de MySQL

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.

1. ¿Qué es el registro de rehacer?

Sabemos que InnoDBel 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 Poolen 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 Poolantes 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 PoolPero 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 va InnoDB中是以页为单位a disco IO, 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 las Buffer Poolpá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 redobeneficios 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. redoHablaremos 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.
  • redoEl 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 redoel registro esencialmente solo registra qué modificaciones realizó la transacción en la base de datos. InnoDBSe definen varios tipos de redoregistros para diferentes escenarios de modificación de transacciones a la base de datos, pero la mayoría de los tipos de redoregistros tienen la siguiente estructura general:

inserte la descripción de la imagen aquí
La explicación detallada de cada parte es la siguiente:

  • type: el tipo del registro de rehacer. InnoDB redoha diseñado un total 53de diferentes tipos de registros, que se presentarán en detalle redomás adelante.
  • space ID: ID de espacio de tablas
  • page number: número de página
  • data: 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_idcolumna oculta como la clave principal de la tabla. La forma de asignar un valor a esta columna oculta row_id es la siguiente:

  • row_idEl 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 como row_idel valor de la columna del nuevo registro, y la variable se incrementará automáticamente.1
  • Siempre que el valor de esta variable 256sea un múltiplo de , el valor de la variable se actualizará a 表空间的页号为7的页面中un Max Row IDatributo 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 IDen la memoria, agregará el valor y 256lo asignará a la variable global que mencionamos anteriormente (porque el valor de la variable global puede ser mayor que el Max Row IDvalor del atributo en el último apagado)

Max Row IDEl espacio de almacenamiento ocupado por este atributo es 8bytes Cuando una transacción row_idinserta un registro en una tabla que contiene columnas ocultas y asigna un row_idvalor al registro que 256es 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 Poolrealiza en , necesitamos registrar un redoregistro 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, redoen el registro 只需要记录一下在某个页面的某个偏移量处修改了⼏个字节的值,具体被修改的内容是啥就好了, InnoDBeste redoregistro 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( typeEl número decimal correspondiente al campo es ): Indica el tipo de registro 1que escribe 1 byte en un cierto desplazamiento de la página .redo
  • MLOG_2BYTE( typeEl número decimal correspondiente al campo es 2): Indica el tipo de registro que escribe 2 bytes en un cierto desplazamiento de la página redo.
  • MLOG_4BYTE( typeEl número decimal correspondiente al campo es 4): Indica el tipo de registro que escribe 4 bytes en un cierto desplazamiento de la página redo.
  • MLOG_8BYTE( typeEl número decimal correspondiente al campo es ): Indica el tipo de registro 8que escribe 8 bytes en un determinado desplazamiento de la página .redo
  • MLOG_WRITE_STRING( typeEl número decimal correspondiente al campo es 30): Indica que una cadena de datos se escribe en un cierto desplazamiento de la página.

El atributo que mencionamos anteriormente Max Row IDen 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, redoy MLOG_8BYTEla redoestructura del registro es la siguiente:

inserte la descripción de la imagen aquí
La estructura de registro de los otros tipos MLOG_1BYTE, MLOG_2BYTEy 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_4BYTEredoMLOG_8BYTEMLOG_WRITE_STRINGredolen

inserte la descripción de la imagen aquí

小提示:
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 INSERTdeclaración como ejemplo. Además de B+insertar datos en la página del árbol, también puede actualizar Max Row IDel 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, INSERTla 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_STRINGtipo de redoregistro, 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 Headery Page Directoryasí 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 DirectoryInformación de slots en posibles actualizaciones .
  • Page HeaderVarias estadísticas de página en , PAGE_N_DIR_SLOTSla cantidad de espacios representados puede cambiar, PAGE_HEAP_TOPla dirección mínima del espacio no utilizado representado puede cambiar, PAGE_N_HEAPla 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:

inserte la descripción de la imagen aquí
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_DELETECorresponde al redo log del tipo, indicando borrar una serie de registros hasta MLOG_COMP_LIST_END_DELETEel 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_INSERTregistro 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_INSERTla estructura de este tipo de redo log (como son demasiados campos, es mejor verlos en vertical):

inserte la descripción de la imagen aquí
MLOG_COMP_REC_INSERTHay 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_uniqueses 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 puede n_uniquesordenar según el campo anterior del registro. Para los índices agrupados, n_uniquesel 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 ser NULL, por lo que el valor sigue siendo 索引列数+主键列数.

  • field1_len ~ fieldn_lenRepresenta 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) INTo de longitud variable (por ejemplo VARCHAR(M)), el tamaño ocupado por el campo Escribir siempre en el registro de rehacer.

  • offsetRepresenta 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 el next_recordpropiedades del registro anterior next_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 pasado end_seg_lenpuede 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 escribir redoun registro es una operación muy frecuente. InnoDBTratando de reducir redoel espacio de almacenamiento ocupado por el propio registro, pensé en algunos algoritmos intrincados para lograr este objetivo. end_seg_lenEste campo se propone para ahorrar el espacio de almacenamiento del registro de rehacer de .

  • mismatch_indexEl valor de también se establece para guardar el tamaño del registro de rehacer, puede ignorarlo.

Obviamente, este tipo de MLOG_COMP_REC_INSERTregistro redono registra PAGE_N_DIR_SLOTSpara qué se modificó el valor, PAGE_HEAP_TOPpara qué se modificó el valor, PAGE_N_HEAPpara 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 redolos 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_HEAPy 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 INSERTpuede 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 redoregistros correspondientes. El log generado durante la ejecución de la sentencia redoes dividido artificialmente en varios por el tío que diseñó InnoDB 不可分割的组, como por ejemplo:

  • Max Row IDEl 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_INSERTEsto se llama situación 乐观插入. Si el árbol B+ correspondiente a un índice se ve así:

    inserte la descripción de la imagen aquíAhora queremos insertar un 10registro con un valor de clave, que obviamente debe insertarse en 页bDado que la página ahora tiene suficiente espacio para acomodar un registro, es bueno binsertar el registro directamente en la página , así:b
    inserte la descripción de la imagen aquí

  • 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 que redose generarán varios registros, lo que llamamos esta situación 悲观插入. Si el árbol B+ correspondiente a un índice se ve así:

    inserte la descripción de la imagen aquí

    Ahora queremos insertar un 10registro 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í:

    inserte la descripción de la imagen aquí
    Si 页ael 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ás redoregistros. 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 tabla FREE链表、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 redoel 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 varios redoregistros. 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 de redoregistro después del último registro de rehacer en el grupo. El nombre del tipo es MLOG_MULTI_REC_END, y la estructura del registro type字段对应的十进制数字为31de 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:redotype
    inserte la descripción de la imagen aquí
    MLOG_MULTI_REC_END

    inserte la descripción de la imagen aquí

  • De esta forma, cuando el sistema falla y se reinicia para recuperarse, solo cuando se MLOG_MULTI_REC_ENDanaliza 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 redoregistro, por ejemplo, Max Row IDla operación de actualización de atributos solo genera un registro de rehacer.

    De hecho, MLOG_MULTI_REC_ENDtambién es posible seguir un registro con un tipo de registro de rehacer, pero InnoDBes más económico y no quieren desperdiciar un poco. No olvides que aunque redohay muchos tipos de registros, hay decenas de ellos, que son más pequeños que 127este número, es decir, usamos 7 bits para cubrir todos redolos tipos de registro, y el campo de tipo en realidad ocupa 11 palabra. En otras palabras, podemos ahorrar un poco para indicar que la operación que necesita asegurar la atomicidad solo genera un único redoregistro, como se muestra en el diagrama esquemático:

    inserte la descripción de la imagen aquí

  • Si typeel primer bit del campo es 1, 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, mtrpor ejemplo, el valor modificado una vez mencionado anteriormente Max Row IDes 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 mtrpuede contenerlas 若干条redo日志. Haz un dibujo para mostrar su relación de esta manera:

inserte la descripción de la imagen aquí

4. El proceso de escritura del registro de rehacer

4.1 bloque de registro de rehacer

InnoDBPara recuperarse mejor de los bloqueos del sistema durante el diseño , colocan los registros mtrgenerados 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:redo512redoblockredo log block

inserte la descripción de la imagen aquí
Todos los registros reales redose almacenan en 496el tamaño de los bytes , y los y almacenados log block bodyen la figura son información de administración. Veamos qué son estos llamados datos de gestión:log block headerlog block trailer

inserte la descripción de la imagen aquí
Los significados de varias de estas log block headerpropiedades 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 blockasigna cuando se usa por primera vez y lsnestá relacionado con el valor del sistema en ese momento. Use la siguiente fórmula para calcular el blockvalor LOG_BLOCK_HDR_NO: ((lsn / 512) & 0x3FFFFFFFUL) + 1
    esta fórmula 0x3FFFFFFFULpuede confundir a todos, pero su representación binaria puede ser más amigable:

    inserte la descripción de la imagen aquíSe puede ver en la figura que 0x3FFFFFFFULlos 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 una 0x3FFFFFFFULoperació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 él 0x3FFFFFFFUL. Esto también muestra que no importa cuán grande sea lsn, ((lsn / 512) & 0x3FFFFFFFUL)el valor de lsn debe estar 0~0x3FFFFFFFULentre, y si agrega 1, debe estar 1~0x40000000ULentre. Y 0x40000000ULeste valor debería ser familiar para todos, este valor representa 1GB. Es decir, el sistema puede generar como máximo un único LOG_BLOCK_HDR_NOvalor 1GB. El diseño InnoDBestipula redoque el tamaño total de todos los archivos contenidos en el grupo de archivos de registro no debe exceder 512GB, y un blocktamaño es 512bytes, es decir, el número máximo de bloques redocontenidos en el grupo de archivos de registro es , por lo que un número que no se repite el valor es suficiente.block1GB1GB

    Además, LOG_BLOCK_HDR_NOel primer bit del valor es especial, por lo que flush bitsi 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_GROUPpara representar el primer grupo de registro de registro mtrgenerado en el bloque Offset (de hecho, redoes el desplazamiento del blockprimer registro generado por el primer mtr aquí ).redo

  • LOG_BLOCK_CHECKPOINT_NO: Indica el llamado checkpointnúmero de serie, checkpointque es el foco de nuestro contenido de seguimiento. No es necesario aclarar su significado ahora, así que no se impaciente.

log block trailerLos 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í:

inserte la descripción de la imagen aquí
Podemos innodb_log_buffer_sizeespecificar log bufferel 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 bufferEl proceso de escritura de registros en redoel medio es secuencial, es decir, blockprimero se escribe en el medio anterior y luego se escribe blocken el medio siguiente cuando se agota el espacio libre del medio correspondiente . blockCuando queremos escribir registros log bufferen , 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的哪个偏移量处InnoDBbuf_freelog buffer

inserte la descripción de la imagen aquí
Dijimos anteriormente que se pueden generar mtrvarios 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:redoredoredologbuffermtrmtrredolog bufferT1T22个mtr

  • 事务T1Los dos mtrse llaman mtr_T1_1ymtr_T1_2
  • 事务T2Los dos mtrse llaman mtr_T2_1ymtr_T2_2

Cada uno mtrgenerará un conjunto de redoregistros y utilizará un diagrama esquemático para describir los mtrregistros generados:

inserte la descripción de la imagen aquí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 bufferDibujemos un diagrama esquemático (por el bien de la belleza, dibujamos todos los redo logs generados en un mtr como un todo):

inserte la descripción de la imagen aquíEn el diagrama esquemático, podemos ver que mtrel 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 mtrgenerados 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. :redomtrlog buffer

  • log buffer空间不足时: El tamaño del búfer de registro es limitado ( innodb_log_buffer_sizeespecificado por las variables del sistema), si continúa agregando registros a este búfer de registro de tamaño limitado, se llenará pronto. InnoDBSe 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 que redose usa el registro es principalmente porque ocupa menos espacio y todavía se escribe secuencialmente. Cuando se confirma la transacción, las páginas 可以不把modificadas Buffer Poolse 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 un Master Threadsubproceso en segundo plano, que descarga los registros en el disco aproximadamente una vez por log buffersegundo redo.
  • 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

MySQLSHOW VARIABLES LIKE 'datadir'De forma predeterminada, hay dos archivos con nombre ib_logfile0y 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_logfile1log bufferredo

  • 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 especifica redoel número de archivos de registro, 默认值为2, 最大值为100.

redoComo 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_logfile0escribe desde el principio. Si ib_logfile0está lleno, continuará escribiendo ib_logfile1. De manera similar, si ib_logfile1está lleno, se escribirá ib_logfile2y así sucesivamente. ¿Qué pasa si el último archivo está escrito? Luego regrese para ib_logfile0continuar escribiendo, por lo que todo el proceso se muestra en la siguiente figura:

inserte la descripción de la imagen aquí
El redotamañ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í que checkpointel concepto propuesto por InnoDB, nos centraremos en explicarlo más adelante~

5.3 formato de archivo de registro de rehacer

Dijimos anteriormente que log bufferes esencialmente un espacio de memoria continuo, que se divide en varios 512tamaños de bytes block. La esencia de actualizar el registro en log bufferel disco redoal disco es blockescribir la imagen del archivo de registro en el archivo de registro, por lo que redoel archivo de registro en realidad se compone de varios 512bytes de tamaño block. redoCada archivo del grupo de archivos de registro tiene el mismo tamaño y formato, y consta de dos partes:

  • El primer 2048byte, es decir, el primero 4个blockse utiliza para almacenar alguna información de gestión
  • 从第2048字节往后se utiliza para log 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:

inserte la descripción de la imagen aquí
Ya mencionamos el formato común blockcuando 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 bufferlog block headerlog block bodylog blocktrialerredo日志文件前2048个字节

inserte la descripción de la imagen aquí
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:

inserte la descripción de la imagen aquí

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:

inserte la descripción de la imagen aquí
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 redolos registros se generan constantemente. redoLa cantidad de registros aumenta constantemente, al igual que la edad de una persona, ha aumentado desde su nacimiento y nunca se puede reducir. InnoDBPara 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 bufferescribir registros en redoel registro, no se escribe uno por uno, sino que se escribe en unidades de un mtrconjunto de registros generado . redoY, de hecho, el contenido del registro está escrito en logblock body. Sin embargo, al contar la cantidad de crecimiento, se calcula lsnen función de la cantidad de registro escrita real más la suma ocupada log block header. log block trailerVeamos un ejemplo:

  • Cuando el sistema se inicializa después del primer inicio log buffer, ( la variable que marca la ubicación donde se debe escribir buf_freeel siguiente registro) apuntará al primer lugar donde el desplazamiento es bytes ( tamaño), y el valor lsn seguirá aumentando en 12:redolog bufferblock12log block header

    inserte la descripción de la imagen aquí

Si el espacio de almacenamiento ocupado por un mtrconjunto de registros generado redoes relativamente pequeño, es decir, cuando el espacio libre restante del bloque que se insertará puede acomodar el mtrregistro enviado, lsnla cantidad de aumento es la cantidad de bytes ocupados por el registro mtrgenerado , como este:redo

inserte la descripción de la imagen aquí

  • Suponemos que la cantidad de registros mtr_1generados en la figura anterior redoson 200bytes, luego lsnse incrementará 8716sobre la base de 200y se convierte en 8916.

  • Si el espacio de almacenamiento ocupado por mtrun conjunto de registros generados es relativamente grande, es decir, cuando el espacio libre restante redopara 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í:blockmtrlsnmtrredolog block headerlog block trailer

    inserte la descripción de la imagen aquí

  • Suponemos que la cantidad de registros mtr_2generados 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 deredo1000mtr_2redolog bufferblocklsn89161000 + 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

redoEl registro primero se escribe log buffery luego se descarga en redoel archivo de registro en el disco. Así que InnoDBse me ocurrió una buf_next_to_writevariable global llamada etiqueta 当前log buffer中已经有哪些日志被刷新到磁盘中了. Haz un dibujo para mostrar que es así:

inserte la descripción de la imagen aquíAnteriormente dijimos lsnque indica la cantidad de registros escritos en el sistema actual redo, que incluye log bufferlos registros que se escriben pero no se descargan en el disco. En consecuencia, InnoDB propone una redovariable 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, redoel 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_lsnel valor de lsn amplían la brecha. Demostremos:

  • Después de que el sistema se inicia por primera vez, log bufferse escriben en él los tres registros generados por mtr_1, mtr_2y Suponga que los valores correspondientes al principio y al final de estos tres mtrs son:mtr_3mtrredolsn

    • mtr_1:8716 ~ 8916
    • mtr_2:8916 ~ 9948
    • mtr_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_lsnel valor en este momento sigue siendo 8704el que se muestra en la figura:

    inserte la descripción de la imagen aquí
    log bufferA continuación, realice la operación de blockvaciar el registro en redoel archivo de registro. Suponiendo que el registro de mtr_1y 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_2flushed_to_disk_lsnmtr_1mtr_2flushed_to_disk_lsn9948

    inserte la descripción de la imagen aquí

En resumen, cuando redose escribe un nuevo registro log buffer, el primer lsnvalor aumentará, pero flushed_to_disk_lsnpermanecerá sin cambios, y luego, a medida que los log bufferregistros en curso se vacíen en el disco, flushed_to_disk_lsnel 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_lsnel valor de aumentará en consecuencia, cuando 仅仅把log buffer中的日志写入到操作系统缓冲区却没有显式的刷新到磁盘时,另外的一个称之为write_lsn的值跟着增长. Sin embargo, para comodidad de la comprensión de todos, confundimos los conceptos de flushed_to_disk_lsny 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 lsnque 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:redomtrlsnlog block headerlog blocktrailermtrlsnredo

inserte la descripción de la imagen aquí
LSNEl valor inicial 8704corresponde al desplazamiento del archivo 2048, y luego el valor aumentará a medida que mtrse escriban muchos bytes de registros en el disco .lsn

6.3 LSN en la lista de descarga

Sabemos que un mtracceso atómico a la página subyacente puede generar un conjunto de redoregistros indivisibles durante el proceso de acceso y, al mtrfinal, este conjunto redode registros se escribirá en log buffer. Además, mtrhay otra cosa muy importante que hacer al final, que es mtragregar páginas que pueden haber sido modificadas durante la ejecución a Buffer Poolla flushlista enlazada. Para evitar que todos olviden flushqué es una lista enlazada, veamos la imagen nuevamente:

inserte la descripción de la imagen aquí
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 flushla 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 y Buffer Poolmodifica por primera vez, el mtrvalor lsn correspondiente al comienzo de la modificación de la página se escribirá en esta propiedad
  • newest_modification: Cada vez que se modifique una página, se escribirá en esta propiedad el valor mtrcorrespondiente al final de la modificación de la página. lsnEs 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_1se modifique durante la ejecución 页a, el bloque de control correspondiente mtr_1se 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 ):页aflushmtr_1lsn8716页aoldest_modificationmtr_1lsn8916页anewest_modificationoldest_modification缩写成了o_m,把newest_modification缩写成了n_m

    inserte la descripción de la imagen aquí

  • Luego, suponiendo que mtr_2se modifican dos páginas 页bde y durante la ejecución 页c, al mtr_2final de la ejecución, los bloques de control correspondientes de 页by 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:页cflush链表mtr_2lsn8916页b页coldest_modificationmtr_2lsn9948页b页cnewest_modification

    inserte la descripción de la imagen aquí

  • Se puede ver en la figura que cada nuevo flushnodo insertado en la lista enlazada se coloca en la cabeza, es decir, las flushpá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_3se 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页bflush链表mtr_3页dflush链表mtr_3lsn9948页doldest_modificationmtr_3lsn10000页dnewest_modification由于页b在mtr_3执行过程中又发生了一次修改,所以需要更新页b对应的控制块中newest_modification的值为10000

    inserte la descripción de la imagen aquí

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:

inserte la descripción de la imagen aquí

Como se muestra en la figura, aunque los mtr_1registros mtr_2generados redose 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 redoen el disco no se puede sobrescribir. Luego, a medida que el sistema se ejecuta, si 页ase vacía en el disco, se flush链表eliminará , así:

inserte la descripción de la imagen aquí
mtr_1Los registros generados de esta manera redoson inútiles y el espacio en disco que ocupan se puede sobrescribir. El diseño InnoDBes proponer una variable global para representar la cantidad total de registros checkpoint_lsnque se pueden sobrescribir en el sistema actual , y el valor inicial de esta variable también es el mismo .redo8704

Por ejemplo, 页asi se vacía en el disco ahora, redoel registro generado por mtr_1 se puede sobrescribir, por lo que podemos realizar una checkpoint_lsnoperación adicional y llamamos a este proceso una vez checkpoint. Hacerlo una vez checkpointen realidad se puede dividir en dos pasos:

  • Paso 1: Calcular el valor máximo redocorrespondiente al registro que se puede sobrescribir en el sistema actuallsn

    redoEl 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_modificationserá la página sucia a .oldest_modificationredooldest_modificationcheckpoint_lsn

    Por ejemplo, si el sistema actual 页ase ha vaciado en el disco, entonces el flush链表nodo de cola 页ces la primera página sucia modificada en el sistema actual. Su oldest_modificationvalor es 89168916, por lo que lo asignamos a checkpoint_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_lsncorrespondiente redoy este número en la información de administración (es decir , o ) checkpintdel archivo de registro .checkpoint1checkpoint2

    InnoDBcheckpointMantiene una variable de cuántas veces ha hecho el sistema hasta el momento , y el valor de la variable se incrementa checkpoint_nocada 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.checkpoint1lsnredocheckpoint_lsnredocheckpoint_offsetredo

    Dijimos que cada redoarchivo de registro tiene 2048un byte de información de administración, pero la checkpointinformació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 en checkpoint1o checkpoint2en? InnoDB especifica,当checkpoint_no的值是偶数时,就写到checkpoint1中,是奇数时,就写到checkpoint2中

Después de registrar checkpointla información, la relación de redocada valor en el grupo de archivos de registro lsnes así:

inserte la descripción de la imagen aquí

6.5 Eliminar por lotes las páginas sucias de la lista de eliminación

Como dijimos Buffer Poolen la introducción, en circunstancias normales, el subproceso en segundo plano está limpiando la LRUlista 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. flushSin 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_modificationla 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 STATUScomandos para visualizar los distintos valores InnoDBen 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_lsnEl 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 vaciado oldest_modification.
  • Last checkpoint at: El checkpoint_lsnvalor 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 redoen 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_commitel 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, redoel 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_lsnanteriores redose 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_lsnsubsiguientes 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_lsnel registro desde el principio redopara restaurar la página. Por supuesto, hay dos datos almacenados redoen 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 .blockcheckpoint_lsncheckpointcheckpointcheckpoint_nocheckpoint1checkpoint2blockcheckpoint_nocheckpoint_nocheckpointcheckpointcheckpoint_lsncheckpoint_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:

inserte la descripción de la imagen aquí
blockLa parte común log block headertiene un LOG_BLOCK_HDR_DATA_LENatributo 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é redoregistros 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:

inserte la descripción de la imagen aquí
Como redo0está en checkpoint_lsnla parte trasera, se puede dejar solo cuando se recupera. Ahora podemos redoescanear checkpoint_lsnlos redoregistros 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 InnoDBsigo 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日志放到哈希表的同一个槽里

    inserte la descripción de la imagen aquí

  • 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 a checkpoint_lsnlos registros anteriores redodeben haber sido vaciadas en el disco, pero no podemos estar seguros de si los registros checkpoint_lsnposteriores redose han vaciado en el disco, principalmente porque después del último checkpointregistro, 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 estos checkpoint_lsnregistros 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 redosi 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 Headerparte llamada , y hay Headerun FIL_PAGE_LSNatributo 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 checkpointuna página sucia se descarga en el disco después de cierto tiempo, entonces el FIL_PAGE_LSNvalor lsn correspondiente a la página debe ser mayor que el checkpoint_lsnvalor de , cualquier página que cumpla con esta situación no necesita ejecutar repetidamente el registro lsncon un valor FIL_PAGE_LSNmenor redoque Más mejorado la velocidad de recuperación de fallos.

Supongo que te gusta

Origin blog.csdn.net/liang921119/article/details/130883489
Recomendado
Clasificación