prefacio
A través del contenido anterior, creo que todos saben que el espacio de tabla es un concepto abstracto, para el espacio de tabla del sistema, corresponde al 一个或多个
archivo real en el sistema de archivos, para cada espacio de tabla independiente, corresponde a 一个
la tabla nombrada en el archivo system.nombre .ibd
del archivo real.
Puede pensar en el espacio de tablas como un grupo dividido en muchas páginas.Cuando queremos insertar un registro para una tabla determinada, sacamos una página correspondiente del grupo y escribimos los datos en ella.
El contenido de este capítulo profundizará en los detalles del espacio de tablas y lo llevará a nadar en la piscina de la estructura de almacenamiento de InnoDB. Ya que habrá más conceptos involucrados en este capítulo, aunque estos conceptos no son difíciles, dependen unos de otros, por lo que te aconsejo que leas:不要跳着看
Tabla de contenido
- 1. Recuerda algunos conocimientos antiguos
- Dos mesas independientes
- Tres, la estructura del espacio de tabla del sistema
- Cuatro Resumen
1. Recuerda algunos conocimientos antiguos
1.1 Tipos de páginas
Permítanme enfatizar aquí nuevamente que InnoDB administra el espacio de almacenamiento 页
en unidades.Nuestro índice agrupado (es decir, datos completos de la tabla) y otros índices secundarios se guardan en el espacio de la tabla en forma de árbol B+, y B+ Los nodos del árbol son datos paginas Como dijimos anteriormente, el nombre de tipo de esta página de datos es en realidad: fil_page_index
. Además de este tipo de página para almacenar datos de índice, InnoDB también diseña varios tipos diferentes de páginas para diferentes propósitos.Creo que debe estar familiarizado con la siguiente tabla:
escribe un nombre | hexadecimal | describir |
---|---|---|
fil_page_type_allocated | 0x0000 | última asignación, aún no utilizada |
fil_page_undo_log | 0x0002 | deshacer página de registro |
fil_page_inode | 0x0003 | nodo de información del segmento |
fil_page_ibuf_free_list | 0x0004 | insertar lista libre de búfer |
file_page_ibuf_mapa de bits | 0x0005 | insertar mapa de bits de búfer |
fil_page_type_sys | 0x0006 | página del sistema |
fil_page_type_trx_sys | 0x0007 | datos del sistema transaccional |
fil_page_type_fsp_hdr | 0x0008 | Información del encabezado del espacio de tabla |
fil_page_type_xdes | 0x0009 | Página de descripción extendida |
fil_page_type_blob | 0x000a | página de desbordamiento |
fil_page_index | 0x45bf | Páginas de índice, también conocidas como páginas de datos |
Debido a que hay un prefijo de fil_page
o delante del tipo de página fil_page_type
, en aras de la simplicidad, omitiremos estos prefijos cuando expliquemos el tipo de página más adelante. Por ejemplo, fil_page_type_allocated
el tipo se llama allocated
tipo y fil_page_index
el tipo se llama index
tipo, etc.
1.2 Partes comunes de la página
Dijimos MySQL之数据页结构
en el artículo anterior que la página de datos, es decir, index
la página tipo consta de 7 partes, dos de las cuales son comunes a todos los tipos de páginas (por supuesto, no espero que recuerdes lo que dije, así que de nuevo aquí) Cualquier tipo de página tiene la siguiente estructura general:
Como puede ver en la imagen de arriba, cualquier tipo de página contendrá estas dos secciones:
File Header:
Alguna información general sobre la página de registroFile Trailer:
Verifique la integridad de la página para garantizar la consistencia del contenido cuando se actualice de la memoria al disco.
Para File Trailer
no enfatizar demasiado, enfatizaremos los File Header
diversos componentes nuevamente aquí:
nombre | Tamaño (unidad: B) | describir |
---|---|---|
fil_page_space_or_chksum | 4 | Suma de verificación de página (valor de suma de verificación) |
fil_page_offset | 4 | número de página |
página_archivo_prev | 4 | número de página de la página anterior |
fil_pagina_siguiente | 4 | Número de página de la página siguiente |
fil_page_lsn | 8 | La posición de la secuencia de registro correspondiente cuando se modificó la página por última vez (el nombre en inglés es: número de secuencia de registro) |
fil_page_type | 2 | tipo de página |
fil_page_file_flush_lsn | 8 | Solo se define en una página del espacio de tabla del sistema, lo que significa que el archivo se ha actualizado al menos al valor lsn correspondiente |
fil_page_arch_log_no_or_space_id | 4 | a qué tablespace pertenece la página |
Ahora, a excepción de los dos campos con LSN en el nombre que quizás no entienda, los otros campos deben ser muy familiares, pero aún queremos enfatizar los siguientes puntos:
-
Cada página en el espacio de tablas corresponde a un número de página, es decir
fil_page_offset
, el número de página consta de 4 bytes, es decir, 32 bits, por lo que un espacio de tablas puede tener un máximo de2³²
páginas, si de acuerdo con el tamaño predeterminado de la página 16kb es la cantidad máxima64tb
de datos admitida por un espacio de tablas. El número de página de la primera página del espacio de tabla es 0, y los números de página subsiguientes son 1, 2, 3... y así sucesivamente. -
Algunos tipos de páginas pueden formar una lista enlazada, y las páginas de la lista enlazada no se pueden almacenar en orden físico, sino que almacenan los números de página de la página anterior y la página siguiente según la
fil_page_prev
sumafil_page_next
. Cabe señalar que estos dos campos son principalmente paraindex
los tipos de páginas, es decir, las páginas de datos de las que hemos estado hablando antes se utilizan para establecer una lista doblemente enlazada para cada capa de nodos después de establecer el árbol B+. de las páginas no utilizan estos dos campos -
El tipo de cada página se
fil_page_type
indica, por ejemplo, el valor de este campo de una página de datos es0x45bf
, presentaremos varios tipos de páginas más adelante, y diferentes tipos de páginas tienen diferentes valores en este campo
Dos mesas independientes
Sabemos que InnoDB admite muchos tipos de espacios de tabla y este artículo se centra en la estructura de 独立表空间
y 系统表空间
. Sus estructuras son relativamente similares, pero dado que el espacio de tabla del sistema contiene información adicional sobre todo el sistema, primero presentaremos un espacio de tabla independiente más simple y hablaremos sobre la estructura del espacio de tabla del sistema más adelante.
2.1 El concepto de Extensión
Hay demasiadas páginas en el espacio de tablas 为了更好的管理这些页面
e InnoDB propone extent
el concepto de área (nombre en inglés: ). Para una página de 16 KB, las páginas consecutivas 64
son un área, es decir, un área ocupa el 1MB
tamaño de espacio predeterminado. Tanto si se trata de un espacio de tablas del sistema como de un espacio de tablas independiente, se puede considerar que se compone de varias áreas 每256个区被划分成一组
. Haz un dibujo para mostrar que es así:
Entre ellos , extent 0 ~ extent 255
se cuentan estos 256 distritos 第一个组
, extent 256 ~ extent 511
se cuentan estos 256 distritos 第二个组
, y extent 512 ~ extent 767
se cuentan estos 256 distritos 第三个组
(la figura anterior no dibuja todos los distritos del tercer grupo, por favor tome una decisión), y así sucesivamente, más grupos Puede ser dividido. Las primeras páginas de estos grupos son de tipo similar, por ejemplo:
De la figura anterior podemos conocer la siguiente información:
-
第一个组最开始的3个页面的类型是固定的
, es decir, los tipos de las tres primeras páginas en el área de extensión 0 son fijos, y son:FSP_HDR类型:
Este tipo de página se utiliza para registrar表空间的一些整体属性以及本组所有的区
las propiedades de las 256 áreas completas, a saber, extensión 0 ~ extensión 255. Una cosa a tener en cuenta es que todo el tablespace solo está disponible一个FSP_HDR类型的页面
.IBUF_BITMAP类型:
Este tipo de página es本组所有的区的所有页面关于INSERT BUFFER
información almacenada.INODE类型:
Este tipo de página almacena una serie deINODE
estructuras de datos denominadas .
-
Para el resto
最开始的2个页面的类型是固定
de grupos, es decir, se fijan los tipos de las dos primeras páginas de extensión 256 y extensión 512, respectivamente:XDES类型:
El nombre completo es Extent Descriptor,用来登记本组256个区的属性
es decir, para este tipo de página en la extensión 256, los atributos de estas áreas se almacenan en la extensión 256 ~ extensión 511, y para este tipo de página en la extensión 512, se almacena en la extensión 512 ~ extensión 767 propiedades de estas áreas. La página de tipo FSP_HDR presentada anteriormente es similar a la página de tipo XDES, excepto que la página de tipo FSP_HDR almacenará además algunos atributos de espacio de tabla.IBUF_BITMAP类型:
Igual que arriba, no hay más explicación aquí.
La estructura macro es así, y no es necesario que memorice los sustantivos con demasiada claridad, siempre que recuerde aproximadamente:表空间被划分为许多连续的区,每个区默认由64个页组成,每256个区划分为一组,每个组的最开始的几个页面类型是固定的就好了
2.2 El concepto de segmento (Segmento)
Si la cantidad de datos en nuestra tabla es pequeña, como si solo hay docenas o cientos de datos en su tabla, el concepto de área no es necesario, porque los datos correspondientes se pueden almacenar en unas pocas páginas simples, pero en la etapa posterior, no puede retener más y más registros en la tabla.
Teóricamente hablando, usar solo el concepto de página sin introducir el concepto de área no tiene ningún efecto sobre el funcionamiento del motor de almacenamiento, pero consideremos el siguiente escenario:
- Cada vez que insertamos un registro en la tabla, esencialmente insertamos datos en la tabla
聚簇索引
y所有二级索引
los nodos del árbol B+ representado. Y las páginas en cada capa del árbol B+ formarán una lista doblemente enlazada, si es así以页为单位来分配存储空间的话,双向链表相邻的两个页之间的物理位置可能离得非常远
. Cuando introdujimos los escenarios aplicables del índice de árbol B+范围查询只需要定位到最左边的记录和最右边的记录
, mencionamos específicamente que es suficiente escanear a lo largo de la lista doblemente enlazada, y si hay dos páginas adyacentes en la lista enlazada物理位置离得非常远
, es la llamada随机I/O
.
Sugerencia:
la velocidad del disco es diferente en varios órdenes de magnitud a la velocidad de la memoria, y la E/S aleatoria es muy lenta, por lo que debemos tratar de hacer que las posiciones físicas de las páginas adyacentes en la lista enlazada sean adyacentes entre sí. de modo que solo cuando se realizan consultas de rango se pueden usar las llamadas E/S secuenciales.
Por eso se introdujo el concepto de extensión 一个区就是在物理位置上连续的64个页(1M)
. Cuando la cantidad de datos en la tabla es grande, al asignar espacio para un índice, ya no se asigna en unidades de páginas, sino en unidades de regiones Incluso cuando los datos en la tabla son muy, muy grandes, pueden ser asignados a la vez Asignar varias regiones contiguas. Aunque puede causar un pequeño desperdicio de espacio (los datos son insuficientes para llenar toda el área), desde el punto de vista del rendimiento, puede eliminar una gran cantidad de E/S aleatorias, y las ventajas superan las desventajas.
La consulta de rango que mencionamos es en realidad 对B+树叶子节点中的记录进行顺序扫描
, y si no distingue entre los nodos de hoja y los nodos que no son de hoja, y coloca todas las páginas representadas por los nodos en el área aplicada, el efecto del escaneo de rango se reducirá considerablemente. Entonces, InnoDB trata los nodos hoja y los nodos no hoja del árbol B+ de manera diferente, es decir 叶子节点有自己独有的区
, 非叶子节点也有自己独有的区
. 存放叶子节点的区的集合就算是一个段(segment),存放非叶子节点的区的集合也算是一个段
. eso 一个索引会生成2个段,一个叶子节点段,一个非叶子节点段
es
De manera predeterminada, una tabla que usa el motor de almacenamiento InnoDB tiene solo un índice agrupado, y un índice generará 2 segmentos, y un 段是以区为单位申请存储空间
área ocupa 1M de espacio de almacenamiento de manera predeterminada, por lo que, de manera predeterminada, una tabla pequeña con solo unos pocos registros también necesita 2M ¿Espacio de almacenamiento? ¿Debo solicitar 2 millones más de espacio de almacenamiento cada vez que agregue un índice en el futuro?
Esto es simplemente un gran desperdicio para las tablas que almacenan menos registros. InnoDB es bastante frugal y, por supuesto, esta situación se tiene en cuenta. El quid de este problema es que las áreas que hemos introducido hasta ahora son todas muy puras, es decir, un área está completamente asignada a un determinado segmento, o todas las páginas de un área existen para almacenar los datos del mismo segmento. los datos del segmento no llenan todas las páginas de la zona, las páginas restantes no se pueden utilizar para otros fines. Ahora, para considerar el hecho de que asignar un determinado segmento en unidades de un área completa es un desperdicio de espacio de almacenamiento para una tabla con una pequeña cantidad de datos, InnoDB propone que en un 一个碎片(fragment)区的概念
área fragmentada, no todas las páginas son para The existen datos del mismo segmento, pero las páginas en el área fragmentada se pueden usar para diferentes propósitos. Por ejemplo, algunas páginas se usan para el segmento A, algunas páginas se usan para el segmento B y algunas páginas ni siquiera pertenecen a ningún segmento . El área fragmentada pertenece directamente al espacio de tabla y no pertenece a ningún segmento. Entonces, la estrategia para asignar espacio de almacenamiento para un determinado segmento es la siguiente:
-
Al comienzo de la inserción de datos en la tabla, el segmento asigna espacio de almacenamiento en unidades de una sola página desde un área fragmentada
-
当某个段已经占用了32个碎片区页面之后
, el espacio de almacenamiento se asignará en unidades de áreas completas
Entonces, ahora un segmento no puede definirse simplemente como una colección de ciertas áreas, sino que, de manera más precisa, debe ser una colección de algunas páginas dispersas y algunas áreas completas. Además de los segmentos de nodos de hoja y los segmentos de nodos que no son de hoja del índice, InnoDB también tiene segmentos definidos para almacenar algunos datos especiales, como segmentos de reversión. Por supuesto, no nos importan otros tipos de segmentos ahora, y ahora solo necesitamos saber Un segmento es una colección de páginas fragmentadas así como secciones completas.
2.3 Clasificación de zonas
El espacio de la mesa se compone de varias áreas, que se pueden dividir aproximadamente en 4 tipos:
-
空闲的区:
Actualmente no se utilizan páginas en esta zona. -
有剩余空间的碎片区:
Indica que todavía hay páginas disponibles en el área fragmentada. -
没有剩余空间的碎片区:
Indica que se utilizan todas las páginas del área fragmentada y que no hay páginas libres. -
附属于某个段的区:
Cada índice se puede dividir en segmentos de nodo hoja y segmentos de nodo no hoja. Además, InnoDB también definirá algunos segmentos de propósito especial. Cuando la cantidad de datos en estos segmentos es grande, el área se utilizará como unidad de asignación básica. . .
Estos 4 tipos de zonas también pueden denominarse los 4 estados de la zona. InnoDB define sustantivos específicos para estos 4 estados de zonas:
nombre del Estado | significado |
---|---|
gratis | Área libre |
libre_frag | Área fragmentada con espacio libre |
fragmento_completo | Área fragmentada sin espacio libre |
fseg | zona adjunta a un segmento |
Es necesario enfatizar nuevamente que free、free_frag和full_frag
los distritos en estos tres estados son independientes, por lo que lo son 直属于表空间
; y fseg
los distritos en el estado son 附属于某个段
.
Sugerencias
Si compara el espacio de la tabla con un país, un segmento equivale a una provincia y un distrito equivale a una ciudad. Una ciudad en general pertenece a una determinada provincia, al igual que todos los distritos del estado de la fseg pertenecen a un determinado segmento. Sin embargo, los tres estados de free, free_frag y full_frag están directamente bajo el espacio de la tabla, al igual que Beijing, Tianjin y Shanghai están directamente bajo la administración estatal.
Para facilitar la gestión de estas áreas, InnoDB diseñó una estructura denominada XDES Entry (el nombre completo es Extent Descriptor Entry), cada área corresponde a una estructura XDES Entry, que registra algunos atributos del área correspondiente. Primero veamos la imagen para tener una comprensión general de esta estructura:
En la figura, podemos ver que XDES Entry es una estructura de 40 bytes, que se divide aproximadamente en 4 partes. La interpretación de cada parte es la siguiente:
-
ID de segmento (8 bytes)
Cada segmento tiene un número único, que está representado por ID. El campo de ID de segmento aquí indica el segmento donde se encuentra el área. Por supuesto, la premisa es que el área se ha asignado a un segmento, de lo contrario, el valor de este campo no tiene sentido. -
La parte del nodo de lista (12 bytes)
puede conectar varias estructuras de entrada XDES en una lista enlazada. Eche un vistazo a la estructura del nodo de lista:
Si queremos ubicar una determinada posición en el espacio de la tabla, solo necesitamos especificar el número de página y el desplazamiento de página de la posición en el número de página especificado. entonces:-
La combinación de Prev Node Page Number y Prev Node Offset es un puntero a la entrada XDES anterior
-
La combinación de Número de página del siguiente nodo y Desplazamiento del siguiente nodo es un puntero a la siguiente entrada XDES.
-
-
Estado (4 bytes)
Este campo indica el estado de la zona. Los valores opcionales son los cuatro que mencionamos anteriormente, a saber: free, free_frag, full_frag y fseg. -
Mapa de bits de estado de página (16 bytes)
Esta parte ocupa un total de 16 bytes, es decir, 128 bits. Decimos que un área tiene 64 páginas por defecto, y estos 128 bits se dividen en 64 partes, y cada parte tiene 2 bits, correspondientes a una página del área. Por ejemplo, los bits 1 y 2 de la parte Mapa de bits de estado de página corresponden a la página 1 en el área, los bits 3 y 4 corresponden a la página 2 en el área, y así sucesivamente, la parte Mapa de bits de estado de página Los bits 127 y 128 bits de la correspondiente a la página 64 en la zona. El primer bit de estos dos bits indica si la página correspondiente está libre y el segundo bit aún no se usa
2.4 Lista enlazada de entradas XDES
Hasta ahora, hemos propuesto conceptos como área, segmento, área de fragmento, área adjunta a un segmento y entrada XDES. Nuestra intención original de hacer las cosas tan problemáticas es solo mejorar la eficiencia de insertar datos en la tabla sin reducir la cantidad. de datos la tabla desperdicia espacio. Ahora sabemos que insertar datos en la tabla es esencialmente insertar datos en los segmentos de nodos de hoja y en los segmentos de nodos que no son de hoja de cada índice en la tabla. También sabemos que diferentes áreas tienen diferentes estados y luego regresan al punto de partida original. y avanzar El proceso de insertar datos en un segmento:
-
Cuando haya menos datos en el segmento, primero verificará si hay
free_frag
un área en el espacio de la tabla con un estado de, es decir, buscará un área fragmentada con espacio libre. Si lo encuentra, luego tomará algunas páginas dispersas del e inserte los datos en él. De lo contrario, vaya al espacio de tabla para solicitar unfree
área con un estado de, es decir, un área libre, cambie el estado del área a free_frag y luego tome algunas páginas fragmentadas del nuevo área aplicada e inserte los datos en ella. Más tarde, cuando diferentes segmentos usen páginas fragmentadas, se tomarán de esta área hasta que no haya espacio libre en esta área, y luego el estado de esta área será full_frag.La pregunta ahora es ¿cómo sabe qué áreas en el espacio de la tabla están libres, qué áreas son free_frag y qué áreas son full_frag? Debe saber que el tamaño del espacio de la tabla se puede aumentar continuamente. Cuando crezca al nivel de GB, la cantidad de distritos será de miles. No podemos atravesar las estructuras de entrada XDES correspondientes a estos distritos cada vez, ¿verdad? En este momento, es hora de que la parte del nodo de lista en XDES Entry tenga un efecto milagroso. Podemos hacer tres cosas a través del puntero en el nodo de lista:
Conecte la estructura de entrada XDES correspondiente al área libre a través del nodo de la lista para formar una lista enlazada. A esta lista enlazada la llamamos lista enlazada libre.
-
Conectar la estructura XDES Entry correspondiente al área cuyo estado es free_frag en una lista enlazada a través del nodo list. A esta lista enlazada la llamamos lista enlazada free_frag.
-
Conecte la estructura de entrada XDES correspondiente al área cuyo estado es full_frag en una lista enlazada a través del nodo list. A esta lista enlazada la llamamos lista enlazada full_frag.
De esta forma, siempre que queramos encontrar un área de estado de free_frag, sacamos directamente el nodo principal de la lista enlazada de free_frag, tomamos algunas páginas dispersas de este nodo para insertar datos y lo modificamos cuando se usa el área correspondiente a este nodo. A continuación, el valor del campo de estado de este nodo se mueve de la lista enlazada free_frag a la lista enlazada full_frag. De manera similar, si no hay ningún nodo en la lista vinculada free_frag, tome directamente un nodo de la lista vinculada libre para pasar al estado de la lista vinculada free_frag, y modifique el valor del campo de estado del nodo a free_frag, y luego obtenga el fragmento del área correspondiente a este nodo La página está bien.
-
-
Cuando los datos del segmento hayan ocupado 32 páginas dispersas, solicite directamente un área completa para insertar los datos.
Sigue siendo la misma pregunta, ¿cómo sabemos qué áreas pertenecen a qué segmento? Luego, ¿atravesar cada estructura de entrada XDES? Es imposible atravesar, y es imposible atravesar en esta vida, hay una lista enlazada y es un hilo de lana. Entonces, ¿agregamos todas las estructuras de entrada xdes correspondientes al área fseg a una lista vinculada? Tonto, ¿cómo pueden diferentes segmentos compartir un área? ¿Desea almacenar tanto el segmento de nodo de hoja del índice a como el segmento de nodo de hoja del índice b en una zona? Obviamente queremos que cada segmento tenga su propia lista enlazada, por lo que podemos crear una lista enlazada basada en el número de segmento (es decir, la identificación del segmento) ¿Cuántas listas enlazadas podemos construir tantos segmentos como hay? Parece ser un pequeño problema, porque puede haber muchas áreas en un segmento, algunas áreas son completamente gratuitas, algunas áreas aún tienen algunas páginas disponibles y algunas áreas no tienen páginas gratuitas para usar, por lo que debemos continuar puntos de detalle, innodb crea tres listas enlazadas para la estructura de entrada xdes correspondiente a la zona en cada segmento:
- Lista enlazada libre: En el mismo segmento, se agregará a esta lista enlazada la estructura Entrada XDES correspondiente al área donde todas las páginas están libres. Tenga en cuenta que es diferente de la lista enlazada libre que pertenece directamente al espacio de tablas.La lista enlazada libre aquí se adjunta a un segmento determinado.
- lista enlazada not_full: En el mismo segmento se añadirá a esta lista enlazada la estructura XDES Entry correspondiente al área que aún tiene espacio libre.
- Lista enlazada completa: En el mismo segmento, se agregará a esta lista enlazada la estructura Entrada XDES correspondiente al área que no tiene espacio libre.
Se enfatiza nuevamente que cada índice corresponde a dos segmentos, y cada segmento mantendrá las tres listas vinculadas anteriores. Por ejemplo, tome la siguiente tabla:
create table demo9 (c1 int not null auto_increment,c2 varchar(100),c3 varchar(100),primary key (c1), key idx_c2 (c2));
Esta tabla t tiene dos índices, un índice agrupado y un índice secundario idx_c2, por lo que esta tabla tiene 4 segmentos en total, y cada segmento mantendrá las 3 listas enlazadas anteriores, un total de 12 listas enlazadas, más lo que dijimos anteriormente Hay 3 listas enlazadas que pertenecen directamente al espacio de tablas, y un total de 15 listas enlazadas deben mantenerse en todo el espacio de tablas independiente. Por lo tanto, si el segmento inserta datos cuando la cantidad de datos es relativamente grande, primero obtendrá el nodo principal de la lista enlazada not_full e insertará directamente los datos en el área correspondiente al nodo principal. se ha agotado, mueva el nodo a la lista enlazada completa.
2.4.1 Nodo base de lista enlazada
Hay muchas listas vinculadas presentadas anteriormente, ¿cómo encontrar estas listas vinculadas o cómo encontrar la posición del nodo principal o el nodo final de una lista vinculada en el espacio de la tabla? Por supuesto, InnoDB ha considerado este problema y ha diseñado una estructura llamada List Base Node, que se traduce al chino como el nodo base de la lista enlazada. Esta estructura contiene punteros al nodo principal y al nodo final de la lista enlazada e información sobre cuántos nodos están contenidos en la lista enlazada.Dibujemos un diagrama para ver el diagrama esquemático de esta estructura:
Cada lista vinculada que presentamos anteriormente corresponde a una estructura de nodo base de lista de este tipo, donde:
- Longitud de la lista indica cuántos nodos tiene la lista vinculada.
- Número de página del primer nodo y Desplazamiento del primer nodo indican la posición del nodo principal de la lista vinculada en el espacio de tabla.
- Número de página del último nodo y Desplazamiento del último nodo indican la posición del nodo final de la lista vinculada en el espacio de tabla.
Generalmente, colocamos la estructura del nodo base de la lista correspondiente a una determinada lista vinculada en una posición fija en el espacio de la tabla, de modo que resulta muy sencillo localizar una determinada lista vinculada.
2.4.2 Resumen de lista enlazada
En resumen, el espacio de tabla se compone de varias áreas, y cada área corresponde a una estructura de entrada XDES.La estructura de entrada XDES correspondiente al área que pertenece directamente al espacio de tabla se puede dividir en tres listas enlazadas: free, free_frag y full_frag; Un segmento se puede adjuntar a varias áreas, y la estructura de Entrada XDES correspondiente al área en cada segmento se puede dividir en tres listas enlazadas: libre, no_lleno y lleno. Cada lista enlazada corresponde a una estructura de nodo base de lista, que registra las posiciones de los nodos de cabeza y cola de la lista enlazada y el número de nodos contenidos en la lista enlazada. Es precisamente por la existencia de estas listas enlazadas que la gestión de estas áreas se ha convertido en una cuestión muy sencilla.
2.4.3 Estructura de la sección
Como dijimos anteriormente, un segmento en realidad no corresponde a un área física continua en el tablespace, sino a un concepto lógico que consta de varias páginas dispersas y algunas áreas completas. Como cada zona tiene una entrada XDES correspondiente para registrar los atributos en esta zona, InnoDB define una estructura de entrada INODE para cada segmento para registrar los atributos en el segmento. Echemos un vistazo al diagrama esquemático:
Sus diversas partes se explican a continuación:
-
ID de segmento
se refiere al número (ID) del segmento correspondiente a la estructura de Entrada INODE. -
El campo NOT_FULL_N_USED
se refiere a cuántas páginas se han utilizado en la lista enlazada NOT_FULL. -
Los tres nodos base de lista
definen nodos base de lista para la lista enlazada libre, la lista enlazada not_null y la lista enlazada completa del segmento respectivamente, de modo que cuando queremos encontrar el nodo principal y el nodo final de una lista enlazada de un segmento determinado, podemos ir directamente a esta parte para encontrarlos. Corresponde al nodo base de la lista de la lista vinculada. -
El valor de Número Mágico
se utiliza para marcar si la Entrada INODE ha sido inicializada (inicialización significa llenar los valores de cada campo). Si este número es un valor de 97937874, indica que la entrada INODE se ha inicializado, de lo contrario, no se ha inicializado. (No se preocupe por el significado especial de este valor, regulaciones). -
El segmento Fragment Array Entry
es una colección de algunas páginas fragmentadas y algunas áreas completas. Cada estructura Fragment Array Entry corresponde a una página fragmentada. Esta estructura tiene un total de 4 bytes, lo que indica el número de página de una página fragmentada.
Combinado con esta estructura de entrada de INODE, puede tener una comprensión más profunda de un segmento como una colección de páginas dispersas y algunas áreas completas.
2.4.4 Tipo FSP_HDR
La primera página del tablespace, número de página 0. El tipo de esta página es FSP_HDR, que almacena algunos atributos generales del espacio de tabla y la estructura de Entrada XDES correspondiente de los distritos 256 en el primer grupo.Vea el diagrama esquemático de este tipo de página directamente:
Una página de tipo FSP_HDR completa se compone aproximadamente de 5 partes, y la interpretación específica de cada parte es la siguiente:
nombre | nombre chino | Tamaño del espacio ocupado | Descripción rápida |
---|---|---|---|
Encabezado de archivo | encabezado del archivo | 38 bytes | Algunos datos generales de la página |
Encabezado de espacio de archivo | encabezado de espacio de tabla | 112 bytes | Alguna información general de atributos del espacio de tabla |
Entrada XDES | Información de descripción del distrito | 10240 bytes | Almacenar la información de los atributos correspondientes a los 256 distritos de este grupo |
Espacio vacio | espacio no utilizado | 5986 bytes | Se utiliza para rellenar la estructura de la página, que no tiene ningún significado práctico. |
Tráiler de archivo | fin del documento | 8 bytes | Si la página de verificación está completa |
El encabezado del archivo y el tráiler del archivo ya no se enfatizan. Entre las otras partes, el espacio vacío es un espacio sin usar. Ignorémoslo y concentrémonos en las dos partes del encabezado del espacio del archivo y la entrada XDES.
Parte de encabezado de espacio de archivos
Esta parte se utiliza para almacenar información general de atributos del espacio de tabla; consulte la siguiente figura para obtener más información:
La siguiente es una breve descripción de cada atributo:
nombre | Tamaño del espacio ocupado | describir |
---|---|---|
ID de espacio | 4 bytes | ID del espacio de tabla |
No utilizado | 4 bytes | Estos 4 bytes no se utilizan y se pueden ignorar |
Tamaño | 4 bytes | El número de páginas ocupadas por el tablespace actual |
Límite GRATIS | 4 bytes | El número de página mínimo que no ha sido inicializado, y la estructura de Entrada XDES correspondiente al área mayor o igual a este número de página no ha sido agregado a la lista LIBRE |
Banderas espaciales | 4 bytes | Algunos atributos del espacio de tablas que ocupan un espacio de almacenamiento relativamente pequeño |
FRAG_N_USADO | 4 bytes | El número de páginas utilizadas en la lista enlazada FREE_FRAG |
Lista de nodos base para lista GRATUITA | 16 bytes | El nodo base de la lista enlazada GRATIS |
Lista de nodos base para la lista FREE_FRAG | 16 bytes | El nodo base de la lista enlazada FREE_FRAG |
Listar nodo base para lista FULL_FRAG | 16 bytes | El nodo base de la lista enlazada FULL_FRAG |
Siguiente ID de segmento no utilizado | 8 bytes | El siguiente ID de segmento no utilizado en el tablespace actual |
List Base Node for SEG_INODES_FULL List | 16字节 | SEG_INODES_FULL链表的基节点 |
List Base Node for SEG_INODES_FREE List | 16字节 | SEG_INODES_FREE链表的基节点 |
这里头的Space ID、Not Used、Size这三个字段大家肯定一看就懂,其他的字段我们再详细看一下:
-
List Base Node for FREE List、List Base Node for FREE_FRAG List、List Base Node for FULL_FRAG List
这三个大家看着太亲切了,分别是直属于表空间的FREE链表的基节点、FREE_FRAG链表的基节点、FULL_FRAG链表的基节点,这三个链表的基节点在表空间的位置是固定的,就是在表空间的第一个页面(也就是FSP_HDR类型的页面)的File Space Header部分。所以之后定位这几个链表就很简单啦。 -
FRAG_N_USED
这个字段表明在FREE_FRAG链表中已经使用的页面数量。 -
FREE Limit
我们知道表空间都对应着具体的磁盘文件,一开始我们创建表空间的时候对应的磁盘文件中都没有数据,所以我们需要对表空间完成一个初始化操作,包括为表空间中的区建立XDES Entry结构,为各个段建立INODE Entry结构,建立各种链表的各种操作。我们可以一开始就为表空间申请一个特别大的空间,但是实际上有绝大部分的区是空闲的,我们可以选择把所有的这些空闲区对应的XDES Entry结构加入FREE链表,也可以选择只把一部分的空闲区加入FREE链表,等啥时候空闲链表中的XDES Entry结构对应的区不够使了,再把之前没有加入FREE链表的空闲区对应的XDES Entry结构加入FREE链表,中心思想就是啥时候用到啥时候初始化,InnoDB采用的就是后者,他们为表空间定义了FREE Limit这个字段,在该字段表示的页号之前的区都被初始化了,之后的区尚未被初始化。 -
Next Unused Segment ID
表中每个索引都对应2个段,每个段都有一个唯一的ID,那当我们为某个表新创建一个索引的时候,就意味着要创建两个新的段。那怎么为这个新创建的段找一个唯一的ID呢?去遍历现在表空间中所有的段么?我们说过,遍历是不可能遍历的,这辈子都不可能遍历,InnoDB提出了这个名叫Next Unused Segment ID的字段,该字段表明当前表空间中最大的段ID的下一个ID,这样在创建新段的时候赋予新段一个唯一的ID值就so easy啦,直接使用这个字段的值就好了。 -
Space Flags
表空间对于一些布尔类型的属性,或者只需要寥寥几个比特位搞定的属性都放在了这个Space Flags中存储,虽然它只有4个字节,32个比特位大小,却存储了好多表空间的属性,详细情况如下表:
标志名称 | 占用空间(单位:bit) | 描述 |
---|---|---|
POST_ANTELOPE | 1 | 表示文件格式是否大于ANTELOPE |
ZIP_SSIZE | 4 | 表示压缩页面的大小 |
ATOMIC_BLOBS | 1 | 表示是否自动把值非常长的字段放到BLOB页里 |
PAGE_SIZE | 4 | 页面大小 |
DATA_DIR | 1 | 表示表空间是否是从默认的数据目录中获取的 |
SHARED | 1 | 是否为共享表空间 |
TEMPORARY | 1 | 是否为临时表空间 |
ENCRYPTION | 1 | 表空间是否加密 |
UNUSED | 18 | 没有使用到的比特位 |
-
List Base Node for SEG_INODES_FULL List和List Base Node for SEG_INODES_FREE List
每个段对应的INODE Entry结构会集中存放到一个类型为INODE的页中,如果表空间中的段特别多,则会有多个INODE Entry结构,可能一个页放不下,这些INODE类型的页会组成两种列表:-
SEG_INODES_FULL链表,该链表中的INODE类型的页面都已经被INODE Entry结构填充满了,没空闲空间存放额外的INODE Entry了。
-
SEG_INODES_FREE链表,该链表中的INODE类型的页面仍有空闲空间来存放INODE Entry结构。
由于我们现在还没有详细介绍INODE类型页,所以等会说过INODE类型的页之后再回过头来看着两个链表。
-
XDES Entry部分
紧接着File Space Header部分的就是XDES Entry部分了,我们嘴上说过无数次,却从没见过真身的XDES Entry就是在表空间的第一个页面中保存的。我们知道一个XDES Entry结构的大小是40字节,但是一个页面的大小有限,只能存放有限个XDES Entry结构,所以我们才把256个区划分成一组,在每组的第一个页面中存放256个XDES Entry结构。大家回看那个FSP_HDR类型页面的示意图,XDES Entry 0就对应着extent 0,XDES Entry 1就对应着extent 1… 依此类推,XDES Entry255就对应着extent 255。
因为每个区对应的XDES Entry结构的地址是固定的,所以我们访问这些结构就非常简单啦。
2.4.5 XDES类型
每一个XDES Entry结构对应表空间的一个区,虽然一个XDES Entry结构只占用40字节,但你抵不住表空间的区的数量也多啊。在区的数量非常多时,一个单独的页可能就不够存放足够多的XDES Entry结构,所以我们把表空间的区分为了若干个组,每组开头的一个页面记录着本组内所有的区对应的XDES Entry结构。由于第一个组的第一个页面有些特殊,因为它也是整个表空间的第一个页面,所以除了记录本组中的所有区对应的XDES Entry结构以外,还记录着表空间的一些整体属性,这个页面的类型就是我们刚刚说完的FSP_HDR类型,整个表空间里只有一个这个类型的页面。除去第一个分组以外,之后的每个分组的第一个页面只需要记录本组内所有的区对应的XDES Entry结构即可,不需要再记录表空间的属性了,为了和FSP_HDR类型做区别,我们把之后每个分组的第一个页面的类型定义为XDES,它的结构和FSP_HDR类型是非常相似的:
与FSP_HDR类型的页面对比,除了少了File Space Header部分之外,也就是除了少了记录表空间整体属性的部分之外,其余的部分是一样一样的。
2.4.6 IBUF_BITMAP类型
每个分组的第二个页面的类型都是IBUF_BITMAP,这种类型的页里边记录了一些有关Change Buffer的信息。
2.4.7 INODE类型
再次对比前边介绍表空间的图,第一个分组的第三个页面的类型是INODE。我们前边说过InnoDB为每个索引定义了两个段,而且为某些特殊功能定义了些特殊的段。为了方便管理,他们又为每个段设计了一个INODE Entry结构,这个结构中记录了关于这个段的相关属性。而我们这会儿要介绍的这个INODE类型的页就是为了存储INODE Entry结构而存在的。
一个INODE类型的页面是由这几部分构成的:
名称 | 中文名 | 占用空间大小 | 简单描述 |
---|---|---|---|
File Header | 文件头部 | 38字节 | 页的一些通用信息 |
List Node for INODE Page List | 通用链表节点 | 12字节 | 存储上一个INODE页面和下一个INODE页面的指针 |
INODE Entry | 段描述信息 | 16320字节 | 存储段描述信息 |
Empty Space | 尚未使用空间 | 6字节 | 用于页结构的填充,没啥实际意义 |
File Trailer | 文件尾部 | 8字节 | 校验页是否完整 |
除了File Header、Empty Space、File Trailer这几个老朋友外,我们重点关注List Node for INODE Page List和INODE Entry这两个部分。
首先看INODE Entry部分,我们前边已经详细介绍过这个结构的组成了,主要包括对应的段内零散页面的地址以及附属于该段的FREE、NOT_FULL和FULL链表的基节点。每个INODE Entry结构占用192字节,一个页面里可以存储85个这样的结构。
重点看一下List Node for INODE Page List,因为一个表空间中可能存在超过85个段,所以可能一个INODE类型的页面不足以存储所有的段对应的INODE Entry结构,所以就需要额外的INODE类型的页面来存储这些结构。还是为了方便管理这些INODE类型的页面,InnoDB将这些INODE类型的页面串联成两个不同的链表:
- SEG_INODES_FULL链表:该链表中的INODE类型的页面中已经没有空闲空间来存储额外的INODE Entry结构了。
- SEG_INODES_FREE链表:该链表中的INODE类型的页面中还有空闲空间来存储额外的INODE Entry结构了。
想必大家已经认出这两个链表了,我们前边提到过这两个链表的基节点就存储在File Space Header里边,也就是说这两个链表的基节点的位置是固定的,所以我们可以很轻松的访问到这两个链表。以后每当我们新创建一个段(创建索引时就会创建段)时,都会创建一个INODE Entry结构与之对应,存储INODE Entry的大致过程就是这样的:
- 先看看SEG_INODES_FREE链表是否为空,如果不为空,直接从该链表中获取一个节点,也就相当于获取到一个仍有空闲空间的INODE类型的页面,然后把该INODE Entry结构放到该页面中。当该页面中无剩余空间时,就把该页放到SEG_INODES_FULL链表中。
- 如果SEG_INODES_FREE链表为空,则需要从表空间的FREE_FRAG链表中申请一个页面,修改该页面的类型为INODE,把该页面放到SEG_INODES_FREE链表中,与此同时把该INODE Entry结构放入该页面
2.4.8 Segment Header结构的运用
我们知道一个索引会产生两个段,分别是叶子节点段和非叶子节点段,而每个段都会对应一个INODE Entry结构,那我们怎么知道某个段对应哪个INODE Entry结构呢?所以得找个地方记下来这个对应关系。记得之前学习过的数据页MySQL之数据页结构,也就是INDEX类型的页有一个Page Header部分,所以把Page Header部分粘一下:
其中的PAGE_BTR_SEG_LEAF和PAGE_BTR_SEG_TOP都占用10个字节,它们其实对应一个叫Segment Header的结构,该结构图示如下:
各个部分的具体释义如下:
名称 | 占用空间大小 | 描述 |
---|---|---|
Space ID of the INODE Entry | 4字节 | INODE Entry结构所在的表空间ID |
Page Number of the INODE Entry | 4字节 | INODE Entry结构所在的页面页号 |
Byte Offset of the INODE Entry | 2字节 | INODE Entry结构在该页面中的偏移量 |
这样子就很清晰了,PAGE_BTR_SEG_LEAF记录着叶子节点段对应的INODE Entry结构的地址是哪个表空间的哪个页面的哪个偏移量,PAGE_BTR_SEG_TOP记录着非叶子节点段对应的INODE Entry结构的地址是哪个表空间的哪个页面的哪个偏移量。这样子索引和其对应的段的关系就建立起来了。不过需要注意的一点是,因为一个索引只对应两个段,所以只需要在索引的根页面中记录这两个结构即可。
2.5 真实表空间对应的文件大小
上边的这些概念已经压的快喘不过气了。不过独立表空间有那么大么?我到数据目录里看了,一个新建的表对应的.ibd文件只占用了96K,才6个页面大小,上边的说了那么多概念,那么大的空间占用,为什么只有96KB大小?
一开始表空间占用的空间自然是很小,因为表里边都没有数据。.ibd文件是自扩展的,随着表中数据的增多,表空间对应的文件也逐渐增大。
三、系统表空间结构
了解完了独立表空间的基本结构,系统表空间的结构也就好理解多了,系统表空间的结构和独立表空间基本类似,只不过由于整个MySQL进程只有一个系统表空间,在系统表空间中会额外记录一些有关整个系统信息的页面,所以会比独立表空间多出一些记录这些信息的页面。因为这个系统表空间最厉害,相当于是表空间之首,所以它的表空间 ID(Space ID)是0。
3.1 系统表空间整体结构
系统表空间与独立表空间的一个非常明显的不同之处就是在表空间开头有许多记录整个系统属性的页面,如图:
可以看到,系统表空间和独立表空间的前三个页面(页号分别为0
、1
、2
,类型分别是FSP_HDR
、IBUF_BITMAP
、INODE
)的类型是一致的,只是页号为3~7
的页面是系统表空间特有的,我们来看一下这些多出来的页面都是干啥使的:
页号 | 页面类型 | 英文描述 | 描述 |
---|---|---|---|
3 | SYS | Insert Buffer Header | 存储Insert Buffer的头部信息 |
4 | INDEX | Insert Buffer Root | 存储Insert Buffer的根页面 |
5 | TRX_SYS | Transction System | 事务系统的相关信息 |
6 | SYS | First Rollback Segment | 第一个回滚段的页面 |
7 | SYS | Data Dictionary Header | 数据字典头部信息 |
除了这几个记录系统属性的页面之外,系统表空间的extent 1
和extent 2
这两个区,也就是页号从64~191
这128
个页面被称为Doublewrite buffer
,不过上述的大部分知识都涉及到了事务和多版本控制的问题,这些问题我们会放在后边的章节中进行学习,所以现在我们只学习下有关InnoDB数据字典的知识,其余的概念在后边再看
3.2 InnoDB数据字典
我们平时使用INSERT
语句向表中插入的那些记录称之为用户数据,MySQL只是作为一个软件来为我们来保管这些数据,提供方便的增删改查接口而已。但是每当我们向一个表中插入一条记录的时候,MySQL先要校验一下插入语句对应的表存不存在,插入的列和表中的列是否符合,如果语法没有问题的话,还需要知道该表的聚簇索引和所有二级索引对应的根页面是哪个表空间的哪个页面,然后把记录插入对应索引的B+
树中。所以说,MySQL除了保存着我们插入的用户数据之外,还需要保存许多额外的信息,比方说:
- 某个表属于哪个表空间,表里边有多少列
- 表对应的每一个列的类型是什么
- 该表有多少索引,每个索引对应哪几个字段,该索引对应的
- 页面在哪个表空间的哪个页面
- 该表有哪些外键,外键对应哪个表的哪些列
- 某个表空间对应文件系统上文件路径是什么等等
上述这些数据并不是我们使用INSERT
语句插入的用户数据,实际上是为了更好的管理我们这些用户数据而不得已引入的一些额外数据,这些数据也称为元数据
。InnoDB
存储引擎特意定义了一些列的内部系统表
(internal system table
)来记录这些这些元数据
:
表名 | 描述 |
---|---|
SYS_TABLES | 整个InnoDB存储引擎中所有的表的信息 |
SYS_COLUMNS | 整个InnoDB存储引擎中所有的列的信息 |
SYS_INDEXES | 整个InnoDB存储引擎中所有的索引的信息 |
SYS_FIELDS | 整个InnoDB存储引擎中所有的索引对应的列的信息 |
SYS_FOREIGN | 整个InnoDB存储引擎中所有的外键的信息 |
SYS_FOREIGN_COLS | 整个InnoDB存储引擎中所有的外键对应列的信息 |
SYS_TABLESPACES | 整个InnoDB存储引擎中所有的表空间信息 |
SYS_DATAFILES | 整个InnoDB存储引擎中所有的表空间对应文件系统的文件路径信息 |
SYS_VIRTUAL | 整个InnoDB存储引擎中所有的虚拟生成列的信息 |
这些系统表也被称为数据字典
,它们都是以B+
树的形式保存在系统表空间的某些页面中,其中SYS_TABLES
、SYS_COLUMNS
、SYS_INDEXES
、SYS_FIELDS
这四个表尤其重要,称之为基本系统表
(basic system tables
),我们先看看这4个表的结构。
3.2.1 SYS_TABLES 表
SYS_TABLES
表的列
列名 | 描述 |
---|---|
NAME | 表的名称 |
ID | InnoDB存储引擎中每个表都有一个唯一的ID |
N_COLS | 该表拥有列的个数 |
TYPE | 表的类型,记录了一些文件格式、行格式、压缩等信息 |
MIX_ID | 已过时,忽略 |
MIX_LEN | 表的一些额外的属性 |
CLUSTER_ID | 未使用,忽略 |
SPACE | 该表所属表空间的ID |
这个SYS_TABLES
表有两个索引:
- 以
NAME
列为主键的聚簇索引 - 以
ID
列建⽴的二级索引
3.2.2 SYS_COLUMNS表
SYS_COLUMNS
表的列
列名 | 描述 |
---|---|
TABLE_ID | 该列所属表对应的ID |
POS | 该列在表中是第几列 |
NAME | 该列的名称 |
MTYPE | main data type,主数据类型,就是那堆INT、CHAR、VARCHAR、FLOAT、DOUBLE之类的等 |
PRTYPE | precise type,精确数据类型,就是修饰主数据类型的那堆东东,比如是否允许NULL值,是否允许负数啥的 |
LEN | 该列最多占用存储空间的字节数 |
PREC | 该列的精度,不过这列貌似都没有使用,默认值都是0 |
这个SYS_COLUMNS
表只有一个聚集索引:
- 以
(TABLE_ID, POS)
列为主键的聚簇索引
3.2.3 SYS_INDEXES表
SYS_INDEXES
表的列
列名 | 描述 |
---|---|
TABLE_ID | 该索引所属表对应的ID |
ID | InnoDB存储引擎中每个索引都有一个唯一的ID |
NAME | 该索引的名称 |
N_FIELDS | 该索引包含列的个数 |
TYPE | 该索引的类型,比如聚簇索引、唯一索引、更改缓冲区的索引、全文索引、普通的二级索引等等各种类型 |
SPACE | 该索引根页面所在的表空间ID |
PAGE_NO | 该索引根页面所在的页面号 |
MERGE_THRESHOLD | 如果页面中的记录被删除到某个比例,就把该页面和相邻页面合并,这个值就是这个比例 |
这个SYS_INEXES
表只有一个聚集索引:
- 以
(TABLE_ID, ID)
列为主键的聚簇索引
3.2.4 SYS_FIELDS表
SYS_FIELDS
表的列
列名 | 描述 |
---|---|
INDEX_ID | 该索引列所属的索引的ID |
POS | 该索引列在某个索引中是第几列 |
COL_NAME | 该索引列的名称 |
这个SYS_INEXES
表只有一个聚集索引:
- 以
(INDEX_ID, POS)
列为主键的聚簇索引
3.3 Data Dictionary Header页面
只要有了上述4个基本系统表,也就意味着可以获取其他系统表以及用户定义的表的所有元数据。比方说我们想看看SYS_TABLESPACES
这个系统表里存储了哪些表空间以及表空间对应的属性,那就可以:
-
到
SYS_TABLES
表中根据表名定位到具体的记录,就可以获取到SYS_TABLESPACES
表的TABLE_ID
-
使用这个
TABLE_ID
到SYS_COLUMNS
表中就可以获取到属于该表的所有列的信息。 -
使用这个
TABLE_ID
还可以到SYS_INDEXES
表中获取所有的索引的信息,索引的信息中包括对应的INDEX_ID
,还记录着该索引对应的B+数根页面是哪个表空间的哪个页面。 -
使用
INDEX_ID
就可以到SYS_FIELDS
表中获取所有索引列的信息。
也就是说这4个表是表中之表,那这4个表的元数据去哪里获取呢?没法搞了,只能把这4个表的元数据,就是它们有哪些列、哪些索引等信息硬编码到代码中,然后InnoDB
拿出一个固定的页面来记录这4个表的聚簇索引和二级索引对应的B+树位置
,这个页面就是页号为7
的页面,类型为SYS
,记录了Data Dictionary Header
,也就是数据字典的头部信息。除了这4个表
的5个索引
的根页面信息外,这个页号为7的页面还记录了整个InnoDB存储引擎的一些全局属性
:
可以看到这个页面由下边几个部分组成:
名称 | 中文名 | 占用空间大小 | 简单描述 |
---|---|---|---|
File Header | 文件头部 | 38字节 | 页的一些通用信息 |
Data Dictionary Header | 数据字典头部信息 | 56字节 | 记录一些基本系统表的根页面位置以及InnoDB存储引擎的一些全局信息 |
Segment Header | 段头部信息 | 10字节 | 记录本页面所在段对应的INODE Entry位置信息 |
Empty Space | 尚未使用空间 | 16272字节 | 用于页结构的填充,没啥实际意义 |
File Trailer | 文件尾部 | 8字节 | 校验页是否完整 |
可以看到这个页面里竟然有Segment Header
部分,意味着InnoDB把这些有关数据字典的信息当成一个段来分配存储空间,我们就姑且称之为数据字典段
吧。由于目前我们需要记录的数据字典信息非常少(可以看到Data Dictionary Header
部分仅占用了56字节),所以该段只有一个碎片页,也就是页号为7
的这个页。
接下来我们需要细细说一下Data Dictionary Header
部分的各个字段:
-
Max Row ID
:我们说过如果我们不显式的为表定义主键
,而且表中也没有UNIQUE
索引,那么InnoDB
存储引擎会默认为我们生成一个名为row_id
的列作为主键。因为它是主键,所以每条记录的row_id
列的值不能重复。原则上只要一个表中的row_id列不重复
就可以了,也就是说表a和表b拥有一样的row_id
列也没啥关系,不过InnoDB
只提供了这个Max Row ID
字段,不论哪个拥有row_id
列的表插入一条记录时,该记录的row_id
列的值就是Max Row ID
对应的值,然后再把Max Row ID
对应的值加1,也就是说这个Max Row ID是全局共享的
。 -
Max Table ID
:InnoDB存储引擎中的所有的表都对应一个唯一的ID,每次新建一个表时,就会把本字段的值作为该表的ID,然后自增本字段的值。 -
Max Index ID:
InnoDB存储引擎中的所有的索引都对应一个唯一的ID,每次新建一个索引时,就会把本字段的值作为该索引的ID,然后自增本字段的值。Max Space ID
:InnoDB存储引擎中的所有的表空间都对应一个唯一的ID,每次新建一个表空间时,就会把本字段的值作为该表空间的ID,然后自增本字段的值。 -
Mix ID Low(Unused)
:这个字段没啥用,跳过。 -
Root of SYS_TABLES clust index
:本字段代表SYS_TABLES
表聚簇索引的根页面的页号。 -
Root of SYS_TABLE_IDS sec index
:本字段代表SYS_TABLES
表为ID
列建立的二级索引
的根页面的页号。 -
Root of SYS_COLUMNS clust index
:本字段代表SYS_COLUMNS表聚簇索引的根页面的页号。 -
Root of SYS_INDEXES clust index
:本字段代表SYS_INDEXES
表聚簇索引的根页面的页号。 -
Root of SYS_FIELDS clust index
:本字段代表SYS_FIELDS
表聚簇索引的根页面的页号。 -
Unused
:这4个字节没用,跳过。
以上就是页号为7的页面的全部内容,看一次肯定懵,一定要反复多看几次。
3.4 information_schema系统数据库
需要注意一点的是,用户是不能直接访问InnoDB的这些内部系统表的,除非你直接去解析系统表空间对应文件系统上的文件。不过InnoDB考虑到查看这些表的内容可能有助于大家分析问题,所以在系统数据库information_schema
中提供了一些以innodb
开头的表:
mysql> USE information_schema;
Database changed
mysql> show tables like 'in%';
+------------------------------------+
| Tables_in_information_schema (IN%) |
+------------------------------------+
| INNODB_BUFFER_PAGE |
| INNODB_BUFFER_PAGE_LRU |
| INNODB_BUFFER_POOL_STATS |
| INNODB_CACHED_INDEXES |
| INNODB_CMP |
| INNODB_CMP_PER_INDEX |
| INNODB_CMP_PER_INDEX_RESET |
| INNODB_CMP_RESET |
| INNODB_CMPMEM |
| INNODB_CMPMEM_RESET |
| INNODB_COLUMNS |
| INNODB_DATAFILES |
| INNODB_FIELDS |
| INNODB_FOREIGN |
| INNODB_FOREIGN_COLS |
| INNODB_FT_BEING_DELETED |
| INNODB_FT_CONFIG |
| INNODB_FT_DEFAULT_STOPWORD |
| INNODB_FT_DELETED |
| INNODB_FT_INDEX_CACHE |
| INNODB_FT_INDEX_TABLE |
| INNODB_INDEXES |
| INNODB_METRICS |
| INNODB_SESSION_TEMP_TABLESPACES |
| INNODB_TABLES |
| INNODB_TABLESPACES |
| INNODB_TABLESPACES_BRIEF |
| INNODB_TABLESTATS |
| INNODB_TEMP_TABLE_INFO |
| INNODB_TRX |
| INNODB_VIRTUAL |
+------------------------------------+
31 rows in set (0.00 sec)
在information_schema
数据库中的这些以INNODB
开头的表并不是真正的内部系统表(内部系统表就是我们上边唠叨的以SYS
开头的那些表),而是在存储引擎启动时读取这些以SYS开头的系统表,然后填充到这些以INNODB_SYS
开头的表中。以INNODB_SYS
开头的表和以SYS
开头的表中的字段并不完全一样,但供大家参考已经足矣。
四、总结
今天我们学习了关于InnoDB存储引擎表空间的结构,通篇几乎全是概念、图片,这部分知识本就枯燥乏味,但是’春天’马上就到来,最枯燥乏味的内容马上结束了。由于今天的内容都是偏理论的概念,加上篇幅原因,就不做知识点总结了。
今天的文章我第一次读原著时,一脸懵逼,好像知道了表空间结构是怎么一回事儿,但是好像又讲不出来什么,所以建议大家多看几次,我相信一句话:书读百遍,其义自见。当你一次看不懂的时候,就一定要多看几次。同时,建议大家多动手画一画结构图,这样理解起来更加深刻。
A través del estudio de este artículo, conozco la complejidad y la sutileza del diseño de bases de datos MySQL. El autor del libro original, el niño 4919, es tan profundo en la investigación de la tecnología que actualmente estoy fuera de mi alcance. Por lo tanto, sigue el ritmo del tipo grande y progresa un poco todos los días. Espero que todos eventualmente se conviertan en grandes disparado a los ojos de los demás. ¡Animémonos unos a otros!
Hasta ahora, el estudio de hoy ha terminado, espero que te conviertas en un yo indestructible
~~~
No puedes conectar los puntos mirando hacia adelante; solo puedes conectarlos mirando hacia atrás. Así que tienes que confiar en que los puntos se conectarán de alguna manera en tu futuro. Tienes que confiar en algo: tu instinto, destino, vida, karma, lo que sea. Este enfoque nunca me ha defraudado y ha marcado una gran diferencia en mi vida.
Si mi contenido te es útil, por favor 点赞
, 评论
,, 收藏
la creación no es fácil, el apoyo de todos es la motivación para que yo persevere