Tabla de contenidos
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
a otra máquinaInnoDB
InnoDB
InnoDB
y AUTOCOMMIT
InnoDB
y TRANSACTION ISOLATION LEVEL
SELECT ... FOR UPDATE
y
SELECT ... LOCK IN SHARE MODE
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
InnoDB
dota a MySQL de un motor de
almacenamiento transaccional (conforme a ACID
)
con capacidades de commit (confirmación), rollback (cancelación) y
recuperación de fallas. InnoDB
realiza
bloqueos a nivel de fila y también porporciona funciones de
lectura consistente sin bloqueo al estilo Oracle en sentencias
SELECT
. Estas características incrementan el
rendimiento y la capacidad de gestionar múltiples usuarios
simultáneos. No se necesita un bloqueo escalado en
InnoDB
porque los bloqueos a nivel de fila
ocupan muy poco espacio. InnoDB
también
soporta restricciones FOREIGN KEY
. En consultas
SQL, aún dentro de la misma consulta, pueden incluirse libremente
tablas del tipo InnoDB
con tablas de otros
tipos.
InnoDB
se diseñó para obtener el máximo
rendimiento al procesar grandes volúmenes de datos. Probablemente
ningún otro motor de bases de datos relacionales en disco iguale
su eficiencia en el uso de CPU.
A pesar de estar totalmente integrado con el servidor MySQL, el
motor de almacenamiento InnoDB
mantiene su
propio pool de almacenamiento intermedio para tener un cache de
datos e índices en la memoria principal.
InnoDB
almacena sus tablas e índices en un
espacio de tablas, el cual puede consistir de varios ficheros (o
particiones disco). Esto difiere de, por ejemplo, el motor
MyISAM
, donde cada tabla se almacena empleando
ficheros separados. Las tablas InnoDB
pueden
ser de cualquier tamaño, aún en sistemas operativos donde el
tamaño de los ficheros se limita a 2GB.
En MySQL 5.0, InnoDB
viene incluido por defecto
en las distribuciones binarias. El instalador Windows Essentials
configura a InnoDB
como el tipo de base de
datos MySQL por defecto en Windows.
InnoDB
se utiliza en muchos grandes sitios de
bases de datos que necesitan alto rendimiento. El famoso sitio de
noticias de Internet Slashdot.org corre sobre
InnoDB
. Mytrix, Inc. almacena más de 1TB de
datos en InnoDB
, y otros sitios manejan una
carga promedio de 800 inserciones y actualizaciones por segundo en
InnoDB
.
InnoDB
se publica bajo la misma licencia GNU
GPL Versión 2 (de Junio de 1991) que MySQL. Para más
información sobre el licenciamiento de MySQL, consulte
http://www.mysql.com/company/legal/licensing/.
Información de contacto para Innobase Oy, creador del motor
InnoDB
:
Sitio web: http://www.innodb.com/
Correo electrónico: <sales@innodb.com>
Teléfonos: +358-9-6969 3250 (oficina)
+358-40-5617367 (móvil)
Innobase Oy Inc.
World Trade Center Helsinki
Aleksanterinkatu 17
P.O.Box 800
00101 Helsinki
Finland
En MySQL 5.0, el motor de almacenamiento InnoDB
está habilitado por defecto. Si no se desean emplear tablas
InnoDB
, puede agregarse la opción
skip-innodb
al fichero de opciones de MySQL.
Dos recursos basados en disco muy importantes que gestiona el
motor de almacenamiento InnoDB
son sus ficheros
de datos de espacios de tablas y sus ficheros de registro (log).
Si no se especifican opciones de configuración para
InnoDB
, MySQL 5.0 crea en el directorio de
datos de MySQL un fichero de datos de 10MB (autoextensible)
llamado ibdata1
y dos ficheros de registro
(log) de 5MB llamados ib_logfile0
y
ib_logfile1
.
Nota: InnoDB
dota a MySQL de un motor de almacenamiento transaccional (conforme
a ACID
) con capacidades de commit
(confirmación), rollback (cancelación) y recuperación de fallas.
Esto no es posible si el sistema
operativo subyacente y el hardware no funcionan como se requiere.
Muchos sistemas operativos o subsistemas de disco podrían diferir
o reordenar operaciones de escritura a fin de mejorar el
rendimiento. En algunos sistemas operativos, la propia llamada del
sistema (fsync()
), que debería esperar hasta
que todos los datos no guardados de un fichero se graben a disco,
en realidad puede retornar antes de que los datos se guarden en
las tablas de almacenamiento. Debido a esto, una caída del
sistema operativo o un corte en el suministro eléctrico pueden
destruir datos recientemente grabados, o, en el peor de los casos,
corromper la base de datos debido a que las operaciones de
escritura han sido reordenadas. Si la integridad de los datos es
importante, se deberían llevar a cabo algunas pruebas que simulen
caídas (“pull-the-plug”) e interrupciones súbitas,
antes de comenzar el uso para producción. En Mac OS X 10.3 y
posteriores, InnoDB emplea un método especial de volcado a
fichero llamado fcntl()
. Bajo Linux, es
aconsejable deshabilitar el write-back
cache.
En discos duros ATAPI, un comando como hdparm -W0
/dev/
puede funcionar.
Hay que tener en cuenta que algunas unidades
o controladores de disco podrían estar imposibilitados de
desactivar el write-back cache.
hda
Nota: Para obtener un buen
desempeño, se deberían proveer expresamente los parámetros de
InnoDB
como se explica en los siguientes
ejemplos. Naturalmente, habrá que editar la configuración para
acomodarla a los requerimientos del hardware en uso.
Para configurar los ficheros de espacio de tablas de
InnoDB
, debe utilizarse la opción
innodb_data_file_path
en la sección
[mysqld]
del fichero de opciones
my.cnf
. En Windows, se puede emplear en su
lugar my.ini
. El valor de
innodb_data_file_path
debería ser una lista de
una o más especificaciones de ficheros. Si se incluirá más de
un fichero de datos, habrá que separarlos con punto y coma
(';
'):
innodb_data_file_path=espec_fichero_datos1
[;espec_fichero_datos2
]...
Por ejemplo, la siguiente es una configuración que creará explícitamente un espacio de tablas con las mismas características que el predeterminado:
[mysqld] innodb_data_file_path=ibdata1:10M:autoextend
Esto configura un único fichero de 10MB llamado
ibdata1
el cual es autoextensible. No se
suministra la ubicación del fichero, por lo tanto, el directorio
predeterminado es el directorio de datos de MySQL.
El tamaño del fichero se especifica empleando como sufijo las
letras M
o G
para indicar
unidades de MB o GB.
A continuación se configura un espacio de tablas que contiene un
fichero de datos de tamaño fijo de 50MB llamado
ibdata1
y un fichero autoextensible de 50MB
llamado ibdata2
, ambos en el directorio de
datos:
[mysqld] innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
La sintaxis completa para especificar un fichero de datos incluye el nombre del fichero, su tamaño, y varios atributos opcionales:
nombre_de_fichero
:tamaño_de_fichero
[:autoextend[:max:tamaño_máximo_de_fichero
]]
El atributo autoextend
y aquellos que lo siguen
sólo pueden emplearse con el último fichero en la línea de
innodb_data_file_path
.
Si se especifica la opción autoextend
para el
último fichero de datos, InnoDB
incrementará
el tamaño del fichero si se queda sin capacidad para el espacio
de tablas. El incremento es de 8MB cada vez.
Si se agotara la capacidad del disco, podría desearse agregar
otro fichero de datos en otro disco. Las instrucciones para
reconfigurar un espacio de tablas existente se encuentran en
Sección 15.7, “Añadir y suprimir registros y ficheros de datos InnoDB
”.
InnoDB
no detecta el tamaño máximo de
fichero, por lo tanto, hay que ser cuidadoso en sistemas de
ficheros donde el tamaño máximo sea de 2GB. Para especificar el
tamaño máximo de un fichero autoextensible, se emplea el
atributo max
. La siguiente configuración le
permite a ibdata1
crecer hasta un límite de
500MB:
[mysqld] innodb_data_file_path=ibdata1:10M:autoextend:max:500M
InnoDB
crea los ficheros de espacios de tablas
en el directorio de datos de MySQL en forma predeterminada. Para
especificar una ubicación expresamente, se emplea la opción
innodb_data_home_dir
. Por ejemplo, para crear
dos ficheros llamados ibdata1
e
ibdata2
pero creándolos en el directorio
/ibdata
, InnoDB
se
configura de este modo:
[mysqld] innodb_data_home_dir = /ibdata innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
Nota: InnoDB
no crea directorios, de modo que hay que estar seguro de que el
directorio /ibdata
existe antes de iniciar el
servidor. Esto se aplica también a cualquier directorio de
ficheros de registro (log) que se configure. Para crear los
directorios necesarios se emplea el comando
mkdir
que existe en Unix y DOS.
InnoDB
forma el directorio para cada fichero de
datos concatenando el valor textual de
innodb_data_home_dir
con el nombre del fichero,
agregando una barra o barra invertida entre ellos si se necesita.
Si la opción innodb_data_home_dir
no aparece
en my.cnf
, el valor predeterminado es el
directorio ./
, lo cual indica el directorio
de datos de MySQL.
Si se especifica una cadena vacía en
innodb_data_home_dir
, se pueden especificar
rutas absolutas para los ficheros de datos listados en el valor de
innodb_data_file_path
. El siguiente ejemplo es
equivalente al anterior:
[mysqld] innodb_data_home_dir = innodb_data_file_path=/ibdata/ibdata1:50M;/ibdata/ibdata2:50M:autoextend
Un ejemplo sencillo de
my.cnf
. Suponiendo que se posee
un ordenador con 128MB de RAM y un disco duro, el siguiente
ejemplo muestra posibles parámetros de configuración
InnoDB
en my.cnf
o
my.ini
incluyendo el atributo
autoextend
.
Este ejemplo satisface las necesidades de la mayoría de los
usuarios, tanto en Unix como en Windows, que no deseen distribuir
los ficheros de datos InnoDB
en varios discos.
Crea un fichero de datos autoextensible llamado
ibdata1
y dos ficheros de registro (log) de
InnoDB
llamados
ib_logfile0
y
ib_logfile1
en el directorio de datos de
MySQL. También, el fichero de registros archivados de
InnoDB
ib_arch_log_0000000000
que MySQL crea
automáticamente, termina ubicado en el directorio de datos.
[mysqld] # Las demas opciones del servidor MySQL pueden escribirse aquí # ... # Los ficheros de datos deben ser capaces de contener datos e índices # Hay que asegurarse de tener suficiente espacio en disco. innodb_data_file_path = ibdata1:10M:autoextend # # Establecer el tamaño del buffer en un 50-80% de la memoria del ordenador set-variable = innodb_buffer_pool_size=70M set-variable = innodb_additional_mem_pool_size=10M # # Establecer el tamaño del fichero de registro (log) en un 25% del tamaño del buffer set-variable = innodb_log_file_size=20M set-variable = innodb_log_buffer_size=8M # innodb_flush_log_at_trx_commit=1
Hay que asegurarse de que el servidor MySQL tiene los derechos de acceso apropiados para crear ficheros en el directorio de datos. Más generalmente, el servidor debe tener derechos de acceso a cualquier directorio donde necesite crear ficheros de datos o registro (logs).
Notar que los ficheros de datos deben ser menores de 2GB en algunos sistemas de ficheros. El tamaño combinado de los ficheros de registro debe ser menor de 4GB. El tamaño combinado de los ficheros de datos debe ser de por lo menos 10MB.
Cuando se crea un espacio de tablas InnoDB
por
primera vez, es mejor iniciar el servidor MySQL desde la línea de
comandos. Entonces, InnoDB
imprimirá en
pantalla la información acerca de la creación de bases de datos,
de forma que se podrá ver lo que está ocurriendo. Por ejemplo,
en Windows, si mysqld-max se ubica en
C:\mysql\bin
, se puede iniciar de este modo:
C:\> C:\mysql\bin\mysqld-max --console
Si no se envía la salida del servidor a la pantalla, se puede ver
el fichero de registro de errores del servidor para averiguar lo
que InnoDB
imprime durante el proceso de
inicio.
Consulte Sección 15.5, “Crear el espacio de tablas InnoDB
” para un ejemplo de cómo
debería lucir la información mostrada por
InnoDB
.
¿Dónde deben especificarse las opciones en Windows? Las reglas para ficheros de opciones en Windows son las siguientes:
Solo debe crearse el fichero my.cnf
o
my.ini
, pero no los dos.
El fichero my.cnf
debe colocarse en el
directorio raíz de la unidad C:
.
El fichero my.ini
debería colocarse en
el directorio WINDIR
; por ejemplo,
C:\WINDOWS
o
C:\WINNT
. Puede utilizarse el comando
SET
en una ventana de consola para mostrar
el valor de WINDIR
:
C:\> SET WINDIR windir=C:\WINNT
Si el ordenador emplea un gestor de arranque donde la unidad
C:
no es la unidad de arranque, sólo es
posible emplear el fichero my.ini
.
Si se instaló MySQL empleando los asistentes de instalación
y configuración, el fichero my.ini
se
ubica en el directorio de instalación de MySQL. Consulte
Sección 2.3.5.14, “Dónde está el fichero my.ini”.
¿Dónde deben especificarse las opciones en Unix? En Unix, mysqld lee las opciones en los siguientes ficheros, si existen, en el siguiente orden:
/etc/my.cnf
Opciones globales.
$MYSQL_HOME/my.cnf
Opciones específicas del servidor.
defaults-extra-file
El fichero especificado con la opción
--defaults-extra-file
.
~/.my.cnf
Opciones específicas del usuario.
MYSQL_HOME
representa una variable de entorno
la cual contiene la ruta al directorio que hospeda al fichero
específico de servidor my.cnf
.
Si se desea estar seguro de que mysqld lee sus
opciones únicamente desde un fichero determinado, se puede
emplear --defaults-option
como la primera
opción en la línea de comandos cuando se inicia el servidor:
mysqld --defaults-file=ruta_a_my_cnf
Un ejemplo avanzado de
my.cnf
. Suponiendo que se posee
un ordenador Linux con 2GB de RAM y tres discos duros de 60GB (en
los directorios /
, /dr2
y /dr3
). El siguiente ejemplo muestra
posibles parámetros de configuración InnoDB
en my.cnf
.
[mysqld] # Las demas opciones del servidor MySQL pueden escribirse aquí # ... innodb_data_home_dir = # # Los ficheros de datos deben ser capaces de contener datos e índices innodb_data_file_path = /ibdata/ibdata1:2000M;/dr2/ibdata/ibdata2:2000M:autoextend # # Establecer el tamaño del buffer en un 50-80% de la memoria del ordenador, # pero hay que asegurarse que en Linux x86 el uso total de memoria es < 2GB innodb_buffer_pool_size=1G innodb_additional_mem_pool_size=20M innodb_log_group_home_dir = /dr3/iblogs # innodb_log_files_in_group = 2 # # Establecer el tamaño del fichero de registro (log) en un 25% del tamaño del buffer innodb_log_file_size=250M innodb_log_buffer_size=8M # innodb_flush_log_at_trx_commit=1 innodb_lock_wait_timeout=50 # # Quitar marca de comentario a las siguientes lineas si se desea usarlas #innodb_thread_concurrency=5
Nótese que el ejemplo ubica los dos ficheros de datos en discos
diferentes. InnoDB
llena el espacio de tablas
comenzando por el primer fichero de datos. En algunos casos, el
rendimiento de la base de datos mejorará si no se colocan todos los datos
en el mismo disco físico. Colocar los ficheros de registro (log) en un
disco diferente a los datos, a menudo es beneficioso para el rendimiento.
También se pueden utilizar dispositivos en bruto (raw devices) como
ficheros de datos InnoDB
, lo cual mejorará la velocidad
de E/S. Consulte Sección 15.14.2, “Usar dispositivos en bruto (raw devices) para espacios de tablas”.
Advertencia: En GNU/Linux x86 de 32 bits,
se debe tener cuidado con no establecer el uso de memoria en un número
demasiado alto. glibc
le puede permitir al heap de
proceso que crezca por sobre la pila de los subprocesos, lo cual hará caer el
servidor. Es arriesgado que el resultado del siguiente cálculo exceda los
2GB:
innodb_buffer_pool_size + key_buffer_size + max_connections*(sort_buffer_size+read_buffer_size+binlog_cache_size) + max_connections*2MB
Cada hilo emplea una pila (a menudo de 2MB, pero de solamente 256KB en los
binarios de MySQL AB) y en el peor caso también empleará una cantidad de
memoria adicional igual a sort_buffer_size +
read_buffer_size
.
Compilando MySQL por sí mismo, el usuario puede emplear hasta 64GB de
memoria física en Windows de 32 bits. Consulte la descripción de
innodb_buffer_pool_awe_mem_mb
en
Sección 15.4, “Opciones de arranque de InnoDB
”.
¿Cómo deben ajustarse otros parámetro del servidor mysqld? Los siguientes son valores típicos adecuados para la mayoría de los usuarios:
[mysqld]
skip-external-locking
max_connections=200
read_buffer_size=1M
sort_buffer_size=1M
#
# Establecer key_buffer a un 5 - 50% de la RAM., dependiendo de cuánto se usen
# tablas MyISAM, pero manteniendo key_buffer_size + InnoDB
# buffer pool size < 80% de la RAM
key_buffer_size=value
Esta sección describe las opciones de servidor relacionadas con
InnoDB
. En MySQL 5.0, todas son especificadas
con la forma
--
en la línea de comandos o en ficheros de opciones.
opt_name
=value
innodb_additional_mem_pool_size
El tamaño del pool de memoria que InnoDB
utiliza
para almacenar información del diccionario de datos y otras
estructuras de datos internas. Mientras más tablas se tengan en la
aplicación, mayor será este tamaño. Si InnoDB
se
queda sin memoria en este pool, comenzará a tomar memoria del sistema
operativo, y dejará mensajes de advertencia en el log de errores de
MySQL. El valor por defecto es 1MB.
innodb_autoextend_increment
El tamaño a incrementar (en megabytes) cuando se extiende el tamaño de un espacio de tablas autoextensible, luego de llenarse. El valor por defecto es 8. Esta opción puede cambiarse en tiempo de ejecución como una variable de sistema global.
innodb_buffer_pool_awe_mem_mb
El tamaño (en MB) del pool de buffer, si está ubicado en la memoria
AWE en Windows de 32 bits, y sólo relevante en este tipo de sistemas
operativos. Si el sistema operativo Windows de 32 bits en uso soporta
más de 4GB de memoria, usualmente llamado “Address Windowing
Extensions”, se puede ubicar el pool del buffer de
InnoDB
dentro de la memoria física AWE utilizando
este parámetro. El máximo valor posible es de 64000. Si se especifica
este parámetro, innodb_buffer_pool_size
es la
ventana en el espacio de direcciones de 32 bits de
mysqld donde InnoDB
direcciona
la memoria AWE. Un valor adecuado para
innodb_buffer_pool_size
son 500MB.
innodb_buffer_pool_size
El tamaño del buffer de memoria que InnoDB
emplea
para el almacenamiento intermedio de los datos e índices de sus
tablas. Mientras más grande sea este valor, menores operaciones de E/S
en disco serán necesarias para acceder a los datos de las tablas. En
un servidor de bases de datos dedicado, se puede establecer este valor
en hasta el 80% de la memoria física del ordenador. Sin embargo, no
debe establecerse en un valor demasiado grande porque la pugna por la
memoria física podría causar que el sistema oeprativo comience a
paginar.
innodb_checksums
InnoDB
emplea validación por sumas de verificación
(checksums) en todas las páginas leídas desde el disco, para asegurar
una tolerancia extra contra fallas frente a hardware averiado o
ficheros corruptos. Sin embargo, bajo ciertas circunstancias
inusuales (por ejemplo al ejecutar pruebas de rendimiento) esta
característica extra de seguridad es innecesaria. En tales casos, esta
opción (que está habilitada por defecto) puede deshabilitarse con
--skip-innodb-checksums
. Esta opción fue agregada
en MySQL 5.0.3.
innodb_data_file_path
Las rutas a los ficheros individuales de datos y sus tamaños. La ruta
de directorio completa a cada fichero de datos se obtiene concatenando
innodb_data_home_dir
con cada ruta especificada
aquí. Los tamaños de fichero se especifican en megabytes o gigabytes
(1024MB) agregando M
o
G
al valor que representa el tamaño. La sumatoria
de los tamaños de fichero debe ser de al menos 10MB. En algunos
sistemas operativos, los ficheros deben tener menos de 2GB. Si no se
indica innodb_data_file_path
, el comportamiento
predeterminado de inicio es crear un único fichero autoextensible de
10MB llamado ibdata1
. En aquellos sistemas
operativos que soporten ficheros grandes, se puede establecer el tamaño
de fichero en más de 4GB. También pueden utilizarse como ficheros de
datos particiones de dispositivos en bruto. Consulte Sección 15.14.2, “Usar dispositivos en bruto (raw devices) para espacios de tablas”.
innodb_data_home_dir
La porción común de la ruta de directorio para todos los ficheros de
datos InnoDB
. Si este valor no se establece, por
defecto será el directorio de datos de MySQL. También puede
especificarse como una cadena vacía, en cuyo caso se podrán utilizar
rutas absolutas en innodb_data_file_path
.
innodb_doublewrite
Por defecto, InnoDB
almacena todos los datos dos
veces, la primera en el buffer de escritura doble (o doublewrite), y
luego a los ficheros de datos reales. Esta opción puede emplearse para
desactivar dicha funcionalidad. Al igual que
innodb_checksums
, esta opción está habilitada por
defecto; puede desactivarse con
--skip-innodb-doublewrite
en pruebas de rendimiento
o casos en que el máximo desempeño prevalezca sobre la preocupacion
por la integridad de los datos o las posibles fallas. Esta opción se
agregó en MySQL 5.0.3.
innodb_fast_shutdown
Si se establece a 0, InnoDB
efectúa una descarga
completa y vuelca los buffers de inserción antes de llevar a cabo el
cierre del servidor. Estas operaciones pueden tomar minutos o incluso
horas en casos extremos. Si se establece en 1,
InnoDB
pasa por alto estas operaciones al cierre.
El valor por defecto es 1. Si se establece en 2 (opción que está
disponible desde MySQL 5.0.5, excepto en Netware), InnoDB simplemente
vuelca a disco sus registros (logs) y se cierra en frío, como si
hubiera ocurrido una caída; ninguna transacción confirmada se perderá,
pero en el siguiente inicio se ejecutará una recuperación ante caídas.
innodb_file_io_threads
El número de subprocesos (threads) de E/S de fichero en
InnoDB
. Normalmente esto debería ser dejado en el
valor predeterminado de 4, pero la E/S de disco en Windows puede
beneficiarse con un número mayor. En Unix, incrementar el número no
tiene efecto; InnoDB
siempre utiliza el valor
predeterminado.
innodb_file_per_table
Esta opción provoca que InnoDB
cree cada
nueva tabla utilizando su propio fichero .ibd
para almacenar datos e índices, en lugar de colocarlo en el espacio de
tablas compartidas. Consulte Sección 15.6.6, “Usar un espacio de tablas para cada tabla”.
innodb_flush_log_at_trx_commit
Cuando innodb_flush_log_at_trx_commit
se establece
en 0, una vez por segundo el buffer de registros (log buffer) se graba
en el fichero de registro y se vuelca a disco, pero no se hace nada al
confirmar una transacción. Cuando este valor es 1 (predeterminado),
cada vez que se confirma una transacción el buffer de registros
(log buffer) se graba en el fichero de registro y se vuelca a disco
Cuando se establece en 2, el buffer de registros (log buffer) se graba
en el fichero de registro, pero no se vuelca a disco. Sin embargo, el
volcado a disco del fichero de registro se produce una vez por segundo
también cuando vale 2. Se debe tener en cuenta que el volcado una vez
por segundo no está 100% garantizado que ocurra en cada segundo,
debido a cuestiones de programación (scheduling) de procesos. Se puede
alcanzar un mayor rendimiento estableciendo un valor diferente de 1,
pero en caso de caída se puede perder un segundo de transacciones. Si
se establece el valor en 0, cualquier caída en un proceso de
mysqld puede borrar el último segundo de
transacciones. Si se establece el valor en 2, entonces únicamente una
caída del sistema operativo o una interrupción de energía pueden
borrar el último segundo de transacciones. Hay que notar que muchos
sistemas operativos y algunos tipos de discos puedne ser engañosos en
las operaciones de volcado a disco. Podrían indicarle a
mysqld que el volcado ha tenido lugar, aunque no
sea así. En tal caso la estabilidad de las transacciones no está
garantizada ni aún con el valor 1, y en el peor de los casos una
interrupción de energía puede incluso corromper la base de datos
InnoDB. Utilizar un caché de disco apoyado por baterías en el
controlador de disco SCSI o en el propio disco, acelera los volcados a
disco, y hace más segura la operación. También puede intentarse con el
comando de Unix hdparm, el cual deshabilita el
almacenamiento en caches de hardware de las operaciones de escritura
a disco, o utilizar algún otro comando específico del fabricante del
hardware. El valor por defecto de esta opción es 1
innodb_flush_method
Esta opción solamente es relevante en sistemas Unix. Si se establece
en fdatasync
(el valor predeterminado),
InnoDB
utiliza fsync()
para
volcar tanto los ficheros de datos como de registro (log). Si se
establece en O_DSYNC
, InnoDB
emplea O_SYNC
para abrir y volcar los ficheros de
registro, pero utiliza fsync()
para volcar los
ficheros de datos. Si se especifica O_DIRECT
(disponible en algunas versiones de GNU/Linux),
InnoDB
utiliza O_DIRECT
para
abrir los ficheros de datos, y fsync()
para volcar
tanto los ficheros de datos como de registro. Nótese que
InnoDB
emplea fsync()
en lugar
de fdatasync()
, y no emplea
O_DSYNC
por defecto porque han ocurrido problemas
con éste en muchas variedades de Unix.
innodb_force_recovery
Advertencia: Esta opción debería ser definida solamente en una
situación de emergencia cuando se desean volcar las tablas desde una
base de datos corrupta. Los posibles valores van de 1 a 6. Los
significados de estos valores se describen en Sección 15.8.1, “Forzar una recuperación”. Como una medida de seguridad,
InnoDB
impide que un usuario modifique datos cuando
esta opción tiene un valor mayor a 0.
innodb_lock_wait_timeout
El límite de tiempo, en segundos, que una transacción
InnoDB
puede esperar por un bloqueo antes de ser
cancelada. InnoDB
automáticamente detecta bloqueos
mutuos (deadlocks) en su propia tabla de bloqueos, y cancela la
transacción. InnoDB detecta los bloqueos por el uso de la sentencia LOCK
TABLES
. El valor predeterminado es de 50 segundos.
Para conseguir la mayor estabilidad y consistencia posibles en una
configuración de replicación, se debería utilizar
innodb_flush_logs_at_trx_commit=1
,
sync-binlog=1
, y
innodb_safe_binlog
en el fichero
my.cnf
principal.
innodb_locks_unsafe_for_binlog
Esta opción desactiva el bloqueo de la clave siguiente en búsquedas y
exploraciones de índices InnoDB
. El valor por
defecto de esta opción es falso.
Normalmente, InnoDB
utiliza un algoritmo denominado
bloqueo de clave siguiente (next-key).
InnoDB
efectúa un bloqueo a nivel de fila de tal forma que cuando busca o
explora el índice de una tabla, establece bloqueos compartidos o
exclusivos en cualquier registro de índice que encuentre. El bloqueo
que InnoDB
establece en registros de índice también
afecta al “vacío” que precede a ese registro. Si un usuario
tiene un bloqueo compartido o exclusivo sobre el registro
R en un índice, otro usuario no puede insertar un
nuevo registro de índice inmediatamente antes de
R en el orden del índice. Esta opción provoca que
InnoDB
no utilice el bloqueo de clave siguiente en
búsquedas o exploraciones de índices. El bloqueo de clave siguiente es
todavía utilizado para asegurar las restricciones de claves foráneas y
la verificación de claves duplicadas. Nótese que el uso de esta opción
puede provocar problemas secundarios: suponiendo que se deseen leer y
bloquear todos los registros hijos de la tabla child
que tengan un identificador mayor a 100, junto al posterior intento de
actualizar algunas columnas en las filas seleccionadas:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
Supóngase que hay un índice sobre la columna id
. La
consulta explora aquel índice comenzando por el primer registro en que
id
sea mayor que 100. Si el bloqueo efectuado sobre
los registros del índice no bloquea las inserciones realizadas en los
espacios vacíos, en la tabla se insertará un nuevo registro. Si se
ejecuta el mismo SELECT
dentro de la misma
transacción, se verá un nuevo registro en el conjunto de resultados
devuelto por la consulta. Esto también significa que si se agregan
nuevos elementos a la base de datos, InnoDB no garantiza la
serialización; sin embargo, los conflictos de serialización aún están
garantizados. Por lo tanto, si esta opción se utiliza, InnoDB garantiza
como mucho el nivel de aislamiento READ COMMITTED
.
A partir de MySQL 5.0.2 esta opción es aún más insegura.
InnoDB
en un UPDATE
o
DELETE
solamente bloquea los registros que se
actualizan o borran. Esto reduce notablemente la probabilidad de
bloqueos mutuos (deadlocks), pero aún pueden ocurrir. Nótese que esta
opción todavía no le permite a operaciones como
UPDATE
predominar sobre otras operaciones similares
(como otro UPDATE
) aún en el caso en que actúen
sobre registros diferentes. Considérese lo siguiente:
example:
CREATE TABLE A(A INT NOT NULL, B INT); INSERT INTO A VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;
Si una conexión realiza una consulta:
SET AUTOCOMMIT = 0; UPDATE A SET B = 5 WHERE B = 3;
y la otra conexión ejecuta otra consulta a continuación de la primera:
SET AUTOCOMMIT = 0; UPDATE A SET B = 4 WHERE B = 2;
La consulta dos tendrá que esperar la confirmación o la cancelación de
la consulta uno, porque ésta tiene un bloqueo exclusivo en el registro
(2,3), y la consulta dos, mientras explora los registros,
también intenta colocar un bloqueo exclusivo en la misma fila, cosa
que no puede hacer. Esto se debe a que la consulta dos primero
establece el bloqueo sobre un registro y luego determina si el
registro pertenece al conjunto de resultados, y si no es así libera el
bloqueo innecesario, cuando se emplea la opción
innodb_locks_unsafe_for_binlog
.
Por lo tanto, la consulta uno se ejecuta de este modo:
x-lock(1,2) unlock(1,2) x-lock(2,3) update(2,3) to (2,5) x-lock(3,2) unlock(3,2) x-lock(4,3) update(4,3) to (4,5) x-lock(5,2) unlock(5,2)
entonces la consulta dos se ejecuta así:
x-lock(1,2) update(1,2) to (1,4) x-lock(2,3) - wait for query one to commit or rollback
innodb_log_arch_dir
El directorio donde los ficheros de registro (logs) terminados se
archivarán si se utiliza el archivo de ficheros de registro. Si se
utiliza, el valor de este parámetro debería ser el mismo que
innodb_log_group_home_dir
. Sin embargo, no es
requerido.
innodb_log_archive
Este valor generalmente debería establecerse a 0. Debido a que la
recuperación a partir de una copia de respaldo es realizada por MySQL
empleando sus propios ficheros de registro (log), en general no
hay necesidad de archivar los ficheros de registro de
InnoDB
. El valor predeterminado para esta opción es
0.
innodb_log_buffer_size
El tamaño del buffer que InnoDB
emplea para
escribir los ficheros de registro (logs) en disco. Los valores
razonables se encuentran entre 1MB y 8MB. El valor predeterminado es
1MB. Un buffer de fichero de registro grande le permite a las
transacciones extensas ejecutarse sin necesidad de guardar el fichero
de registro en disco antes de que la transacción se confirme. Por lo
tanto, si se manejan grandes transacciones, incrementar el tamaño del
buffer de ficheros de registro ahorra operaciones de E/S en disco.
innodb_log_file_size
El tamaño de cada fichero de registro (log) en un grupo de ficheros de
registro. El tamaño combinado de estos ficheros debe ser inferior a
4GB en ordenadores de 32 bits. El valor predeterminado es de 5MB. El
rango de valores razonables va desde 1MB hasta la
1/N
parte del tamaño del pool de buffer,
donde N
es la cantidad de ficheros de
registro en el grupo. Mientras mayor es el valor, menor es la cantidad
de guardado de puntos de verificación que se necesitan en el pool de
buffer, ahorrando operaciones de E/S en disco. Pero tener ficheros de
registro más grandes también significa que la recuperación es más
lenta en caso de caídas.
innodb_log_files_in_group
En un grupo de ficheros de registro (logs), es la cantidad de ficheros
que contiene.
InnoDB
escribe en los ficheros siguiendo una forma
circular. El valor predeterminado es 2 (recomendado).
innodb_log_group_home_dir
La ruta de directorio a los ficheros de registro (log) de
InnoDB
. Debe tener el mismo valor que
innodb_log_arch_dir
. Si no se especifican
parámetros de ficheros de registro InnoDB
, la
acción predeterminada es crear dos ficheros de 5MB llamados
ib_logfile0
y ib_logfile1
en
el directorio de datos de MySQL.
innodb_max_dirty_pages_pct
Un entero en el rango de 0 a 100. El valor por defecto es 90. El
subproceso (thread) principal en InnoDB
intenta
volcar páginas desde el pool de buffer de modo que a lo sumo este
porcentaje de las páginas aún sin volcar sea volcado en un momento
determinado. Si se tiene el privilegio SUPER
, este
porcentaje pude cambiarse mientras el servidor está en ejecución:
SET GLOBAL innodb_max_dirty_pages_pct = value
;
innodb_max_purge_lag
Esta opción controla la forma de retrasar las operaciones
INSERT
, UPDATE
y
DELETE
cuando las operaciones de descarga (ver
Sección 15.12, “Implementación de multiversión”) están sufiendo demoras. TEl
valor por defecto de este parámetro es cero, lo que significa que no
se retrasarán. Esta opción puede modificarse en tiempo de ejecución
como una variable global de sistema.
El sistema de transacciones de InnoDB mantiene una lista de
transacciones que tienen entradas en los índices marcadas para ser
eliminadas por operaciones
UPDATE
o DELETE
.
Se deja que la longitud de esta lista sea
purge_lag
. Cuando
purge_lag
excede a
innodb_max_purge_lag
, cada operación de
INSERT
, UPDATE
y
DELETE
se retrasa durante
((purge_lag
/innodb_max_purge_lag
)*10)-5
milisegundos. El retraso se computa en el comienzo de un lote de
depuración, cada diez segundos. Las operaciones no se retrasan si no
puede ejecutarse la depuración debido a una vista de lectura consistente
(consistent read) anterior que contenga los registros a ser depurados.
Un escenario típico para una carga de trabajo problemática podría ser 1 millón, asumiendo que las transacciones son pequeñas, sólo 100 bytes de tamaño, y se pueden permitir 100 MB de registros sin descargar en las tablas.
innodb_mirrored_log_groups
El número de copias idénticas de grupos de ficheros de registro que se mantienen para la base de datos. Actualmente debería establecerse en 1.
innodb_open_files
Esta opción sólo es relevante si se emplean múltiples espacios de
tablas en InnoDB
. Especifica el número máximo de
ficheros .ibd
que InnoDB
puede
mantener abiertos al mismo tiempo. El mínimo es 10. El valor
predeterminado es 300.
Los descriptores de fichero empleados para ficheros
.ibd
son únicamente para
InnoDB
. Son independientes de los especificados por
la opción de servidor --open-files-limit
, y no
afectan la operación del caché de tablas.
innodb_safe_binlog
Contribuye a asegurar la consistencia entre el contenido de las tablas
InnoDB
y el registro binario (binary log). Consulte
Sección 5.10.3, “El registro binario (Binary Log)”.
innodb_status_file
Esta opción provoca que InnoDB
cree un fichero
para la salida períodica de <datadir>
/innodb_status.<pid>
SHOW INNODB STATUS
.
Disponible desde MySQL 4.0.21.
innodb_table_locks
InnoDB
respeta lo establecido por LOCK
TABLES
, y MySQL no retorna desde un LOCK
TABLE .. WRITE
hasta que todos los otros flujos (threads)
han levantado sus bloqueos a la tabla. El valor por defecto es 1, lo
cual significa que LOCK TABLES
causará que InnoDB
bloquee una tabla internamente. En aplicaciones que emplean
AUTOCOMMIT=1
, los bloqueos internos de tabla de
InnoDB pueden originar bloqueos mutuos (deadlocks). Se puede
establecer innodb_table_locks=0
en
my.cnf
(o my.ini
en
Windows) para eliminar ese problema.
innodb_thread_concurrency
InnoDB
intenta mantener el número de flujos
(threads) del sistema operativo que concurren dentro de
InnoDB
en un valor menor o igual al límite
especificado por este parámetro. Antes de MySQL 5.0.8, el valor por
defecto es 8. Si se tienen dificultades de rendimiento, y SHOW INNODB
STATUS
indica que hay muchos subprocesos esperando por
semáforos, se podrían tener subprocesos pugnando por recursos, y se
debería establecer este parámetro en un número mayor o menor. Si se
posee un ordenador con varios procesadores y discos, se puede intentar
aumentar el valor para hacer mejor uso de los recursos del ordenador.
Un valor recomendado es la suma del número de procesadores y discos
que tiene el sistema. Un valor de 500 o mayor deshabilitará la
verificación de concurrencia. A partir de MySQL 5.0.8, el valor por
defecto es 20, y la verificación de concurrencia se deshabilita si se
establece en 20 o más.
innodb_status_file
Esta opción provoca que InnoDB
cree un fichero
para almacenar periódicamente la salida de <datadir>
/innodb_status.<pid>
SHOW INNODB
STATUS
.
Suponiendo que se ha instalado MySQL y se editó el fichero de opciones
para que contenga los parámetros de InnoDB
necesarios,
antes de iniciar MySQL se debería verificar que los directorios indicados
para los ficheros de datos y de registro (log) InnoDB
existen y que el servidor MySQL tiene permisos de acceso a dichos
directorios. InnoDB
no puede crear directorios,
solamente ficheros. Hay que verificar también que se tiene suficiente
espacio en disco para los ficheros de datos y de registro.
Cuando se crea una base de datos InnoDB
, es mejor
ejecutar el servidor MySQL mysqld desde la línea de
comandos, no desde el envoltorio mysqld_safe o como
un servicio de Windows. Cuando se lo ejecuta desde la línea de comandos,
se puede ver lo que mysqld imprime y qué está
ocurriendo. En Unix, simplemente debe invocarse mysqld.
En Windows, hay que usar la opción --console
.
Cuando se inicia el servidor MySQL luego de la configuración inicial de
InnoDB
en el fichero de opciones,
InnoDB
crea los ficheros de datos y de registro e
imprime algo como lo siguiente:
InnoDB: The first specified datafile /home/heikki/data/ibdata1 did not exist: InnoDB: a new database to be created! InnoDB: Setting file /home/heikki/data/ibdata1 size to 134217728 InnoDB: Database physically writes the file full: wait... InnoDB: datafile /home/heikki/data/ibdata2 did not exist: new to be created InnoDB: Setting file /home/heikki/data/ibdata2 size to 262144000 InnoDB: Database physically writes the file full: wait... InnoDB: Log file /home/heikki/data/logs/ib_logfile0 did not exist: new to be created InnoDB: Setting log file /home/heikki/data/logs/ib_logfile0 size to 5242880 InnoDB: Log file /home/heikki/data/logs/ib_logfile1 did not exist: new to be created InnoDB: Setting log file /home/heikki/data/logs/ib_logfile1 size to 5242880 InnoDB: Doublewrite buffer not found: creating new InnoDB: Doublewrite buffer created InnoDB: Creating foreign key constraint system tables InnoDB: Foreign key constraint system tables created InnoDB: Started mysqld: ready for connections
Se ha creado una nueva base de datos InnoDB
. Se puede
conectar al servidor MySQL con los programas cliente acostumbrados, como
mysql. Cuando se detiene el servidor MySQL, con
mysqladmin shutdown, la salida es como la siguiente:
010321 18:33:34 mysqld: Normal shutdown 010321 18:33:34 mysqld: Shutdown Complete InnoDB: Starting shutdown... InnoDB: Shutdown completed
Se puede mirar en los directorios de ficheros de datos y registro y se
verán los ficheros creados. El directorio de registro (log) también
contiene un pequeño fichero llamado
ib_arch_log_0000000000
. Ese fichero resulta de la
creación de la base de datos, luego de lo cual InnoDB
desactivó el guardado de registros (log). Cuando MySQL inicia de nuevo,
los ficheros de datos y de registro ya han sido creados, por lo que la
salida es más breve:
InnoDB: Started mysqld: ready for connections
Es posible agregar la opción innodb_file_per_table
a
my.cnf
, y hacer que InnoDB almacene cada tabla en su
propio fichero .ibd
en un directorio de bases de
datos de MySQL. Consulte Sección 15.6.6, “Usar un espacio de tablas para cada tabla”.
Si InnoDB
imprime un error de sistema operativo en
una operación de ficheros, generalmente el problema es uno de los
siguientes:
No se creó el directorio para los ficheros de datos o de registros
(log) de InnoDB
.
mysqld no tiene los permisos de acceso para crear ficheros en aquellos directorios.
mysqld no puede leer el fichero de opciones
my.cnf
o my.ini
adecuado,
y por lo tanto no ve las opciones especificadas.
El disco está lleno o se excedió la cuota de disco.
Se ha creado un subdirectorio que tiene el mismo nombre que uno de los ficheros de datos especificados.
Hay un error de sintaxis en innodb_data_home_dir
o innodb_data_file_path
.
Si algo va mal durante el intento de InnoDB
de
inicializar el espacio de tablas o los ficheros de registro, se deberán
borrar todos los ficheros creados por InnoDB
. Esto
comprende todos los ficheros ibdata
y todos los
ib_logfile
. En caso de haber creado alguna tabla
InnoDB
, habrá que borrar del directorio de datos de MySQL
los correspondientes
ficheros .frm
de estas tablas (y cualquier fichero
.ibd
si se están empleando múltiples espacios de
tablas). Entonces puede intentarse nuevamente la creación de la base de
datos InnoDB
. Es mejor iniciar el servidor MySQL
desde una línea de comandos de modo que pueda verse lo que ocurre.
Suponiendo que se ha iniciado el cliente MySQL con el comando
mysql test
, para crear una tabla
InnoDB
se debe especificar la opción
ENGINE = InnoDB
o
TYPE = InnoDB
en la sentencia SQL de creación de tabla:
CREATE TABLE customers (a INT, b CHAR (20), INDEX (a)) ENGINE=InnoDB; CREATE TABLE customers (a INT, b CHAR (20), INDEX (a)) TYPE=InnoDB;
La sentencia SQL crea una tabla y un índice en la columna
a
en el espacio de tablas InnoDB
que
consiste en los ficheros de datos especificados en
my.cnf
. Adicionalmente, MySQL crea un fichero
customers.frm
en el directorio
test
debajo del directorio de bases de datos de
MySQL. Internamente, InnoDB
agrega a su propio
diccionario de datos una entrada para la tabla
'test/customers'
. Esto significa que puede crearse una
tabla con el mismo nombre customers
en otra base de
datos, y los nombres de las tablas no entrarán en conflicto dentro de
InnoDB
.
Se puede consultar la cantidad de espacio libre en el espacio de tablas
InnoDB
dirigiendo una sentencia SHOW
TABLE STATUS
para cualquier tabla InnoDB
. La
cantidad de espacio libre en el espacio de tablas aparece en la sección
Comment
en la salida de SHOW
TABLE STATUS
. Un ejemplo:
SHOW TABLE STATUS FROM test LIKE 'customers'
Nótese que las estadísticas que SHOW
muestra acerca de
las tablas InnoDB
son solamente aproximadas. Se
utilizan en la optimización SQL. No obstante, los tamaños en bytes
reservados para las tablas e índices son exactos.
En forma predeterminada, cada cliente se que conecta al servidor MySQL
comienza con el modo de autocommit habilitado, lo cual automáticamente
confirma (commit) cada sentencia SQL ejecutada. Para utilizar
transacciones de múltiples sentencias se puede deshabilitar el modo
autocommit con la sentencia SQL SET AUTOCOMMIT = 0
y
emplear COMMIT
y ROLLBACK
para
confirmar o cancelar la transacción. Si se desea dejar activado
autocommit, se pueden encerrar las transacciones entre las sentencias
START TRANSACTION
y
COMMIT
o ROLLBACK
. El siguiente
ejemplo muestra dos transacciones. La primera se confirma, la segunda se
cancela.
shell> mysql test Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 5 to server version: 3.23.50-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> CREATE TABLE CUSTOMER (A INT, B CHAR (20), INDEX (A)) -> ENGINE=InnoDB; Query OK, 0 rows affected (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO CUSTOMER VALUES (10, 'Heikki'); Query OK, 1 row affected (0.00 sec) mysql> COMMIT; Query OK, 0 rows affected (0.00 sec) mysql> SET AUTOCOMMIT=0; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO CUSTOMER VALUES (15, 'John'); Query OK, 1 row affected (0.00 sec) mysql> ROLLBACK; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM CUSTOMER; +------+--------+ | A | B | +------+--------+ | 10 | Heikki | +------+--------+ 1 row in set (0.00 sec) mysql>
En APIs como PHP, Perl DBI/DBD, JDBC, ODBC, o la interface de llamadas C
estándar de MySQL, se pueden enviar sentencias de control de
transacciones como COMMIT
al servidor MySQL en forma
de cadenas, igual que otras sentencias SQL como
SELECT
o INSERT
. Algunas APIs
también ofrecen funciones o métodos especiales para confirmación y
cancelación de transacciones.
Importante: No se deben convertir las tablas del sistema en la base de
datos mysql
(por ejemplo, user
o
host
) al tipo InnoDB
. Las tablas
del sistema siempre deben ser del tipo MyISAM
.
Si se desea que todas las tablas que no sean de sistema se creen como
tablas InnoDB
, simplemente debe agregarse la línea
default-table-type=innodb
a la sección
[mysqld]
del fichero my.cnf
o my.ini
.
InnoDB
no posee una optimización especial para la
creación separada de índices en la misma forma que la tiene el motor de
almacenamiento MyISAM
. Por lo tanto, no hay beneficio
en exportar e importar la tabla y crear los índices posteriormente. La
manera más rápida de cambiar una tabla al motor
InnoDB
es hacer las inserciones directamente en una
tabla InnoDB
. Es decir, utilizar ALTER
TABLE ... ENGINE=INNODB
, o crear una tabla
InnoDB
vacía con idénticas definiciones e insertar
las filas con INSERT INTO ... SELECT * FROM
...
.
Si se tienen restricciones UNIQUE
sobre claves
secundarias, se puede acelerar la importación de una tabla desactivando
temporalmente la verificación de unicidad durante la sesión de
importación: SET UNIQUE_CHECKS=0;
. Para tablas
grandes, esto ahorra gran cantidad de operaciones de E/S en disco, debido
a que InnoDB
puede emplear su buffer de inserciones
para grabar registros de índices secundarios en lote.
Para obtener un mejor control sobre el proceso de inserción, podría ser mejor llenar la tablas grandes por partes:
INSERT INTO nuevatabla SELECT * FROM viejatabla WHERE clave > valor1 AND clave <= valor2;
Luego de que todos los registros se hayan insertado, se pueden renombrar las tablas.
Durante la conversión de tablas grandes, se puede reducir la cantidad de
operaciones de E/S en disco incrementando el tamaño del pool de buffer
de InnoDB
. No debe usarse más del 80% de la memoria
física. También pueden aumentarse los tamaños de los ficheros de
registro (log) de InnoDB
.
Hay que asegurarse de no llenar completamente el espacio de tablas: las
tablas InnoDB
necesitan mucho más espacio que las
tablas MyISAM
. Si una sentencia ALTER
TABLE
se queda sin espacio, realizará una cancelación
(rollback), y esto puede tomar horas si lo hace sobre el disco. Para
las inserciones, InnoDB
emplea el buffer de inserción
para combinar en lotes los registros secundarios de índices con los
índices. Esto ahorra gran cantidad de operaciones de E/S en disco.
Durante la cancelación no se emplea ese mecanismo, de modo que puede
llevar más de 30 veces el tiempo insumido por la inserción.
Si se produjera una de estas cancelaciones fuera de control, sino se tienen datos valiosos en la base de datos, puede ser preferible matar el proceso de la base de datos en lugar de esperar que se completen millones de operaciones de E/S en disco. Para el procedimiento completo, consulte Sección 15.8.1, “Forzar una recuperación”.
Si se especifica que una columna de una tabla es
AUTO_INCREMENT
, la entrada para la tabla
InnoDB
en el diccionario de datos contiene un
contador especial llamado "contador de auto incremento", que se utiliza
para asignar nuevos valores a la columna. El contador de auto incremento
se almacena sólo en la memoria principal, no en disco.
InnoDB
utiliza el siguiente algoritmo para
inicializar el contador de auto incremento para una tabla
T
que contiene una columna
AUTO_INCREMENT
llamada ai_col
:
Luego de iniciarse el servidor, cuando un usuario realiza por primera
vez una inserción en una tabla T
,
InnoDB
ejecuta el equivalente de esta sentencia:
SELECT MAX(ai_col) FROM T FOR UPDATE;
El valor retornado por la sentencia se incrementa en uno y se asigna a
la columna, y al contador de auto incremento de la tabla. Si la tabla
está vacía, se asigna el valor 1
. Si el contador aún
no se ha inicializado y se ejecuta una sentencia SHOW TABLE
STATUS
que muestre su salida para la tabla
T
, el contador se inicializa (pero no se incrementa)
y se almacena para usos posteriores. Nótese que en esta inicialización
se realiza una lectura normal con bloqueo exclusivo y el bloqueo
permanece hasta el final de la transacción.
InnoDB
sigue el mismo procedimiento para inicializar
el contador de auto incremento de una tabla recientemente creada.
Nótese que si durante un INSERT
el usuario especifica
un valor NULL
o 0
para una columna
AUTO_INCREMENT
, InnoDB
trata a la
columna como si no se hubiera especificado un valor y genera un nuevo
valor para ella.
Luego de que el contador de auto incremento ha sido inicializado, si un
usuario inserta una fila que explícitamente indica para la columna auto
incremental un valor mayor que el valor actual del contador, éste se
establece al valor actual de la columna. Si no se indica un valor,
InnoDB
incrementa el valor del contador en uno y lo
asigna a la columna.
Al acceder al contador de auto incremento, InnoDB
emplea un nivel de bloqueo de tabla especial
AUTO-INC
, que mantiene hasta el final de la sentencia
SQL actual y no hasta el final de la transacción. Esta estrategia de
bloqueo especial fue introducida para mejorar la concurrencia de
inserciones en una tabla que contiene una columna
AUTO_INCREMENT
. Dos transacciones no pueden tener el
bloqueo AUTO-INC
simultáneamente en la misma tabla.
Nótese que pueden observarse valores faltantes en la secuencia de
valores asignados a la columna AUTO_INCREMENT
si se
cancelan transacciones que ya han obtenido números desde el contador.
El comportamiento del mecanismo de auto incremento no se encuentra definido si un usuario asigna un valor negativo a la columna o si el valor se vuelve mayor que el entero más grande que puede almacenarse en el tipo de entero especificado.
A partir de MySQL 5.0.3, InnoDB
soporta la opción
AUTO_INCREMENT =
en
sentencias n
CREATE TABLE
y ALTER
TABLE
, para establecer inicialmente o modificar el valor
actual del contador. El efecto de esta acción es cancelado al reiniciar
el servidor, por las razones tratadas anteriormente en esta sección.
InnoDB
también soporta restricciones de claves
foráneas. La sintaxis para definir una restricción de clave foránea en
InnoDB
es así:
[CONSTRAINTsímbolo
] FOREIGN KEY [id
] (nombre_índice
, ...) REFERENCESnombre_de_tabla
(nombre_índice
, ...) [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}] [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
Las definiciones de claves foráneas están sujetas a las siguientes condiciones:
Ambas tablas deben ser InnoDB
y no deben ser
tablas temporales.
En la tabla que hace referencia, debe haber un índice donde las columnas de clave extranjera estén listadas en primer lugar, en el mismo orden.
En la tabla referenciada, debe haber un índice donde las columnas referenciadas se listen en primer lugar, en el mismo orden. En MySQL/InnoDB 5.0, tal índice se creará automáticamente en la tabla referenciada si no existe aún.
No están soportados los índices prefijados en columnas de claves
foráneas. Una consecuencia de esto es que las columnas
BLOB
y TEXT
no pueden
incluirse en una clave foránea, porque los índices sobre dichas
columnas siempre deben incluir una longitud prefijada.
Si se proporciona un
CONSTRAINT
,
éste debe ser único en la base de datos. Si no se suministra,
símbolo
InnoDB
crea el nombre automáticamente.
InnoDB
rechaza cualquier operación
INSERT
o UPDATE
que intente crear
un valor de clave foránea en una tabla hija sin un valor de clave
candidata coincidente en la tabla padre. La acción que
InnoDB
lleva a cabo para cualquier operación
UPDATE
o DELETE
que intente
actualizar o borrar un valor de clave candidata en la tabla padre que
tenga filas coincidentes en la tabla hija depende de la
accion referencial especificada utilizando las
subcláusulas ON UPDATE
y ON
DETETE
en la cláusula FOREIGN
KEY
. Cuando el usuario intenta borrar o actualizar una fila de
una tabla padre, InnoDB
soporta cinco acciones
respecto a la acción a tomar:
CASCADE
: Borra o actualiza el registro en la
tabla padre y automáticamente borra o actualiza los registros
coincidentes en la tabla hija. Tanto ON DELETE
CASCADE
como ON UPDATE CASCADE
están
disponibles en MySQL 5.0. Entre dos tablas, no se deberían definir
varias cláusulas ON UPDATE CASCADE
que actúen en
la misma columna en la tabla padre o hija.
SET NULL
: Borra o actualiza el registro en la
tabla padre y establece en NULL
la o las columnas
de clave foránea en la tabla hija. Esto solamente es válido si las
columnas de clave foránea no han sido definidas como NOT
NULL
. MySQL 5.0 soporta tanto ON DELETE SET
NULL
como ON UPDATE
SET NULL
.
NO ACTION
: En el estándar ANSI
SQL-92
, NO ACTION
significa
ninguna acción en el sentido de que unintento
de borrar o actualizar un valor de clave primaria no sera permitido
si en la tabla referenciada hay una valor de clave foránea
relacionado. (Gruber, Mastering
SQL, 2000:181). En MySQL 5.0, InnoDB
rechaza la operación de eliminación o actualización en la tabla
padre.
RESTRICT
: Rechaza la operación de eliminación o
actualización en la tabla padre. NO ACTION
y
RESTRICT
son similares en tanto omiten la
cláusula ON DELETE
u ON
UPDATE
. (Algunos sistemas de bases de datos tienen
verificaciones diferidas o retrasadas, una de las cuales es
NO ACTION
. En MySQL, las restricciones de claves
foráneas se verifican inmediatamente, por eso,
NO ACTION
y RESTRICT
son
equivalentes.)
SET DEFAULT
: Esta acción es reconocida por el
procesador de sentencias (parser), pero InnoDB
rechaza definiciones de tablas que contengan ON DELETE SET
DEFAULT
u ON UPDATE SET
DEFAULT
.
InnoDB
soporta las mismas opciones cuando se
actualiza la clave candidata en la tabla padre. Con
CASCADE
, las columnas de clave foránea en la tabla
hija son establecidas a los nuevos valores de la clave candidata en la
tabla padre. Del mismo modo, las actualizaciones se producen en cascada
si las columnas actualizadas en la tabla hija hacen referencia a claves
foráneas en otra tabla.
Nótese que InnoDB
soporta referencias de clave
foránea dentro de una tabla, y, en estos casos, la tabla hija realmente
significa registros dependientes dentro de la tabla.
InnoDB
necesita que haya índices sobre las claves
foráneas y claves referenciadas, así las verificaciones de claves
foráneas pueden ser veloces y no necesitan recorrer la tabla. En MySQL
5.0, el índice en la clave foránea se crea automáticamente. Esto
contrasta con versiones más antiguas (anteriores a 4.1.8), donde los
índices debían crearse explícitamente o fallaba la creación de
restricciones de claves foráneas.
Las columnas involucradas en la clave foránea y en la clave referenciada
deben tener similares tipos de datos internos dentro de
InnoDB
, de modo que puedan compararse sin una
conversión de tipo. La longitud y la condición de
con o sin signo de los tipos enteros deben ser iguales. La
longitud de los tipos cadena no necesita ser la misma. Si se especifica
una acción SET NULL
, hay que asegurarse de que
las columnas en la tabla hija no se han declarado
como NOT NULL
.
Si MySQL informa que ocurrió un error número 1005 en una sentencia
CREATE TABLE
y la cadena con el mensaje de error se
refiere al errno (número de error) 150, significa que la creación de una
tabla falló debido a una restricción de clave foránea formulada
incorrectamente. Del mismo modo, si un ALTER TABLE
falla y hace referencia al número de error 150, significa que se ha
formulado incorrectamente una restricción de clave extranjera cuando se
alteró la tabla. En MySQL 5.0, puede emplearse SHOW INNODB
STATUS
para mostrar una explicación detallada del último error
de clave foránea sufrido por InnoDB
en el servidor.
Nota: InnoDB
no
verifica las restricciones de claves foráneas en las claves foráneas o
valores de claves referenciados que contengan una columna
NULL
.
Una desviación del estándar SQL: Si en
la tabla padre hay varios registros que contengan el mismo valor de
clave referenciada, entonces InnoDB
se comporta en
las verificaciones de claves extranjeras como si los demás registros con
el mismo valor de clave no existiesen. Por ejemplo, si se ha definido
una restricción del tipo RESTRICT
, y hay un registro
hijo con varias filas padre, InnoDB
no permite la
eliminación de ninguna de éstas.
InnoDB
lleva a cabo las operaciones en cascada a
través de un algoritmo de tipo depth-first, basado en los registros de
los indices correspondientes a las restricciones de claves foráneas.
Una desviación del estándar SQL: Si
ON UPDATE CASCADE
u ON UPDATE
SET NULL
vuelven a modificar la misma
tabla que se está actualizando en cascada, el comportamiento
es como en RESTRICT
. Esto significa que en una tabla
no se pueden ejecutar operaciones ON UPDATE CASCADE
u ON UPDATE SET NULL
que hagan referencia a ella
misma. De ese modo se previenen bucles infinitos resultantes de la
actualización en cascada. En cambio, una operación ON DELETE
SET NULL
, puede hacer referencia a la misma tabla donde se
encuentra, al igual que ON DELETE CASCADE
. En MySQL
5.0, las operaciones en cascada no pueden anidarse en más de 15 niveles
de profundidad.
Una desviación del estándar SQL: Como
sucede en MySQL en general, en una sentencia SQL que realice
inserciones, eliminaciones o actualizaciones en varias filas, InnoDB
verifica las restricciones UNIQUE
y FOREIGN
KEY
fila a fila. De acuerdo con el estándar SQL, el
comportamiento predeterminado debería ser que las restricciones se
verifiquen luego de que la sentencia SQL ha sido procesada por completo.
Note: Actualmente, los disparadores no son activados por acciones de claves foráneas en cascada.
Un ejemplo sencillo que relaciona tablas padre
e
hijo
a través de una clave foránea de una sola
columna:
CREATE TABLE parent( id INT NOT NULL, PRIMARY KEY (id) ) ENGINE=INNODB; CREATE TABLE child( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ) ENGINE=INNODB;
Aquí, un ejemplo más complejo, en el cual una tabla
product_order
tiene claves foráneas hacia otras dos
tablas. Una de las claves foráneas hace referencia a un índice de dos
columnas en la tabla product
. La otra hace referencia
a un índice de una sola columna en la tabla customer
:
CREATE TABLE product ( category INT NOT NULL, id INT NOT NULL, price DECIMAL, PRIMARY KEY(category, id) ) ENGINE=INNODB; CREATE TABLE customer ( id INT NOT NULL, PRIMARY KEY (id) ) ENGINE=INNODB; CREATE TABLE product_order ( no INT NOT NULL AUTO_INCREMENT, product_category INT NOT NULL, product_id INT NOT NULL, customer_id INT NOT NULL, PRIMARY KEY(no), INDEX (product_category, product_id), FOREIGN KEY (product_category, product_id) REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, INDEX (customer_id), FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINE=INNODB;
InnoDB
permite agregar una nueva restricción de
clave foránea a una tabla empleando ALTER TABLE
:
ALTER TABLE yourtablename ADD [CONSTRAINTsymbol
] FOREIGN KEY [id
] (index_col_name
, ...) REFERENCEStbl_name
(index_col_name
, ...) [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}] [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
Debe recordarse crear en primer lugar los índices
necesarios.. También se puede agregar una clave foránea
autoreferente a una tabla empleando ALTER TABLE
.
InnoDB
también soporta el uso de
ALTER TABLE
para borrar claves foráneas:
ALTER TABLEnombre_tabla
DROP FOREIGN KEYsímbolo_clave_foránea
;
Si la cláusula FOREIGN KEY
incluye un nombre de
CONSTRAINT
cuando se crea la clave foránea, se puede
utilizar ese nombre para eliminarla. En otro caso, el valor
símbolo_clave_foránea
es generado internamente por
InnoDB
cuando se crea la clave foránea. Para saber
cuál es este símbolo cuando se desee eliminar una clave foránea, se
emplea la sentencia SHOW CREATE TABLE
. Un ejemplo:
mysql> SHOW CREATE TABLE ibtest11c\G *************************** 1. row *************************** Table: ibtest11c Create Table: CREATE TABLE `ibtest11c` ( `A` int(11) NOT NULL auto_increment, `D` int(11) NOT NULL default '0', `B` varchar(200) NOT NULL default '', `C` varchar(175) default NULL, PRIMARY KEY (`A`,`D`,`B`), KEY `B` (`B`,`C`), KEY `C` (`C`), CONSTRAINT `0_38775` FOREIGN KEY (`A`, `D`) REFERENCES `ibtest11a` (`A`, `D`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `0_38776` FOREIGN KEY (`B`, `C`) REFERENCES `ibtest11a` (`B`, `C`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=INNODB CHARSET=latin1 1 row in set (0.01 sec) mysql> ALTER TABLE ibtest11c DROP FOREIGN KEY 0_38775;
El procesador de sentencias (parser) de InnoDB
permite emplear acentos graves (ASCII 96) para encerrar los nombres de
tablas y columnas en una clásusula FOREIGN KEY ...
REFERENCES ...
. El parser de InnoDB
también
toma en cuenta lo establecido en la variable de sistema
lower_case_table_names
.
InnoDB
devuelve las definiciones de claves foráneas
de una tabla como parte de la salida de la sentencia SHOW CREATE
TABLE
:
SHOW CREATE TABLE tbl_name
;
A partir de esta versión, mysqldump también produce definiciones correctas de las tablas en el fichero generado, sin omitir las claves foráneas.
Se pueden mostrar las restricciones de claves foráneas de una tabla de este modo:
SHOW TABLE STATUS FROMnombre_bd
LIKE 'nombre_tabla
';
Las restricciones de claves foráneas se listan en la columna
Comment
de la salida producida.
Al llevar a cabo verificaciones de claves foráneas,
InnoDB
establece bloqueos compartidos a nivel de fila
en los registros de tablas hijas o padres en los cuales deba fijarse.
InnoDB
verifica las restricciones de claves foráneas
inmediatamente, la verificación no es demorada hasta la confirmación de
la transacción.
Para facilitar la recarga de ficheros de volcado de tablas que tengan
relaciones de claves foráneas, mysqldump incluye
automáticamente una sentencia en la salida del comando para establecer
FOREIGN_KEY_CHECKS
a 0. Esto evita problemas con
tablas que tengan que ser creadas en un orden particular cuando se
recarga el fichero de volcado. También es posible establecer el valor de
esta variable manualmente:
mysql> SET FOREIGN_KEY_CHECKS = 0;
mysql> SOURCE dump_file_name
;
mysql> SET FOREIGN_KEY_CHECKS = 1;
Esto permite importar las tablas en cualquier orden si el fichero de
volcado contiene tablas que no están ordenadas según lo requieran sus
claves foráneas. También acelera la operación de importación. Establecer
FOREIGN_KEY_CHECKS
a 0 también puede ser útil para
ignorar restricciones de claves foráneas durante operaciones LOAD
DATA
y ALTER TABLE
.
InnoDB
no permite eliminar una tabla que está
referenciada por una restricción FOREIGN KEY
, a menos
que se ejecute SET FOREIGN_KEY_CHECKS=0
. Cuando se
elimina una tabla, las restricciones que fueron definidas en su
sentencia de creación también son eliminadas.
Si se recrea una tabla que fue eliminada, debe ser definida de acuerdo a las restricciones de claves foráneas que están haciendo referencia a ella. Debe tener los tipos y nombres correctos de columnas, y debe tener índices sobre las tablas referenciadas, como se estableció anteriormente. Si estas condiciones no se cumplen, MySQL devuelve un error número 1005 y menciona el error número 150 en el mensaje de error.
La replicación en MySQL funciona para tablas InnoDB
del mismo modo que lo hace para tablas MyISAM
. Es
posible usar replicación en una forma en que el tipo de tabla en el
servidor esclavo no es igual a la tabla original en el servidor maestro.
Por ejemplo, se pueden replicar modificaciones de una tabla
InnoDB
en el servidor maestro sobre una tabla
MyISAM
en el servidor esclavo.
Para configurar un nuevo esclavo para un servidor maestro, se debe
realizar una copia del espacio de tablas InnoDB
y de
los ficheros de registro, así como de los ficheros
.frm
de las tablas InnoDB
, y
mover las copias al servidor esclavo. El procedimiento adecuado para
esto se encuenta en Sección 15.9, “Trasladar una base de datos InnoDB
a otra máquina”.
Si se puede detener el servidor maestro o un esclavo existente, se puede
tomar un backup en frío del espacio de tablas InnoDB
y de los ficheros de registro y utilizarlos para configurar un servidor
esclavo. Para crear un nuevo esclavo sin detener ningún servidor, se
puede utilizar la herramienta comercial
InnoDB
Hot Backup
tool.
Una limitación menor en la replicación InnoDB
es que
LOAD TABLE FROM MASTER
no funciona con tablas de tipo
InnoDB
. Hay dos posibles soluciones:
Hacer un volcado de la tabla en el maestro e importarlo dentro del esclavo.
Utilizar ALTER TABLE
en el
maestro antes de realizar la replicación con nombre_tabla
TYPE=MyISAMLOAD
TABLE
, y luego emplear nombre_tabla
FROM MASTERALTER
TABLE
para convertir la tabla en el maestro nuevamente a
InnoDB
.
Las transacciones que fallen en el maestro no afectan en absoluto la replicación. La replicación en MySQL se basa en el registro (log) binario donde MySQL registra las sentencias SQL que modifican datos. Un esclavo lee el registro binario del maestro y ejecuta las mismas sentencias SQL. Sin embargo, las sentencias emitidas dentro de una transacción no se graban en el registro binario hasta que se confirma la transacción, en ese momento todas las sentencias son grabadas de una vez. Si una sentencia falla, por ejemplo por infringir una clave foránea, o si se cancela una transacción, ninguna sentencia se guarda en el registro binario y la transacción no se ejecuta en absoluto en el servidor esclavo.
En MySQL 5.0, se puede almacenar cada tabla InnoDB
y
sus índices en su propio fichero. Esta característica se llama
“multiple tablespaces” (espacios de tablas múltiples)
porque, en efecto, cada tabla tiene su propio espacio de tablas.
El uso de múltiples espacios de tablas puede ser beneficioso para usuarios que desean mover tablas específicas a discos físicos separados o quienes deseen restaurar respaldos de tablas sin interrumpir el uso de las demás tablas InnoDB.
Se pueden habilitar múltiples espacios de tablas agregando esta línea a
la sección [mysqld]
de my.cnf
:
[mysqld] innodb_file_per_table
Luego de reiniciar el servidor, InnoDB
almacenará
cada nueva tabla creada en su propio fichero
en el
directorio de la base de datos a la que pertenece la tabla. Esto es
similar a lo que hace el motor de almacenamiento
nombre_tabla
.ibdMyISAM
, pero MyISAM
divide la
tabla en un fichero de datos
y el
fichero de índice
tbl_name
.MYD
. Para
tbl_name
.MYIInnoDB
, los datos y los índices se almacenan juntos
en el fichero .ibd
. El fichero
se sigue
creando como es usual.
tbl_name
.frm
Si se quita la línea innodb_file_per_table
de
my.cnf
y se reinicia el servidor,
InnoDB
creará nuevamente las tablas dentro de los
ficheros del espacio de tablas compartido.
innodb_file_per_table
afecta solamente la creación de
tablas. Si se inicia el servidor con esta opción, las tablas nuevas se
crearán empleando ficheros .ibd
, pero aún se puede
acceder a las tablas existentes en el espacio de tablas compartido. Si se
remueve la opción, las nuevas tablas se crearán en el espacio
compartido, pero aún se podrá acceder a las tablas creadas en espacios
de tablas múltiples.
InnoDB
siempre necesita del espacio de tablas
compartido. Los ficheros .ibd
no son suficientes
para que funcione InnoDB
. El espacio de tablas
compartido consiste de los ya conocidos ficheros
ibdata
, donde InnoDB
coloca su
diccionario de datos interno y los registros para deshacer cambios (undo
logs).
No se puede mover libremente ficheros
.ibd
entre directorios de bases de datos
en la misma forma en que se hace con ficheros de tablas
MyISAM
. Esto se debe a que la definición de las
tablas se almacena en el espacio de tablas compartido de
InnoDB
, y también porque InnoDB
debe preservar la consistencia de los identificadores de transacciones y
los números de secuencia de registros (log).
Dentro de una determinada instalación MySQL, se puede mover un fichero
.ibd
y las tablas asociadas de una base de datos a
otra con la conocida sentencia RENAME
TABLE
:
RENAME TABLEnombre_bd_anterior.nombre_tabla
TOnombre_bd_nuevo.nombre_tabla
;
Si se tiene un respaldo “limpio” de un fichero
.ibd
, se lo puede restaurar dentro de la
instalación MySQL de donde proviene del siguiente modo:
Utilizando esta sentencia ALTER TABLE
:
ALTER TABLE nombre_tabla
DISCARD TABLESPACE;
Precaución: Esto eliminará el actual
fichero .ibd
.
Colocando el fichero .ibd
nuevamente en el
directorio de la base de datos adecuada.
Utilizando esta sentencia ALTER TABLE
:
ALTER TABLE nombre_tabla
IMPORT TABLESPACE;
En este contexto, un respaldo “limpio”
de un fichero .ibd
significa:
ç
El fichero .ibd
no contiene modificaciones
realizadas por transacciones sin confirmar.
No quedan entradas sin combinar en el buffer de inserciones en el
fichero .ibd
.
Se han quitado todos los registros de índice marcados para
eliminación en el fichero .ibd
.
mysqld ha descargado todas las páginas
modificadas del fichero .ibd
desde el buffer
pool hacia el fichero.
Se puede realizar un respaldo limpio del fichero
.ibd
con el siguiente método:
Detener toda actividad del servidor mysqld y confirmar todas las transacciones.
Esperar hasta que SHOW INNODB STATUS
indique que
no hay transacciones activas en la base de datos, y el estado del
subproceso (trhead) principal de InnoDB
sea
Waiting for server activity
(Esperando por
actividad del servidor). Entonces, se puede hacer una copia del
fichero .ibd
.
Otro método para hacer una copia limpia de un fichero
.ibd
es utilizar la herramienta comercial
InnoDB Hot Backup:
Utilizar InnoDB Hot Backup para respaldar la
instalación InnoDB
.
Iniciar un segundo servidor mysqld sobre el
respaldo y permitirle limpiar los ficheros .ibd
del mismo.
Figura en la lista de pendientes (TODO) para permitir mover ficheros
.ibd
limpios a otra instalación MySQL. Esto
necesita que se inicialicen los IDs (identificadores) de transacciones y
los números de secuencia de registros (log) en el fichero
.ibd
.
Esta sección describe lo que se puede hacer cuando el espacio de tablas
InnoDB
se queda sin espacio o cuando se desea cambiar
el tamaño de los ficheros de registro (log).
La manera más sencilla de incrementar el tamaño del espacio de tablas
InnoDB
es configurarlo desde un principio para que sea
autoextensible, especificando el atributo autoextend
para el último fichero de datos en la definición del espacio de tablas.
Entonces, InnoDB
incrementa el tamaño de ese fichero
automáticamente en intervalos de 8MB cuando se queda sin espacio. El
tamaño del intervalo a incrementar puede configurarse estableciendo el
valor de innodb_autoextend_increment
, el cual está
expresado en megabytes, y cuyo valor predeterminado es 8.
Alternativamente, se puede incrementar el tamaño del espacio de tablas
agregando otro fichero de datos. Para hacer esto, se debe detener el
servidor MySQL, editar el fichero my.cnf
para agregar
un nuevo fichero de datos al final de
innodb_data_file_path
, e iniciar nuevamente el
servidor.
Si el último fichero de datos especificado tiene la palabra clave
autoextend
, el procedimiento para editar a
my.cnf
debe tener en cuenta el tamaño que ha
alcanzado este último fichero. Hay que obtener el tamaño del fichero de
datos, redondearlo hacia abajo a la cantidad de megabytes
(1024 * 1024 bytes) más cercana, y especificar este número explícitamente
en innodb_data_file_path
. Entonces se podrá agregar
otro fichero de datos. Hay que recordar que solamente el último fichero de
datos en innodb_data_file_path
puede especificarse como
autoextensible.
Como ejemplo, se asumirá que el espacio de tablas tiene sólo un fichero de
datos autoextensible ibdata1
:
innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:10M:autoextend
Suponiendo que este fichero de datos, a lo largo del tiempo, ha crecido hasta 988MB, debajo se ve la línea de configuración luego de agregar otro fichero de datos autoextensible.
innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
Cuando se agrega un nuevo fichero al espacio de tablas, hay que asegurarse
de que no exista. InnoDB
crea e inicializa el fichero
al reiniciar el servidor.
Actualmente no es posible quitar un fichero de datos del espacio de tablas. Para reducir el tamaño del espacio de tablas, emplear este procedimiento:
Utilizar mysqldump para hacer un volcado de todas
las tablas InnoDB
.
Detener el servidor.
Eliminar todos los ficheros existentes del espacio de tablas.
Configurar un nuevo espacio de tablas.
Reiniciar el servidor.
Importar el fichero de volcado de tablas.
Si se desea modificar la cantidad o tamaño de los ficheros de registro
(log) de InnoDB
, se debe detener el servidor MySQL y
asegurarse de que se cerró sin errores. Luego, copiar los ficheros de
registro antiguos en un lugar seguro, sólo para el caso de que algo haya
fallado en el cierre del servidor y se necesite recuperar el espacio de
tablas. Eliminar los antiguos ficheros de registro del directorio de
ficheros de registro, editar my.cnf
para modificar la
configuración de los ficheros de registro, e iniciar nuevamente el
servidor MySQL. mysqld verá al iniciar que no hay
ficheros de registro e informará que está creando nuevos.
La clave de una administración de bases de datos segura es realizar copias de respaldo regularmente.
InnoDB Hot Backup es una herramienta de respaldo en
línea que puede utilizarse para respaldar la base de datos
InnoDB
mientras ésta se está ejecutando.
InnoDB Hot Backup no necesita que se detenga la base de
datos y no establece ningún bloqueo ni dificulta el normal procesamiento
de la base de datos. InnoDB Hot Backup es una
herramienta adicional comercial (no grautita) cuyo cargo anual de licencia
es de €390 por cada ordenador en el que se ejecute el servidor MySQL.
Consulte la página de
Internet de InnoDB Hot Backup para obtener
información detallada y ver capturas de pantallas.
Si se está en condiciones de detener el servidor MySQL, puede realizarse
una copia de respaldo binaria, que consiste en todos los ficheros usados
por InnoDB
para administrar sus tablas. Se utiliza el
siguiente procedimiento:
Detener el servidor MySQL y asegurarse de que lo hace sin errores.
Copiar todos los ficheros de datos (ficheros
ibdata
e .ibd
) en un lugar
seguro.
Copiar todos los ficheros ib_logfile
en un lugar
seguro.
Copiar el o los ficheros de configuración my.cnf
en un lugar seguro.
Copiar todos los ficheros .frm
de las tablas
InnoDB
en un lugar seguro.
La replicación funciona con tablas InnoDB
, de forma que
puede emplearse para mantener una copia de la base de datos en sitios de
bases de datos que necesiten alta disponibilidad.
Adicionalmente a la realización de copias de respaldo binarias como se ha
descripto, también se deberían realizar regularmente volcados de las
tablas con mysqldump. El motivo de esto es que un
fichero binario podría corromperse sin que el usuario lo note. El volcado
de las tablas se almacena en ficheros de texto que son legibles por los
seres humanos, facilitando la localización de corrupción en las tablas.
Además, puesto que el formato es más simple, las probabilidades de una
corrupción seria de datos son menores. mysqldump
también tiene una opción --single-transaction
que puede
usarse para capturar una imagen consistente de la base de datos sin
bloquear otros clientes.
Para estar en condiciones de recuperar una base de datos
InnoDB
a partir del respaldo binario descripto
anteriormente, se debe ejecutar el servidor MySQL con el registro binario
(binary logging) activo. Entonces se puede aplicar el log binario al
respaldo de la base de datos para lograr la recuperación a una fecha y
hora determinadas:
mysqlbinlog nombre_de_host
-bin.123 | mysql
Para recuperarse de una caida del servidor, sólo se requiere reiniciarlo.
InnoDB
verifica automáticamente los registros (logs) y
ejecuta una recuperación de la base de datos del tipo roll-forward, es
decir, hasta el momento anterior a la falla. InnoDB
revierte automáticamente las transacciones no grabadas que existían al
momento de la caída. Durante la recuperación, mysqld
muestra información parecida a esta:
InnoDB: Database was not shut down normally. InnoDB: Starting recovery from log files... InnoDB: Starting log scan based on checkpoint at InnoDB: log sequence number 0 13674004 InnoDB: Doing recovery: scanned up to log sequence number 0 13739520 InnoDB: Doing recovery: scanned up to log sequence number 0 13805056 InnoDB: Doing recovery: scanned up to log sequence number 0 13870592 InnoDB: Doing recovery: scanned up to log sequence number 0 13936128 ... InnoDB: Doing recovery: scanned up to log sequence number 0 20555264 InnoDB: Doing recovery: scanned up to log sequence number 0 20620800 InnoDB: Doing recovery: scanned up to log sequence number 0 20664692 InnoDB: 1 uncommitted transaction(s) which must be rolled back InnoDB: Starting rollback of uncommitted transactions InnoDB: Rolling back trx no 16745 InnoDB: Rolling back of trx no 16745 completed InnoDB: Rollback of uncommitted transactions completed InnoDB: Starting an apply batch of log records to the database... InnoDB: Apply batch completed InnoDB: Started mysqld: ready for connections
Si la base de datos se corrompe o falla el disco, habrá que efectuar la recuperación desde una copia de respaldo. En el caso de corrupción, primero habría que encontrar una copa de respaldo realizada antes de la corrupción. Luego de restaurar la copia de respaldo base, debe realizarse la recuperación a partir de los ficheros de registro binario.
En algunos casos de corrupción de base de datos es suficiente con volcar,
eliminar, y volver a crear una o unas pocas tablas corruptas. Se puede
emplear la sentencia SQL CHECK TABLE
para verificar si
una tabla está corrupta, aunque CHECK TABLE
,
naturalmente, no puede detectar cada posible clase de corrupción. Se puede
emplear innodb_tablespace_monitor
para verificar la
integridad de la gestión de espacio de ficheros dentro de los ficheros de
espacio de tablas.
En algunos casos, una aparente corrupción de página de base de datos se debe en realidad a que el sistema operativo está corrompiendo su propio cache de ficheros, y los datos en el disco podrían estar en buenas condiciones. Lo mejor es, antes que nada, intentar el reinicio del ordenador. Ello puede eliminar errores que dan la sensación de tener corrupción en la página de base de datos.
Si hay corrupción de página de base de datos, es posible que se desee
hacer un volcado de tablas desde la base de datos con SELECT
INTO OUTFILE
; usualmente, la mayoría de los datos obtenidos de
esta manera están intactos. Aún así, la corrupción puede hacer que
SELECT * FROM
o
las operaciones en segundo plano de tbl_name
InnoDB
caigan o,
incluso activar la recuperación roll-forward de
InnoDB
. Sin embargo, se puede forzar el motor de
almacenamiento de InnoDB
para que se inicie mientras se
evitan las operaciones en segundo plano, de forma que se pueda realizar
un volcado de las tablas. Por ejemplo, puede agregarse la siguiente
linea a la sección [mysqld]
del fichero de opciones
antes de reiniciar el servidor:
[mysqld] innodb_force_recovery = 4
Debajo se listan los valores mayores a cero permitidos para
innodb_force_recovery
. Un número mayor incluye todas
las precauciones de los números por debajo. Si se logra hacer un volcado
de tablas con un valor de a lo sumo 4, se puede estar relativamente
seguro de que solamente se han perdido algunos datos en páginas
individuales corruptas. Un valor de 6 es más dramático, porque las
páginas de base de datos han quedado obsoletas, lo que a su vez puede
introducir más corrupción en B-trees y otras estructuras de bases de
datos.
1
(SRV_FORCE_IGNORE_CORRUPT
)
Le permite al servidor ejecutarse aún si detecta una página
corrupta; intenta hacer que SELECT * FROM
salte sobre los
registros de índice y páginas corruptos, lo cual es de ayuda en el
volcado de las tablas.
tbl_name
2
(SRV_FORCE_NO_BACKGROUND
)
Impide que se ejecute el subproceso (thread) principal. Si durante la operación de descarga (purge) ocurriese una caida, esto la previene.
3
(SRV_FORCE_NO_TRX_UNDO
)
No revierte transacciones luego de la recuperación.
4
(SRV_FORCE_NO_IBUF_MERGE
)
También evita las operaciones de combinación del buffer de inserciones. Si ello pudiese causar una caida, es mejor no hacerlo; no calcula estadísticas de tablas.
5
(SRV_FORCE_NO_UNDO_LOG_SCAN
)
No tiene en cuenta el contenido de los registros para revertir
cambios (undo logs) al iniciar la base de datos:
InnoDB
considera como confirmadas inclusive a las
transacciones incompletas.
6
(SRV_FORCE_NO_LOG_REDO
)
Omite la creación del registro (log) que permite la recuperación de tipo roll-forward.
En otro caso la base de datos no debe emplearse con ninguna de
estas opciones habilitadas. Como una medida de seguridad,
InnoDB
impide que los usuarios lleven a cabo
operaciones INSERT
, UPDATE
, o
DELETE
cuando
innodb_force_recovery
está configurado en un valor
mayor a 0.
Se pueden ejecutar sentencias DROP
y
CREATE
para eliminar y crear tablas incluso cuando se
está empleando la recuperación forzada. Si se sabe que una determinada
tabla está provocando caídas al hacer una cancelación (rollback), se la
puede eliminar. También puede utilizarse para detener una cancelación
fuera de control causada por una importación o un ALTER
TABLE
en masa. Se puede interrumpir el proceso
mysqld y establecer
innodb_force_recovery
a un valor de
3
para poner a funcionar la base de datos sin que
ejecute cancelación (rollback), luego emplear DROP
para eliminar la tabla que está causando la cancelación fuera de
control.
InnoDB
implementa un mecanismo de puntos de
verificación llamado fuzzy checkpoint (punto de
verificación difuso). InnoDB
descarga las páginas
modificadas de la base de datos desde el pool de buffers en lotes
pequeños. No es necesario descargar al disco el pool de buffers en una
sola acción, lo cual en la práctica podría detener temporalmente el
procesamiento de sentencias SQL del usuario.
Durante la recuperación de caídas, InnoDB
busca un
marcador de checkpoint escrito en los ficheros de registro (log). Ya
sabe que todas las modificaciones a la base de datos anteriores al
marcador están presentes en la imagen en disco de la misma.
Entonces InnoDB
recorre los ficheros de registro
(log) desde el checkpoint hacia adelante, aplicando sobre la base de
datos las modificaciones registradas.
InnoDB
escribe en los ficheros de registro en una
forma rotativa. Todas las modificaciones confirmadas que hagan a
las páginas de la base de datos en el pool de buffers diferentes de las
grabadas en disco deben estar disponibles en los ficheros de registro en
caso de que InnoDB
tenga que hacer una recuperación.
Esto significa que cuando InnoDB
comienza a
reutilizar un fichero de registro, tiene que asegurarse de que las
imágenes en disco de las páginas de base de datos contienen las
modificaciones asentadas en el fichero de registro que se va a
reutilizar. En otras palabras, InnoDB
debe crear un
punto de verificación (checkpoint) y esto a menudo implica la descarga a
disco de las páginas de base de datos modificadas.
La descripción anterior explica porqué hacer los ficheros de registro muy grandes puede ahorrar operaciones de E/S en disco destinadas a la creación de puntos de verificación. A menudo se hace hincapié en establecer el tamaño total de los ficheros de registro en lo mismo que el pool de buffers o aún más grande. La desventaja que tienen los ficheros de registro grandes es que la recuperación ante una caída puede tomar más tiempo debido a que hay más información que debe aplicarse a la base de datos.
En Windows, InnoDB
siempre almacena internamente en
minúsculas los nombres de bases de datos y tablas. Para mover bases de
datos en un formato binario de Unix a Windows o de Windows a Unix, se
deberían tener en minúsculas todos los nombres de tablas y bases de datos.
Una forma apropiada de cumplir con esto es agregar la siguiente línea a la
sección [mysqld]
de los ficheros
my.cnf
o my.ini
antes de crear
cualquier base de datos o tablas:
[mysqld] lower_case_table_names=1
Al igual que los ficheros de datos MyISAM
, los ficheros
de datos y de registro (log) de InnoDB
son compatibles
a nivel binario en todas las plataformas que tengan el mismo formato de
números de coma flotante. Se puede mover una base de datos
InnoDB
simplemente copiando todos los ficheros
relevantes que se listan en Sección 15.8, “Hacer una copia de seguridad y recuperar una base de datos InnoDB
”. Si los formatos
de número de coma flotante difieren pero no se han empleado tipos de datos
FLOAT
o DOUBLE
en las tablas, el
procedimiento es el mismo: copiar los ficheros necesarios. Si los formatos
difieren y las tablas contienen datos de coma flotante, se deberá emplear
mysqldump para volcar las tablas en un ordenador e
importar los ficheros de volcado en otro.
Una forma de incrementar el rendimiento es desactivar el modo autocommit (ejecución automática) al importar datos, asumiendo que el espacio de tablas tiene suficiente sitio para el extenso segmento de cancelación (rollback) que generará la importación de transacciones. La confirmación (commit) se hará luego de importar una tabla entera o un segmento de una tabla.
InnoDB
InnoDB
y AUTOCOMMIT
InnoDB
y TRANSACTION ISOLATION LEVEL
SELECT ... FOR UPDATE
y
SELECT ... LOCK IN SHARE MODE
InnoDB
InnoDB
En el modelo de transacciones de InnoDB
, la meta es
combinar las mejores propiedades de una base de datos multiversión con el
tradicional bloqueo de dos fases. InnoDB
bloquea a
nivel de fila y ejecuta consultas por defecto como lecturas consistentes
(consistent reads) no bloqueadas, al estilo de Oracle. La tabla de bloqueo
en InnoDB
se almacena en forma tan eficiente que no se
necesitan bloqueos escalables: generalmente varios usuarios están
habilitados a bloquear cada fila de la base de datos, o cualquier
subconjunto de filas, sin que InnoDB
incurra en falta
de memoria.
InnoDB
implementa un bloqueo a nivel de fila
estándar, donde hay dos tipos de bloqueos:
Compartido (Shared) (S
) le permite a una
transacción leer una fila.
Exclusivo (Exclusive) (X
) le permite a
una transacción actualizar o eliminar una fila.
Si una transacción A
sostiene un bloqueo
exclusivo (X
) sobre una tupla
t
, entonces una solicitud de otra transacción
B
para establecer un bloqueo de cualquier
tipo sobre t
no puede ser atendida
inmediatamente. En lugar de eso, la transacción
B
debe esperar a que la transacción
A
libere el bloqueo en la tupla
t
.
Si la transacción A
sostiene un bloqueo
compartido (S
) sobre una tupla
t
, entonces
Una solicitud de otra transacción B
para
un bloqueo X
sobre
t
no puede ser atendido inmediatamente.
Una solicitud de otra transacción B
para
un bloqueo S
sobre
t
puede ser atendido inmediatamente. En
consecuencia, tanto A
como
B
sostendrán un bloqueo
S
sobre t
.
Adicionalmente, InnoDB
soporta bloqueo de
granularidad múltiple (multiple granularity locking), el cual
permite que existan simultáneamente bloqueos en registros y bloqueos en
tablas enteras. Para hacer práctico el nivel de bloqueo de granularidad
múltiple, se emplean tipos adicionales de bloqueo, llamados
bloqueos de intención (intention locks). Los
bloqueos de intención son bloqueos de tabla en
InnoDB
. La idea detrás de los mismos es que una
transacción indique qué tipo de bloqueo (compartido o exclusivo)
requerirá más tarde sobre una fila de esa tabla. En
InnoDB
se utilizan dos tipos de bloqueos de intención
(asumiendo que la transacción T
ha solicitado
un bloqueo del tipo indicado en la tabla R
):
Intención compartida (Intention shared)
(IS
): La transacción
T
trata de establecer bloqueos
S
en tuplas individuales de la tabla
T
.
Intención exclusiva (Intention exclusive)
(IX
): La transacción
T
trata de establecer bloqueos
X
en las tuplas.
Luego, el protocolo de bloqueo de intención es el siguiente:
Antes de que de una determinada transacción logre un bloqueo
S
en una determinada fila, primero debe
conseguir establecer un bloqueo IS
o
superior en la tabla que contiene a la fila.
Antes de que de una determinada transacción logre un bloqueo
X
en una determinada fila, primero debe
conseguir establecer un bloqueo IX
en la
tabla que contiene a la fila.
Estas reglas pueden resumirse convenientemente por medio de una matriz de compatibilidad entre tipos de bloqueo:
X | IX | S | IS | - | |
X | N | N | N | N | S |
IX | N | S | N | S | S |
S | N | S | S | S | S |
IS | N | S | S | S | S |
- | S | S | S | S | S |
Por lo tanto, los bloqueos de intención solamente bloquean solicitudes
sobre tablas completas (Ej: LOCK TABLES ... WRITE
).
El propósito principal de IX
y
IS
es mostrar que alguien está bloqueando una
fila, o va a bloquear una fila en la tabla.
En InnoDB
, toda la actividad del usuario se produce
dentro de una transacción. Si el modo de ejecución automática
(autocommit) está activado, cada sentencia SQL conforma una transacción
individual por sí misma. MySQL siempre comienza una nueva conexión con
la ejecución automática habilitada.
Si el modo de ejecución automática se deshabilitó con SET
AUTOCOMMIT = 0
, entonces puede considerarse que un usuario
siempre tiene una transacción abierta. Una sentencia SQL
COMMIT
o ROLLBACK
termina la
transacción vigente y comienza una nueva. Ambas sentencias liberan todos
los bloqueos InnoDB
que se establecieron durante la
transacción vigente. Un COMMIT
significa que los
cambios hechos en la transacción actual se convierten en permanentes y
se vuelven visibles para los otros usuarios. Por otra parte, una
sentencia ROLLBACK
, cancela todas las modificaciones
producidas en la transacción actual.
Si la conexión tiene la ejecución automática habilitada, el usuario puede
igualmente llevar a cabo una transacción con varias sentencias si la
comienza explícitamente con START TRANSACTION
o
BEGIN
y la termina con COMMIT
o
ROLLBACK
.
En los términos de los niveles de aislamiento de transacciones SQL:1992,
el nivel predeterminado en InnoDB
es REPEATABLE
READ
. En MySQL 5.0, InnoDB
ofrece los
cuatro niveles de aislamiento de transacciones descriptos por el
estándar SQL. Se puede establecer el nivel predeterminado de aislamiento
por todas las conexiones mediante el uso de la opción
--transaction-isolation
en la línea de comandos o en
ficheros de opciones. Por ejemplo, se puede establecer la opción en la
sección [mysqld]
de my.cnf
de
este modo:
[mysqld] transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}
Un usuario puede cambiar el nivel de aislamiento de una sesión
individual o de todas las nuevas conexiones con la sentencia SET
TRANSACTION
. Su sintaxis es la siguiente:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
Nótese que se usan guiones en los nombres de niveles de la opción
--transaction-isolation
, pero no en la sentencia
SET TRANSACTION
.
El comportamiento predeterminado es establecer el nivel de aislamiento a
partir de la próxima transacción que se inicie. Si se emplea la palabra
clave GLOBAL
, la sentencia establece el nivel
predeterminado de la transacción globalmente para todas las nuevas
conexiones creadas a partir de ese punto (pero no en las existentes). Se
necesita el privilegio SUPER
para hacer esto.
Utilizando la palabra clave SESSION
se establece el
nivel de transacción para todas las futuras transacciones ejecutadas en
la actual conexión.
Cualquier cliente es libre de cambiar el nivel de aislamiento de la sesión (incluso en medio de una transacción), o el nivel de aislamiento para la próxima transacción.
Los niveles de aislamiento de transacciones globales y de sesión pueden consultarse con estas sentencias:
SELECT @@global.tx_isolation; SELECT @@tx_isolation;
En el bloqueo a nivel de fila, InnoDB
emplea bloqueo
de clave siguiente (next-key). Esto significa que, además de registros
de índice, InnoDB
también puede bloquear el
“vacío” que precede a un registro de índice para bloquear
inserciones de otros usuarios inmediatamente antes del registro de
índice. Un bloqueo de clave siguiente hace referencia a bloquear un
registro de índice y la posición vacía antes de él. Bloquear una
posición vacía es establecer un bloqueo que actúa solamente sobre el
vacío anterior a un registro de índice.
A continuación una descripción detallada de cada nivel de aislamiento en
InnoDB
:
READ UNCOMMITTED
Las sentencias SELECT
son ejecutadas sin realizar
bloqueos, pero podría usarse una versión anterior de un registro.
Por lo tanto, las lecturas no son consistentes al usar este nivel de
aislamiento. Esto también se denomina “lectura sucia”
(dirty read). En otro caso, este nivel de aislamiento funciona igual
que READ COMMITTED
.
READ COMMITTED
Similar en parte al mismo nivel de aislamiento de Oracle.
Todas las sentencias SELECT ... FOR UPDATE
y
SELECT ... LOCK IN SHARE MODE
bloquean solamente
los registros de índice, no los espacios vacíos que los preceden,
por lo tanto se permite la libre inserción de nuevos registros junto
a los bloqueados. Las sentencias UPDATE
and
DELETE
que empleen un índice único con una
condición de búsqueda única bloquean solamente el registro de índice
hallado, no el espacio que lo precede. En las sentencias
UPDATE
y DELETE
que actúan
sobre rangos de registros, InnoDB
debe bloquear
los espacios vacíos y bloquear las inserciones de otros usuarios en
los espacios vacíos que hay dentro del rango. Esto es necesario
debido a que las “filas fantasma” deben ser bloqueadas
para que funcionen la replicación y recuperación en MySQL.
Las lecturas consistentes se comportan como en Oracle: Cada lectura consistente, incluso dentro de la misma transacción, establece y lee su propia captura tomada de la base de datos. Consulte Sección 15.10.4, “Lecturas consistentes que no bloquean”.
REPEATABLE READ
Este es el nivel de aislamiento predeterminado de
InnoDB
. Las sentencias SELECT ... FOR
UPDATE
, SELECT ... LOCK IN SHARE
MODE
, UPDATE
, y
DELETE
que utilicen un índice único con una
condición de búsqueda única, bloquean solamente el registro de índice
hallado, no el espacio vacío que lo precede. Con otras condiciones
de búsqueda, estas operaciones emplean bloqueo de clave siguiente
(next-key), bloqueando el rango de índice cubierto por la operación
incluyendo los espacios vacíos, y bloqueando las nuevas inserciones
por parte de otros usuarios.
En lecturas consistentes (consistent reads), hay una importante
diferencia con respecto al nivel de aislamiento anterior: En este
nivel, todas las lecturas consistentes dentro de la misma
transacción leen de la captura de la base de datos tomada por la
primer lectura. Esta práctica significa que si se emiten varias
sentencias SELECT
dentro de la misma transacción,
éstas serán consistentes unas con otras. Consulte Sección 15.10.4, “Lecturas consistentes que no bloquean”.
SERIALIZABLE
Este nivel es similar a REPEATABLE READ
, pero
todas las sentencias SELECT
son convertidas
implícitamente a SELECT ... LOCK IN SHARE
MODE
.
Lectura consistente significa que InnoDB
utiliza su
característica de multiversión para presentar a una consulta
una captura de la base de datos en un momento determinado. La
consulta ve los cambios realizados exactamente por aquellas
transacciones confirmadas antes de ese momento, y no los cambios hechos
con posterioridad o por transacciones no confirmadas. La excepción a
esto es que la consulta ve los cambios efectuados por la transacción a
donde pertenece.
Si se está ejecutando con el nivel de aislamiento predeterminado
REPEATABLE READ
, entonces todas las lecturas
consistentes dentro de la misma transacción leen la captura creada
por la primer lectura en esa transacción. Se puede refrescar esta
captura confirmando la transacción actual y emitiendo nuevas
consultas.
Lectura consistente es el modo por defecto en el cual
InnoDB
procesa las sentencias
SELECT
en los niveles de aislamiento
READ COMMITTED
y
REPEATABLE READ
. Una lectura consistente no establece
ningún bloqueo en las tablas a las que accede, y, por lo tanto, otros
usuarios están libres para modificar las tablas sobre las que se está
haciendo la lectura consistente.
En ciertas circunstancias, no es conveniente una lectura consistente.
Por ejemplo, se podría desear agregar una fila en la tabla
hijo
, y estar seguro de que dicha fila tiene una fila
padre en la tabla padre
. El siguiente ejemplo muestra
cómo implementar integridad referencial en el código de la aplicación.
Suponiendo que se utiliza una lectura consistente para leer la tabla
padre
y efectivamente puede verse el registro padre
para la fila hijo que se agregará, ¿puede agregarse en forma segura la
fila hijo dentro de la tabla hijo
? No, porque puede
haber ocurrido que entretanto otro usuario haya borrado el registro
padre de la tabla padre
, sin que se tenga
conocimiento de ello.
La solución es llevar a cabo el SELECT
en un modo con
bloqueo, utilizando LOCK IN SHARE MODE
:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
Realizar una lectura en modo compartido (share mode) significa que se
leen los últimos datos disponibles, y se establece un bloqueo en modo
compartido en los registros que se leen. Un bloqueo en modo compartido
evita que otros actualicen o eliminen la fila que se ha leido. Además,
si los datos más actualizados pertenecen a una transacción todavía no
confirmada de otra conexión, se espera hasta que la transacción se
confirme. Luego de ver que la mencionada consulta devuelve el registro
padre 'Jones'
, se puede agregar con seguridad el
registro hijo en la tabla hijo
y confirmar la
transacción.
Otro ejemplo: se tiene un campo contador, entero, en una tabla llamada
child_codes
que se emplea para asignar un
identificador único a cada registro hijo agregado a la tabla
hijo
. Obviamente, utilizar una lectura consistente o
una lectura en modo compartido para leer el valor actual del contador no
es una buena idea, puesto que dos usuarios de la base de datos pueden
ver el mismo valor del contador, y agregar registros hijos con el mismo
identificador, lo cual generaría un error de clave duplicada.
En este caso, LOCK IN SHARE MODE
no es una buena
solución porque si dos usuarios leen el contador al mismo tiempo, al
menos uno terminará en un deadlock cuando intente actualizar el
contador.
En este caso, hay dos buenas formas de implementar la lectura e
incremento del contador: (1), actualizar el contador en un incremento
de 1 y sólo después leerlo, o (2) leer primero el contador estableciendo
un bloqueo FOR UPDATE
, e incrementándolo luego. La
última puede ser implementada como sigue:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
Una sentencia SELECT ... FOR UPDATE
lee el dato más
actualizado disponible, estableciendo bloqueos exclusivos sobre cada
fila leída. Es decir, el mismo bloqueo que haría
UPDATE
.
Nótese que el anterior es un sencillo ejemplo de cómo funciona
SELECT ... FOR UPDATE
. En MySQL, la tarea específica
para generar un identificador único en realidad puede realizarse
utilizando un sólo acceso a la tabla:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID();
La sentencia SELECT
simplemente recupera la
información del identificador (relativa a la conexión actual). No accede
ninguna tabla.
En el bloqueo a nivel de fila, InnoDB
utiliza un
algoritmo llamado bloqueo de próxima clave.
InnoDB
lleva a cabo el bloqueo a nivel de fila de tal
manera que cuando busca o recorre el índice de una tabla, establece
bloqueos compartidos o exclusivos en los registros de índice que
encuentra. Por lo tanto, los bloqueos a nivel de fila son en realidad
bloqueos sobre registros del índice.
El conjunto de bloqueos de InnoDB
sobre los registros
del índice también afecta al “gap” (posición vacía) que
precede al registro de índice. Si un usuario tiene un bloqueo compartido
o exclusivo sobre un registro R
en un índice, otro
usuario no puede insertar un nuevo registro inmediatamente antes de
R
en el orden del índice. Este bloqueo de posiciones
vacías se hace para evitar el llamado “problema fantasma”.
Suponiendo que se desean leer y bloquear todos los hijos de la tabla
hijos
que tengan un identificador mayor a 100, con el
posterior intento de actualizar algunas columnas en las filas
seleccionadas:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
Suponiendo que hay un índice sobre la columna id
, la
consulta recorre ese índice comenzando por el primer registro donde
id
es mayor a 100. Si el bloqueo establecido sobre el
índice no bloqueara también las inserciones hechas en las posiciones
vacías, durante el proceso se podría insertar una nueva fila en la
tabla. Si se ejecuta la misma sentencia SELECT
dentro
de la misma transacción, se podría ver una nueva fila en el conjunto de
resultados devuelto por la consulta. Esto es contrario al principio de
aislamiento de las transacciones: una transacción deberia ejecutarse de
forma que los datos que ha leido no cambien en el transcurso de la
misma. Si se considera un conjunto de columnas como datos, el nuevo
registro hijo “fantasma” violaría el principio de
aislamiento.
Cuando InnoDB
recorre un índice, también puede
bloquear la posición vacía después del último registro del índice. Es
precisamente lo que ocurre en el ejemplo anterior: Los bloqueos
impuestos por InnoDB
evitan cualquier inserción en la
tabla donde id
fuera mayor de 100.
Se puede emplear bloqueo de próxima clave para efectuar el control de la unicidad en una aplicación: Si se leen los datos en modo compartido y no se ve un duplicado de la fila que se va a insertar, entonces puede hacerse con la seguridad de que el bloqueo de próxima clave establecido sobre el registro que continúa a la fila insertada evita que cualquiera inserte un duplicado de ésta. Por lo tanto, el bloqueo de próxima clave permite “bloquear” la no existencia de algo en la tabla.
Suponiendo que se está ejecutando en el nivel de aislamiento
predeterminado REPEATABLE READ
, cuando se realiza una
lectura consistente -esto es, una sentencia SELECT
ordinaria-, InnoDB
le otorga a la transacción un
punto en el tiempo (timepoint) del momento en que se realizó la consulta.
Si otra transacción elimina una fila y confirma la acción en un momento
posterior a dicho punto, no se verá la fila como borrada. Las inserciones y
actualizaciones se tratan del mismo modo.
Se puede obtener un timepoint más reciente confirmando la transacción
actual y emitiendo un nuevo SELECT
.
Esto se llama control de concurrencia multiversión.
Usuario A Usuario B SET AUTOCOMMIT=0; SET AUTOCOMMIT=0; tiempo | SELECT * FROM t; | empty set | INSERT INTO t VALUES (1, 2); | v SELECT * FROM t; empty set COMMIT; SELECT * FROM t; empty set COMMIT; SELECT * FROM t; --------------------- | 1 | 2 | --------------------- 1 row in set
En este ejemplo, el usuario A podrá ver la fila insertada por B solamente cuando B haya confirmado la inserción y A haya confirmado también, de modo que su timepoint avance e incluya la inserción confirmada por B.
Si se desea ver el “más reciente” estado de la base de
datos, se debería emplear ya sea el nivel de aislamiento READ
COMMITTED
o bien una lectura con bloqueo:
SELECT * FROM t LOCK IN SHARE MODE;
Una lectura con bloqueo, un UPDATE
, o un
DELETE
generalmente establecen bloqueos sobre cada
registro de índice que es examinado durante el procesamiento de la
consulta SQL. No son afectadas las filas excluidas por una condición
WHERE
en la consulta. InnoDB
no
recuerda exactamente la condición WHERE
, solamente
los rangos de índices que fueron examinados. Los bloqueos sobre los
registros son normalmente bloqueos de próxima clave, que también impiden
las inserciones en las posiciones vacías (“gap”)
inmediatamente anteriores a los registros.
Si los bloqueos a establecer son exclusivos, entonces
InnoDB
recupera también los registros de índices
agrupados (clustered) y los bloquea.
Si no hay índices apropiados para la consulta y MySQL debe examinar la tabla entera para procesarla, se bloqueará cada fila en la tabla, lo que impide cualquier inserción de otros usuarios. Es importante crear índices adecuados de modo que las consultas no examinen muchas filas innecesariamente.
SELECT ... FROM
es una lectura consistente, que
lee una captura de la base de datos y no establece bloqueos a
menos que el nivel de aislamiento de la transacción sea
SERIALIZABLE
. Para el nivel
SERIALIZABLE
, se establecen bloqueos compartidos
de próxima clave en los registros de índice encontrados.
SELECT ... FROM ... LOCK IN SHARE MODE
establece
bloqueos compartidos de próxima clave en todos los registros de
índice hallados por la lectura.
SELECT ... FROM ... FOR UPDATE
establece
bloqueos exclusivos de próxima clave en todos los registros de
índice hallados por la lectura.
INSERT INTO ... VALUES (...)
establece un bloqueo
exclusivo sobre la fila insertada. Nótese que no se trata de un
bloqueo de próxima clave, y no evita que otros usuarios inserten
registros en la posición vacía precedente. Si ocurriese un error por
duplicación de claves, se establecerá un bloqueo compartido sobre el
registro de índice duplicado.
Mientras se inicializa una columna previamente declarada
AUTO_INCREMENT
, InnoDB
establece un bloqueo exclusivo al final del índice asociado con
dicha columna. Al accederse al contador de autoincremento,
InnoDB
emplea una modo de bloqueo de tabla
específico llamado AUTO-INC
, que dura solamente
hasta el final de la actual consulta SQL, en lugar de existir hasta
el final de la transacción. Consulte Sección 15.10.2, “InnoDB
y AUTOCOMMIT
”.
En MySQL 5.0, InnoDB
trae el valor de una columna
previamente declarada AUTO_INCREMENT
sin
establecer ningún bloqueo.
INSERT INTO T SELECT ... FROM S WHERE ...
establece un bloqueo exclusivo (pero no de próxima clave) en cada
fila insertada dentro de T
. La búsqueda en
S
se hace como una lectura consistente, pero se
establecen bloqueos compartidos de próxima clave en
S
si está activado el registro binario (binary
logging) de MySQL. InnoDB
tiene que establecer
bloqueos en este último caso: en una recuperación de tipo
roll-forward desde una copia de respaldo, cada semtencia SQL debe
ser ejecutada en exactamente la misma manera en que se hizo
originalmente.
CREATE TABLE ... SELECT ...
lleva a cabo el
SELECT
como una lectura consistente o con
bloqueos compartidos, como en el punto anterior.
REPLACE
se ejecuta del mismo modo que una
inserción si no hay colisiones con claves únicas. En otro caso, se
coloca un bloqueo exclusivo de próxima clave en la fila que será
actualizada.
UPDATE ... WHERE ...
establece un bloqueo
exclusivo de próxima clave sobre cada registro encontrado por la
búsqueda.
DELETE FROM ... WHERE ...
establece un bloqueo
exclusivo de próxima clave sobre cada registro encontrado por la
búsqueda.
Si se define una restricción FOREIGN KEY
sobre
una tabla, cualquier inserción, actualización o eliminación que
necesite la verificación de las condiciones impuestas por la
restricción establecerá bloqueos compartidos a nivel de registro
sobre los registros examinados durante la verificación.
InnoDB
también establece estos bloqueos en el
caso de que la verificación falle.
LOCK TABLES
establece bloqueos de tabla, pero es
la capa de MySQL de mayor nivel por debajo de la capa de
InnoDB
la que establece estos bloqueos.
InnoDB
tiene conocimiento de los bloqueos de
tabla si se establecen innodb_table_locks=1
y
AUTOCOMMIT=0
, y la capa de MySQL por debajo de
InnoDB
sabe acerca de los bloqueos a nivel de
fila. En otro caso, la detección automática de deadlocks de InnoDB
no puede detectar los deadlocks donde estén involucradas estas
tablas. Además, puesto que la capa superior de MySQL no sabe acerca
de bloqueos a nivel de fila, es posible obtener un bloqueo de tabla
sobre una tabla donde otro usuario ha colocado bloqueos a nivel de
fila. Sin embargo, esto no pone en peligro la integridad de la
transacción, como se dice en
Sección 15.10.10, “Detección de interbloqueos (deadlocks) y cancelación de transacciones (rollbacks)”. Consulte también Sección 15.16, “Restricciones de las tablas InnoDB
”.
MySQL comienza cada conexión de cliente con el modo de ejecución automática (autocommit) habilitado por defecto. Cuando la ejecución automática está habilitada, MySQL realiza la confirmación luego de cada sentencia SQL, si dicha sentencia no devuelve un error.
Si se tiene desactivado el modo de ejecución automática y se cierra una conexión sin hacer una confirmación explícita de una transacción, MySQL cancelará dicha transacción.
Si una sentencia SQL devuelve un error, la confirmación o cancelación dependen del error. Consulte Sección 15.15, “Tratamiento de errores de InnoDB”.
Las siguientes sentencias SQL (y sus sinónimos) provocan en MySQL una confirmación implícita de la transacción en curso:
ALTER TABLE
, BEGIN
,
CREATE INDEX
, DROP
DATABASE
, DROP INDEX
,
DROP TABLE
, LOAD MASTER
DATA
, LOCK TABLES
,
RENAME TABLE
, SET
AUTOCOMMIT=1
, START
TRANSACTION
, TRUNCATE
,
UNLOCK TABLES
.
Antes de MySQL 5.0.8, CREATE TABLE
provocaba la
confirmación si se empleaba el registro binario (binary logging). A
partir de MySQL 5.0.8, las sentencias
CREATE TABLE
,
TRUNCATE TABLE
, DROP
DATABASE
, y CREATE DATABASE
provocan
una confirmación implícita.
La sentencia CREATE TABLE
en
InnoDB
se procesa como una transacción
individual. Esto significa que un ROLLBACK
emitido por el usuario no cancelará las sentencias CREATE
TABLE
hechas durante una transacción.
InnoDB
detecta automáticamente un deadlock de
transacciones y cancela una o más transacciones para evitarlo.
InnoDB
intenta escoger para cancelar transacciones
pequeñas, el tamaño de la transacción es determinado por el número de
filas insertadas, actualizadas, o eliminadas.
InnoDB
se mantiene al tanto de los bloqueos de tablas
si innodb_table_locks=1
(1 es el valor
predeterminado), y la capa MySQL por debajo sabe acerca de bloqueos a
nivel de fila. En otro caso, InnoDB
no puede detectar
deadlocks cuando están involucrados un bloqueo de tabla establecido
por una sentencia LOCK TABLES
o por otro motor de
almacenamiento que no sea InnoDB
. Estas situaciones
se deben resolver estableciendo el valor de la variable de sistema
innodb_lock_wait_timeout
.
Cuando InnoDB
lleva a cabo una cancelación completa
de una transacción, todos los bloqueos de la transacción son liberados.
Sin embargo, si solamente se cancela como resultado de un error una
sentencia SQL individual, algunos de los bloqueos impuestos por la
sentencia SQL podrían mantenerse. Esto se debe a que
InnoDB
guarda los bloqueos de fila en un formato en
el que no puede luego saber qué sentencia SQL originó cada bloqueo.
Los deadlocks son un problema clásico de las bases de datos transaccionales, pero no son peligrosos a menos que sean tan frecuentes que no se puedan ejecutar en absoluto ciertas transacciones. Normalmente, las aplicaciones deben ser escritas de modo que esten preparadas para emitir nuevamente una transacción si ésta es cancelada debido a un deadlock.
InnoDB
emplea bloqueos automáticos a nivel de fila.
Se pueden producir deadlocks aún en el caso de transacciones que
solamente insertan o eliminan una fila individual. Esto se debe a que
estas operaciones no son realmente “atómicas”; sino que
establecen automáticamente bloqueos enlos (posiblemente varios)
registros de índice de la fila insertada o eliminada.
Con las siguientes técnicas se puede estar a cubierto de los deadlocks y reducir la probabilidad de que ocurran:
Emplear SHOW INNODB STATUS
para determinar la
causa del último deadlock. Puede ayudar a afinar la aplicación para
evitar que ocurran otros.
Siempre hay que estar preparado para emitir nuevamente una transacción que haya fallado por un deadlock. Los deadlocks no revisten peligro, simplemente hay que intentar de nuevo.
Confirmar las transacciones frecuentemente. Las transacciones pequeñas son menos propensas a originar conflictos.
Si se están usando lecturas que establecen bloqueos
(SELECT ... FOR UPDATE
o
... LOCK IN SHARE MODE
), hay que intentar
utilizar un nivel de aislamiento bajo, como READ
COMMITTED
.
Acceder a las tablas y filas en un orden fijo. Entonces, las transacciones forman secuencias bien definidas y no originan deadlocks.
Agregar a las tablas índices adecuadamente elegidos. Entonces las
consultas necesitarán examinar menos registros de índice y en
consecuencia establecerán menos bloqueos. Utilizar EXPLAIN
SELECT
para determinar los índices que MySQL considera más
apropiados para las consultas.
Utilizar menos el bloqueo. Si es aceptable que
SELECT
devuelva datos de una captura de la
base de datos que no sea la más actualizada, no hay que agregarle
las cláusulas FOR
UPDATE
o LOCK IN SHARE MODE
. En este
caso es adecuado utilizar el nivel de aislamiento READ
COMMITTED
, porque cada lectura consistente dentro de la
misma transacción leerá de su propia captura más reciente.
Si nada de esto ayuda, habrá que serializar las transacciones con
bloqueos a nivel de tabla. La forma correcta de emplear LOCK
TABLES
con tablas transaccionales, como InnoDB, es
establecer AUTOCOMMIT = 0
y no invocar a
UNLOCK TABLES
hasta que se haya confirmado
explícitamente la transacción. Por ejemplo, si se necesitara
escribir en una tabla t1
y leer desde una tabla
t2
, se puede hacer esto:
SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ, ...; [aquí se hace algo con las tablas t1 y t2]; COMMIT; UNLOCK TABLES;
Los bloqueos a nivel de tabla favorecen el funcionamiento de la cola de transacciones, y evitan los deadlocks.
Otra manera de serializar transacciones es crear una tabla
“semáforo” auxiliar que contenga sólo una fila. Hay que
hacer que cada transacción actualice esa fila antes de acceder otras
tablas. De ese modo, todas las transacciones se producirán en serie.
Nótese que el algoritmo de detección instantánea de deadlocks de
InnoDB
también funciona en este caso, porque el
bloqueo de serialización es un bloqueo a nivel de fila. Con los
bloqueos a nivel de tabla de MySQL, debe emplearse el método de
timeout para solucionar deadlocks.
En aquellas aplicaciones que emplean el comando de MySQL
LOCK TABLES
, MySQL no establece bloqueos de tabla
si AUTOCOMMIT=1
.
Si la utilidad top
de Unix o el Administrador de
Tareas de Windows muestra que el porcentaje de uso de CPU durante la
carga de trabajo es inferior al 70%, probablemente se está trabajando
directamente sobre el disco. Podría suceder que se estén produciendo
excesivas confirmaciones de transacciones, o que el pool de buffer sea
muy pequeño. Puede ser de ayuda incrementar el tamaño del buffer, pero
no debe alcanzar ni superar el 80% del total de la memoria física del
ordenador.
Incluir varias modificaciones en una sola transacción.
InnoDB
debe descargar su registro (log) al disco
cada vez que se confirma una transacción, si dicha transacción realiza
modificaciones en la base de datos. Dado que la velocidad de rotación
de un disco es generalmente de 167 revoluciones por segundo, esto
restringe el número de confirmaciones a la misma fracción de segundo
si el disco no “engaña” al sistema operativo.
Si es aceptable la pérdida de alguna de las últimas transacciones
confirmadas, se puede establecer en my.cnf
el parámetro innodb_flush_log_at_trx_commit
a un
valor de 0. InnoDB
intenta descargar el registro
(log) una vez por segundo en cualquier caso, aunque la descarga no
está garantizada.
Incrementar el tamaño de los ficheros de registro (log), incluso hasta
equiparar el tamaño del pool de buffer. Cuando
InnoDB
ha colmado la capacidad de los ficheros de
log, debe escribir los contenidos modificados desde el pool de buffer
al disco en un punto de verificación. Los ficheros de log pequeños
pueden causar escrituras en disco innecesarias. La desventaja de los
ficheros de log grandes es que la recuperación demanda más tiempo.
También el buffer del log debe ser suficientemente grande (en el orden de los 8MB).
Emplear el tipo de columna VARCHAR
en lugar de
CHAR
si se almacenarán cadenas de longitud variable
o si la columna contendrá muchos valores NULL
. Una
columna CHAR(
siempre
utiliza N
)N
bytes para almacenar los datos,
inclusive si la cadena es más corta o es NULL
.
Las tablas más pequeñas aprovechan mejor el espacio del pool de buffer
y reducen las operaciones de E/S en disco.
Cuando se utiliza row_format=compact
(el formato de
registro predeterminado para InnoDB en MySQL 5.0) y un conjunto de
caracteres de longitud variable como utf8
o
sjis
,
CHAR(
ocupará una
cantidad variable de espacio, con un mínimo de
N
)N
bytes.
En algunas versiones de GNU/Linux y Unix, descargar ficheros a disco
con la función de Unix fsync()
(la cual es
utilizada en forma predeterminada por InnoDB
) y
otros métodos similares, es sorprendentemente lento. Si no se está
satisfecho con el rendimiento de las operaciones de escritura de la
base de datos, se puede intentar establecer el valor de
innodb_flush_method
en my.cnf
a O_DSYNC
, si bien O_DSYNC
parece ser más lento en otros sistemas.
Durante el empleo del motor de almacenamiento InnoDB en arquitecturas
Solaris 10 para x86_64 (AMD Opteron), es importante usar la opción
forcedirectio
al montar cualquier
sistema de ficheros usado para almacenar los ficheros relacionados con
InnoDB (el comportamiento predeterminado en Solaris 10/x86_64 es
no utilizar esta opción al montar el sistema de
ficheros). Si no se utiliza forcedirectio
se
producirá una seria degradación en la velocidad y rendimiento de
InnoDB en esta plataforma.
Al importar datos dentro de InnoDB
, hay que
asegurarse de que MySQL no tiene habilitado el modo de ejecución
automática (autocommit) porque provocaría una descarga del log a disco
en cada inserción. Para desactivar la ejecución automática durante la
operación de importación, hay que encerrarla entre sentencias
SET AUTOCOMMIT
y
COMMIT
:
SET AUTOCOMMIT=0; /* Sentencias de importación SQL ... */ COMMIT;
Si se utiliza la opción --opt
con
mysqldump, se obtienen ficheros de voclado que son
rápidos de importar en una tabla InnoDB
, incluso
sin encerrarlos en las sentencias SET AUTOCOMMIT
y
COMMIT
.
Tener cuidado con las cancelaciones de inserciones masivas:
InnoDB
emplea el buffer de inserciones para reducir
la cantidad de operaciones de E/S en disco durante las inserciones,
pero ese mecanismo no tiene efecto en la cancelación. Una cancelación
efectuada directamente sobre el disco puede tomar 30 veces el tiempo
que insumen las correspondientes inserciones. Matar el proceso del
servidor de bases de datos no es de ayuda, porque la cancelación
recomienza al volver a iniciar el servidor. La única forma de librarse
de una cancelación fuera de control es incrementar el tamaño del pool
de buffer para que la cancelación se haga sobre la CPU y se ejecute
más rápidamente, o utilizar un procedimiento especial. Consulte Sección 15.8.1, “Forzar una recuperación”.
También hay que tener cuidado con las operaciones de gran tamaño
realizadas directamente sobre el disco. Hay que emplear DROP
TABLE
y CREATE
TABLE
para obtener una tabla vacía, no DELETE FROM
.
tbl_name
Emplear la sintaxis de múltiples filas de INSERT
para reducir la carga extra de la comunicación entre el cliente y el
servidor si se necesita insertar muchos registros:
INSERT INTO yourtable VALUES (1,2), (5,5), ...;
Esta sugerencia es válida para las inserciones en cualquier tipo de
tabla, no solamente en InnoDB
.
Si se tienen restricciones UNIQUE
en claves
secundarias, se puede acelerar la importación desactivando
temporalmente, durante la importación, la verificación de tales
restricciones:
SET UNIQUE_CHECKS=0;
En tablas grandes, esto ahorra una gran cantidad de operaciones de E/S
en disco debido a que InnoDB
puede emplear su
buffer de inserción para escribir de una vez los registros de indice
secundarios.
Si se tienen restricciones FOREIGN KEY
en las
tablas, se puede acelerar la importación desactivando la verificación
de claves foráneas durante la misma:
SET FOREIGN_KEY_CHECKS=0;
Para tablas grandes, esto puede ahorrar gran cantidad de operaciones sobre el disco.
Si a menudo se realizan consultas sobre tablas que no se actualizan con frecuencia, utilizar el cache de consultas:
[mysqld] query_cache_type = ON query_cache_size = 10M
InnoDB
incluye los Monitores
InnoDB
que muestran información relativa al estado
interno de InnoDB
. Se puede emplear la sentencia SQL
SHOW INNODB STATUS
para obtener la salida del Monitor
InnoDB
estándar en el cliente SQL utilizado. Esta
información es útil para ajustes de rendimiento. (Si se usa el cliente
SQL interactivo mysql, la salida es más legible si se
reemplaza el punto y coma que usualmente termina cada sentencia por
\G
.) Para más información sobre los modos de bloqueo
de InnoDB
consulte Sección 15.10.1, “Modos de bloqueo InnoDB
”.
mysql> SHOW INNODB STATUS\G
Otra forma de emplear los Monitores InnoDB
es
permitirles escribir datos contínuamente en la salida estándar del
servidor mysqld. En este caso, no se envía la salida
a los clientes. Cuando se activa, los Monitores
InnoDB
imprimen datos aproximadamente cada 15
segundos. La salida del servidor normalmente se dirige a un fichero
.err
en el directorio de datos de MySQL. Estos
datos son útiles para ajustes de rendimiento. En Windows, se debe
iniciar el servidor desde la linea de comandos de una ventana de consola
con la opción --console
si se desea dirigir la salida
a la ventana en lugar de usar para ello el registro de errores.
La salida del Monitor incluye información de los siguientes tipos:
Tablas y bloqueos de registros en uso por cada transacción activa.
Esperas de transacciones debidas a bloqueos.
Esperas de subprocesos debidas a semáforos.
Solicitudes de E/S de ficheros pendientes.
Estadísticas del pool de buffer.
Actividad de descarga y mezcla del buffer de inserciones del
subproceso principal de InnoDB
.
Para que el Monitor estándar InnoDB
escriba en la
salida estándar de mysqld, se debe emplear la
siguiente sentencia SQL:
CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;
El monitor puede detenerse emitiendo la siguiente sentencia:
DROP TABLE innodb_monitor;
La sintaxis de CREATE TABLE
es solamente una forma de
pasar un comando al motor InnoDB
a través del
intérprete SQL de MySQL: Lo único importante aquí es que la tabla se
llame innodb_monitor
y que sea una tabla
InnoDB
. La estructura de la tabla no es relevante
para el MonitorInnoDB
. si se apaga
el servidor mientras el monitor se está ejecutando, y se desea iniciar
el monitor nuevamente, se debe eliminar la tabla antes de emitir una
nueva sentencia CREATE TABLE
para iniciar el monitor.
Esta sintaxis puede cambiar en una entrega futura de MySQL.
De la misma forma se puede emplear
innodb_lock_monitor
. Esto es lo mismo que
innodb_monitor
, con la excepción de que también
proporciona abundante información sobre bloqueos. Un
innodb_tablespace_monitor
separado imprime una lista
de los segmentos de ficheros creados existentes en el espacio de tablas
y valida las estructuras de datos de asignación del espacio de tablas.
Adicionalmente, hay un innodb_table_monitor
con el
que se pueden imprimir los contenidos del diccionario de datos interno
de InnoDB
.
Un ejemplo de la salida del
Monitor InnoDB
:
mysql> SHOW INNODB STATUS\G *************************** 1. row *************************** Status: ===================================== 030709 13:00:59 INNODB MONITOR OUTPUT ===================================== Per second averages calculated from the last 18 seconds ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 413452, signal count 378357 --Thread 32782 has waited at btr0sea.c line 1477 for 0.00 seconds the semaphore: X-lock on RW-latch at 41a28668 created in file btr0sea.c line 135 a writer (thread id 32782) has reserved it in mode wait exclusive number of readers 1, waiters flag 1 Last time read locked in file btr0sea.c line 731 Last time write locked in file btr0sea.c line 1347 Mutex spin waits 0, rounds 0, OS waits 0 RW-shared spins 108462, OS waits 37964; RW-excl spins 681824, OS waits 375485 ------------------------ LATEST FOREIGN KEY ERROR ------------------------ 030709 13:00:59 Transaction: TRANSACTION 0 290328284, ACTIVE 0 sec, process no 3195, OS thread id 34831 inser ting 15 lock struct(s), heap size 2496, undo log entries 9 MySQL thread id 25, query id 4668733 localhost heikki update insert into ibtest11a (D, B, C) values (5, 'khDk' ,'khDk') Foreign key constraint fails for table test/ibtest11a: , CONSTRAINT `0_219242` FOREIGN KEY (`A`, `D`) REFERENCES `ibtest11b` (`A`, `D`) ON DELETE CASCADE ON UPDATE CASCADE Trying to add in child table, in index PRIMARY tuple: 0: len 4; hex 80000101; asc ....;; 1: len 4; hex 80000005; asc ....;; 2: len 4; hex 6b68446b; asc khDk;; 3: len 6; hex 0000114e0edc; asc ...N..;; 4: len 7; hex 00000000c3e0a7; asc .......;; 5: len 4; hex 6b68446b; asc khDk;; But in parent table test/ibtest11b, in index PRIMARY, the closest match we can find is record: RECORD: info bits 0 0: len 4; hex 8000015b; asc ...[;; 1: len 4; hex 80000005; a sc ....;; 2: len 3; hex 6b6864; asc khd;; 3: len 6; hex 0000111ef3eb; asc ...... ;; 4: len 7; hex 800001001e0084; asc .......;; 5: len 3; hex 6b6864; asc khd;; ------------------------ LATEST DETECTED DEADLOCK ------------------------ 030709 12:59:58 *** (1) TRANSACTION: TRANSACTION 0 290252780, ACTIVE 1 sec, process no 3185, OS thread id 30733 inser ting LOCK WAIT 3 lock struct(s), heap size 320, undo log entries 146 MySQL thread id 21, query id 4553379 localhost heikki update INSERT INTO alex1 VALUES(86, 86, 794,'aA35818','bb','c79166','d4766t','e187358f' ,'g84586','h794',date_format('2001-04-03 12:54:22','%Y-%m-%d %H:%i'),7 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index symbole trx id 0 290252780 lock mode S waiting Record lock, heap no 324 RECORD: info bits 0 0: len 7; hex 61613335383138; asc a a35818;; 1: *** (2) TRANSACTION: TRANSACTION 0 290251546, ACTIVE 2 sec, process no 3190, OS thread id 32782 inser ting 130 lock struct(s), heap size 11584, undo log entries 437 MySQL thread id 23, query id 4554396 localhost heikki update REPLACE INTO alex1 VALUES(NULL, 32, NULL,'aa3572','','c3572','d6012t','', NULL,' h396', NULL, NULL, 7.31,7.31,7.31,200) *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index symbole trx id 0 290251546 lock_mode X locks rec but not gap Record lock, heap no 324 RECORD: info bits 0 0: len 7; hex 61613335383138; asc a a35818;; 1: *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index symbole trx id 0 290251546 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 82 RECORD: info bits 0 0: len 7; hex 61613335373230; asc aa 35720;; 1: *** WE ROLL BACK TRANSACTION (1) ------------ TRANSACTIONS ------------ Trx id counter 0 290328385 Purge done for trx's n:o < 0 290315608 undo n:o < 0 17 Total number of lock structs in row lock hash table 70 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 0 0, not started, process no 3491, OS thread id 42002 MySQL thread id 32, query id 4668737 localhost heikki show innodb status ---TRANSACTION 0 290328384, ACTIVE 0 sec, process no 3205, OS thread id 38929 in serting 1 lock struct(s), heap size 320 MySQL thread id 29, query id 4668736 localhost heikki update insert into speedc values (1519229,1, 'hgjhjgghggjgjgjgjgjggjgjgjgjgjgggjgjgjlhh gghggggghhjhghgggggghjhghghghghghhhhghghghjhhjghjghjkghjghjghjghjfhjfh ---TRANSACTION 0 290328383, ACTIVE 0 sec, process no 3180, OS thread id 28684 co mmitting 1 lock struct(s), heap size 320, undo log entries 1 MySQL thread id 19, query id 4668734 localhost heikki update insert into speedcm values (1603393,1, 'hgjhjgghggjgjgjgjgjggjgjgjgjgjgggjgjgjlh hgghggggghhjhghgggggghjhghghghghghhhhghghghjhhjghjghjkghjghjghjghjfhjf ---TRANSACTION 0 290328327, ACTIVE 0 sec, process no 3200, OS thread id 36880 st arting index read LOCK WAIT 2 lock struct(s), heap size 320 MySQL thread id 27, query id 4668644 localhost heikki Searching rows for update update ibtest11a set B = 'kHdkkkk' where A = 89572 ------- TRX HAS BEEN WAITING 0 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 0 page no 65556 n bits 232 table test/ibtest11a index PRIM ARY trx id 0 290328327 lock_mode X waiting Record lock, heap no 1 RECORD: info bits 0 0: len 9; hex 73757072656d756d00; asc supremum.;; ------------------ ---TRANSACTION 0 290328284, ACTIVE 0 sec, process no 3195, OS thread id 34831 ro llback of SQL statement ROLLING BACK 14 lock struct(s), heap size 2496, undo log entries 9 MySQL thread id 25, query id 4668733 localhost heikki update insert into ibtest11a (D, B, C) values (5, 'khDk' ,'khDk') ---TRANSACTION 0 290327208, ACTIVE 1 sec, process no 3190, OS thread id 32782 58 lock struct(s), heap size 5504, undo log entries 159 MySQL thread id 23, query id 4668732 localhost heikki update REPLACE INTO alex1 VALUES(86, 46, 538,'aa95666','bb','c95666','d9486t','e200498f ','g86814','h538',date_format('2001-04-03 12:54:22','%Y-%m-%d %H:%i'), ---TRANSACTION 0 290323325, ACTIVE 3 sec, process no 3185, OS thread id 30733 in serting 4 lock struct(s), heap size 1024, undo log entries 165 MySQL thread id 21, query id 4668735 localhost heikki update INSERT INTO alex1 VALUES(NULL, 49, NULL,'aa42837','','c56319','d1719t','', NULL, 'h321', NULL, NULL, 7.31,7.31,7.31,200) -------- FILE I/O -------- I/O thread 0 state: waiting for i/o request (insert buffer thread) I/O thread 1 state: waiting for i/o request (log thread) I/O thread 2 state: waiting for i/o request (read thread) I/O thread 3 state: waiting for i/o request (write thread) Pending normal aio reads: 0, aio writes: 0, ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0 Pending flushes (fsync) log: 0; buffer pool: 0 151671 OS file reads, 94747 OS file writes, 8750 OS fsyncs 25.44 reads/s, 18494 avg bytes/read, 17.55 writes/s, 2.33 fsyncs/s ------------------------------------- INSERT BUFFER AND ADAPTIVE HASH INDEX ------------------------------------- Ibuf for space 0: size 1, free list len 19, seg size 21, 85004 inserts, 85004 merged recs, 26669 merges Hash table size 207619, used cells 14461, node heap has 16 buffer(s) 1877.67 hash searches/s, 5121.10 non-hash searches/s --- LOG --- Log sequence number 18 1212842764 Log flushed up to 18 1212665295 Last checkpoint at 18 1135877290 0 pending log writes, 0 pending chkp writes 4341 log i/o's done, 1.22 log i/o's/second ---------------------- BUFFER POOL AND MEMORY ---------------------- Total memory allocated 84966343; in additional pool allocated 1402624 Buffer pool size 3200 Free buffers 110 Database pages 3074 Modified db pages 2674 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages read 171380, created 51968, written 194688 28.72 reads/s, 20.72 creates/s, 47.55 writes/s Buffer pool hit rate 999 / 1000 -------------- ROW OPERATIONS -------------- 0 queries inside InnoDB, 0 queries in queue Main thread process no. 3004, id 7176, state: purging Number of rows inserted 3738558, updated 127415, deleted 33707, read 755779 1586.13 inserts/s, 50.89 updates/s, 28.44 deletes/s, 107.88 reads/s ---------------------------- END OF INNODB MONITOR OUTPUT ============================ 1 row in set (0.05 sec)
Algunas notas acerca de la salida:
Si la sección TRANSACTIONS
informa sobre esperas
por bloqueo, la aplicación puede tener conflictos causados por
bloqueos. La salida también puede ayudar a determinar las razones de
interbloqueos en transacciones.
La seccion SEMAPHORES
informa sobre esperas de
subprocesos debidas a semáforos y brinda estadísticas de cuántas
veces los subprocesos han debido esperar por un mutex o un semáforo
rw-lock. Una gran cantidad de subprocesos esperando por semáforos
puede ser el resultado de operaciones de E/S en disco, o conflictos
dentro de InnoDB
. Los conflictos pueden deberse a
un intenso paralelismo en las consultas, o problemas en la planificación
de subprocesos del sistema operativo. En tales situaciones puede ser
de ayuda establecer innodb_thread_concurrency
en
un valor más bajo.
La sección BUFFER POOL AND MEMORY
proporciona
estadísticas sobre las páginas leídas y escritas. A partir de estas
cifras se puede calcular cuántas operaciones de E/S sobre ficheros
de datos están realizando actualmente las consultas emitidas.
La sección ROW OPERATIONS
muestra lo que está
haciendo el subproceso principal.
InnoDB
envía su salida de diagnóstico a
stderr
o a ficheros en lugar de a
stdout
o a buffers de memoria de tamaño fijo, para
evitar potenciales desbordamientos de buffer. Como efecto secundario, la
salida de SHOW INNODB STATUS
se escribe cada quince
segundos en un fichero de estado. El nombre de este fichero es
innodb_status.
, donde
pid
pid
es el ID del proceso del servidor. Este
fichero se crea en el directorio de datos de MySQL.
InnoDB
borra el fichero durante un apagado normal del
servidor. Si se producen caídas o terminaciones anormales del servidor,
pueden quedar copias de estos ficheros que deberán ser eliminadas
manualmente. Antes de eliminarlas, se las podría examinar para ver si
contienen información útil relativa a la causa de las caídas. En MySQL
5.0, innodb_status.
solamente se crea si se establece la opción de configuración
pid
innodb_status_file=1
.
Debido a que InnoDB
es una base de datos con
multiversión, debe llevar información acerca de las versiones anteriores
de una fila en el espacio de tablas. Esta información se almacena en una
estructura de datos llamada Rollback Segment (Segmento de Cancelación) al
igual que una estructura de datos análoga de Oracle.
Internamente, InnoDB
agrega dos campos a cada fila
almacenada en la base de datos. Un campo de 6 bytes indica el
identificador de la última transacción que insertó o actualizó la fila.
Además, una eliminación se trata internamente como una actualización en la
que un bit especial en la fila se establece a un valor que la señala como
eliminada. Cada registro también contiene un campo de 7 bytes llamado
"roll pointer" que apunta a una entrada del registro (log) de cancelación
de modificaciones (undo) grabado en el segmento de cancelación (RollBack
Segment). Si la fila fue actualizada, la entrada en el registro de
cancelación de modificaciones (undo log) contiene la información necesaria
para recrear el contenido de la fila tal como estaba antes de
actualizarse.
InnoDB
utiliza la información en el segmento de
cancelación para deshacer los cambios durante la cancelación de una
transacción. También la emplea para generar versiones anteriores de una
fila en una lectura consistente.
Los registros de cancelación de modificaciones en el segmento de
cancelación se dividen entre originados por inserciones (insert undo logs)
y actualizaciones (update undo logs). Los insert undo logs se necesitan
solamente para la cancelación de transacciones y se descartan tan pronto
como se confirma la transacción. Los update undo logs se emplean también
para lecturas consistentes, y pueden descartarse solamente cuando no
quedan transacciones integrando una captura tomada por
InnoDB
, que en una lectura consistente podría necesitar
esta información para reconstruir versiones anteriores de una fila.
Se deben confirmar las transacciones regularmente, incluyendo aquellas que
solamente realizan lecturas consistentes. De otro modo,
InnoDB
no podrá descartar datos de los update undo
logs, y el segmento de cancelación puede crecer en demasía, llenando
el espacio de tablas.
El tamaño físico de una entrada en el registro de cancelación de cambios en el segmento de cancelación es generalmente menor que el de la correspondiente fila insertada o actualizada. Se puede emplear esta información para calcular el espacio necesario para el segmento de cancelación.
En el esquema de multiversión de InnoDB
, una fila no es
quitada inmediatamente de la base de datos cuando se la elimina mediante
una sentencia SQL. Sólo cuando InnoDB
pueda descartar
la entrada en el registro de cancelación de modificaciones creada para la
eliminación, procederá a quitar físicamente de la base de datos la fila y
sus entradas en los índices. Esta operación se llama depuración (purge) y
es bastante rápida, generalmente requiere el mismo tiempo que la sentencia
SQL que realizó la eliminación.
En un escenario donde el usuario inserte y elimine filas aproximadamente
en la misma proporción y en pequeños lotes, es posible que el subproceso
de depuración comience a sufrir retrasos y la tabla crezca contínuamente,
haciendo muy lenta cualquier operación que se realice sobre el disco.
Incluso si una tabla contuviera sólo 10 MB de datos útiles, puede crecer
hasta ocupar 10 GB con las filas “muertas”. En tal caso
podría ser bueno limitar las operaciones de filas nuevas y asignar más
recursos al subproceso de depuración. La opción de inicio (y también
variable global configurable) innodb_max_purge_lag
existe precisamente para este propósito. Consulte Sección 15.4, “Opciones de arranque de InnoDB
” para más información.
MySQL almacena la información de su diccionario de datos de tablas en
ficheros .frm
dentro del directorio de cada base de
datos. Esto es así para todos los motores de almacenamiento de MySQL, pero
cada tabla InnoDB
también tiene su propia entrada en
los diccionarios de datos internos de InnoDB
dentro del
espacio de tablas. Cuando MySQL elimina una tabla o una base de datos,
también debe eliminar uno o más ficheros .frm
, y las
correspondientes entradas dentro del diccionario de datos de
InnoDB
. Esta es la razón por la cual no se pueden mover
tablas entre bases de datos sencillamente moviendo los ficheros
.frm
.
Cada tabla InnoDB
tiene un índice especial llamado
índice agrupado (clustered index) donde se almacenan los datos de las
filas. Si se define una PRIMARY KEY
en una tabla, el
índice de la clave primaria es el índice agrupado.
Si no se define una PRIMARY KEY
para la tabla, MySQL
toma como clave primaria el primer índice UNIQUE
que
tenga solamente columnas NOT NULL
, al cual
InnoDB
utiliza como índice agrupado. Si no hay en la
tabla un índice con esas características, InnoDB
generará internamente un índice agrupado donde las filas estarán ordenadas
por el identificador de fila (row ID) que InnoDB
asigna a las columnas en tal tabla. El identificador de fila es un campo
de 6 bytes que se incrementa automáticamente a medida que se agregan
nuevas filas. Por lo tanto, las filas ordenadas por este identificador
están en el orden físico de inserción.
El acceso a una fila a través del índice agrupado es rápido porque la fila de datos se encuentra en la misma página a donde el índice dirige su búsqueda. Si una tabla es grande, la arquitectura del índice agrupado a menudo ahorra operaciones de E/S en disco en comparación a la solución tradicional. (En muchos servidores de bases de datos, los datos se suelen almacenar en una página diferente que la entrada del índice).
En InnoDB
, las entradas en índices no agrupados
(también llamados índices secundarios) contienen el valor de clave
primaria de la fila. InnoDB
utiliza este valor de clave
primaria para buscar la fila a partir del índice agrupado. Nótese que si
la clave primaria es larga, los índices secundarios utilizan más espacio.
InnoDB
compara las cadenas CHAR
y
VARCHAR
de diferente longitud como si el espacio
sobrante en cadena la más corta estuviera relleno con espacios.
Todos los índices en InnoDB
son árboles binarios
(B-trees) donde las entradas de índice se almacenan en páginas que son
las hojas del árbol. El tamaño predeterminado de una página de índice es
16KB. Cuando se insertan nuevas entradas, InnoDB
intenta reservar 1/16 de la página para futuras inserciones y
actualizaciones en el índice.
Si las entradas del índice se insertan en un orden secuencial (sea
ascendente o descendente), las páginas de índice se llenarán en
aproximadamente 15/16 de su capacidad. Si las entradas se insertan en un
orden aleatorio, las páginas se llenarán entre 1/2 y 15/16 de su
capacidad. Si la proporción de espacio ocupado de una página de índice
cae por debajo de 1/2, InnoDB
intentará reducir el
árbol de índice para liberar la página.
Una situación común en una aplicación de bases de datos es que la clave primaria sea un identificador único y las nuevas filas se inserten en el orden ascendente de esta clave. Por lo tanto, las inserciones en el índice agrupado no necesitan lecturas del disco aleatorias.
Por el otro lado, los índices secundarios generalmente no son únicos, y
las inserciones en los mismos ocurren en un orden relativamente
aleatorio. Esto podría ocasionar gran cantidad de operaciones de E/S en
disco, de no ser por un mecanismo especial utilizado en
InnoDB
.
Si una entrada de índice debiera insertarse en un índice secundario no
único, InnoDB
verifica si la página del índice
secundario se encuentra en el pool de buffer. Si ese es el caso,
InnoDB
realizará la inserción directamente en la
página de índice. Si dicha página no se encuentra en el pool de buffer,
InnoDB
insertará la entrada en una estructura de
buffer de inserciones especial. El buffer de inserciones se mantiene tan
pequeño que cabe completamente en el pool de buffer, y las inserciones
pueden hacerse muy rápidamente.
Periódicamente, el buffer de inserciones se integra a los árboles de índices secundarios de la base de datos. A menudo es posible integrar varias inserciones en la misma página del árbol de índices, ahorrando operaciones de E/S en disco. Se ha comprobado que el buffer de inserciones puede acelerar hasta 15 veces las inserciones en una tabla.
La integración del buffer de inserciones puede continuar luego de que la transacción que realiza la inserción ha sido confirmada. De hecho, puede continuar despues de que el servidor ha sido detenido y vuelto a iniciar (consulte Sección 15.8.1, “Forzar una recuperación”).
La integración del buffer de inserciones puede tomar muchas horas, cuando hay varios índices secundarios para actualizar y gran cantidad de filas insertadas. Durante este tiempo, las operaciones de E/S en disco se incrementan, lo cual puede lentificar significativamente las consultas sobre el disco. Otra operación de E/S de importancia que se produce en segundo plano es el subproceso de depuración (Consulte Sección 15.12, “Implementación de multiversión”).
Si una tabla cabe casi completamente en la memoria principal, la manera
más rápida de ejecutar consultas sobre ella es empleando índices hash.
InnoDB
posee un mecanismo automático que supervisa
las búsquedas realizadas sobre los índices de una tabla. Si
InnoDB
advierte que las consultas se podrían
beneficiar con la creación de un índice hash, lo hará automáticamente.
El índice hash siempre está basado en un índice B-tree existente en la
tabla. InnoDB
puede generar un indíce hash sobre un
prefijo de la clave definida para el B-tree de cualquier longitud,
dependiendo del patrón de búsquedas que InnoDB
observe en el índice B-tree. Un índice hash puede ser parcial: no se
necesita que el índice B-tree completo sea alojado en el pool de buffer.
InnoDB
genera índices hash según sea necesario
basándose en las páginas de índice que son utilizadas más frecuentemente.
Podría decirse que, a través del mecanismo de índices hash adaptativos,
InnoDB
se adapta a la memoria principal, acercándose
así a la arquitectura de las bases de datos de memoria.
Los registros de las tablas InnoDB
tienen las
siguientes características:
Cada registro de índice en InnoDB
contiene un
encabezado de seis bytes. El encabezado se emplea para enlazar
juntos registros consecutivos, y también en el bloqueo a nivel de
fila.
Los registros en el índice agrupado contienen campos para todas las columnas definidas por el usuario. Adicionalmente, hay un campo de seis bytes para el IDentificador de transacción y un campo de siete bytes para el roll pointer.
Si no se definió una clave primaria para la tabla, cada registro de índice agrupado contiene también un campo de IDentificación de fila de seis bytes.
Cada registro de índice secundario contiene también todos los campos definidos para la clave del índice agrupado.
Un registro contiene además un puntero a cada campo del mismo. Si la longitud total de los campos en un registro es menos de 128 bytes, el puntero medirá un byte, de lo contrario, tendrá dos bytes de longitud. La matriz de estos punteros se conoce como el directorio de registros. El área a donde señalan estos punteros se denomina la parte de datos del registro.
Internamente, InnoDB
almacena las columnas de
caracteres de longitud fija (como CHAR(10)
) en un
formato de longitud fija. InnoDB
trunca los
espacios sobrantes de las columnas VARCHAR
.
Nótese que MySQL puede convertir internamente columnas
CHAR
a VARCHAR
. Consulte
Sección 13.1.5.1, “Cambios tácitos en la especificación de columnas”.
Un valor NULL
SQL reserva 1 o 2 bytes en el
directorio de registros. No reservará ningún byte en la parte de
datos del registro si se lo almacena en una columna de longitud
variable. En una columna de longitud fija, reservará en la parte de
datos la longitud asignada a dicha columna. La razón por la que se
reserva este espacio fijo a pesar de tratarse de un valor
NULL
, es que en el futuro se podrá insertar en su
lugar un valor no-NULL
sin provocar la
fragmentación de la página de índice.
InnoDB
emplea E/S en disco asíncrona simulada:
InnoDB
crea un número de procesos para hacerse cargo
de las operaciones de E/S, tal como lectura por adelantado (read-ahead).
En InnoDB
hay dos métodos de lectura por adelantado:
En la lectura por adelantado secuencial, si
InnoDB
advierte que el patrón de acceso a un
segmento en el espacio de tablas es secuencial, envía por adelantado
al sistema de E/S un lote de lectura de páginas de base de datos.
En la lectura por adelantado aleatoria, si InnoDB
advierte que algún sector del espacio de tablas parece estar en
proceso de ser completamente leido dentro del pool de búfer, envía
las lecturas restantes al sistema de E/S.
InnoDB
emplea una novedosa técnica de descarga de
ficheros llamada doublewrite. La misma incrementa
la seguridad en la recuperación que sigue a una caida del sistema
operativo o una interrupción de energía eléctrica, y mejora el
rendimiento en muchas variedades de Unix al reducir la necesidad de usar
operaciones fsync()
.
Doublewrite (escritura doble) significa que antes de escribir páginas en
un fichero de datos, InnoDB
las escribe primero en un
área contigua del espacio de tablas llamada el búfer de doublewrite
(o de escritura doble). Solamente luego de que la escritura y descarga
al búfer de escritura doble se ha completado, InnoDB
escribe las páginas en el sitio apropiado del fichero de datos. Si el
sistema operativo colapsa en el transcurso de una escritura de página,
InnoDB
, posteriormente, durante la recuperación, podrá
hallar una copia en buen estado en el búfer de escritura doble.
En MySQL 5.0, se pueden usar particiones de dispositivos en bruto como ficheros de datos del espacio de tablas. Utilizando un dispositivo en bruto, se pueden llevar a cabo operaciones de E/S en Windows y algunas versiones de Unix sin que utilicen el búfer y sin la sobrecarga producida por el sistema de ficheros, lo cual incrementa el rendimiento.
Cuando se crea un nuevo fichero de datos, se debe colocar la palabra
clave newraw
inmediatamente a continuación del tamaño
del fichero de datos en innodb_data_file_path
. La
partición deberá ser al menos tan grande como el tamaño que se haya
especificado. Nótese que 1MB en InnoDB
significa 1024
* 1024 bytes, en tanto que 1MB, en las especificaciones de los discos,
generalmente significa 1.000.000 de bytes.
[mysqld] innodb_data_home_dir= innodb_data_file_path=/dev/hdd1:3Gnewraw;/dev/hdd2:2Gnewraw
La próxima vez que se inicie el servidor, InnoDB
advertirá la palabra clave newraw
e inicializará la
nueva partición. Sin embargo, aún no creará ni modificará ninguna tabla
InnoDB
. De lo contrario, la próxima vez que se
reiniciase el servidor, InnoDB
reinicializaría la
partición y los cambios se perderían. (A partir de la versión 3.23.44,
como medida de seguridad, InnoDB
impide que los
usuarios modifiquen datos cuando se especifica una partición con
newraw
.)
Después que InnoDB
ha inicializado la nueva
partición, hay que detener el servidor y cambiar
newraw
por raw
en la linea que
especifica el fichero de datos:
[mysqld] innodb_data_home_dir= innodb_data_file_path=/dev/hdd1:5Graw;/dev/hdd2:2Graw
Luego, al reiniciar el servidor InnoDB
permitirá
realizar cambios.
En Windows puede asignarse una partición de disco como fichero de datos de este modo:
[mysqld] innodb_data_home_dir= innodb_data_file_path=//./D::10Gnewraw
Los caracteres //./
se corresponden con la sintaxis
Windows de \\.\
para acceder dispositivos físicos.
Al emplear particiones de dispositivos en bruto, hay que cerciorarse de que la cuenta de usuario usada para ejecutar el servidor MySQL tiene permisos de lectura y escritura sobre ellas.
Los ficheros de datos definidos en el fichero de configuración forman el
espacio de tablas de InnoDB
. Los ficheros simplemente
son concatenados para formar el espacio de tablas. No se utiliza
striping (grabación de datos a través de varios discos en simultáneo).
Actualmente no se puede definir en qué parte del espacio de tablas se
ubicarán las tablas. Sin embargo, en un espacio de tablas nuevo,
InnoDB
asigna el espacio comenzando por el primer
fichero de datos.
El espacio de tablas consiste en páginas de base de datos con un tamaño
por defecto de 16KB. Las páginas se agrupan en áreas de 64 páginas
consecutivas. Los “ficheros” dentro de un espacio de tablas
se llaman segmentos en InnoDB
.
El término “segmento de cancelación” (rollback segment) es
un tanto confuso porque en realidad contiene varios segmentos del
espacio de tablas.
Por cada índice de InnoDB
se asignan dos segmentos.
Uno es para los nodos que no son hojas del B-tree, el otro es para los
nodos hoja. La idea es mejorar la secuencialidad de los nodos hoja,
los cuales contienen los datos.
Cuando un segmento crece dentro del espacio de tablas,
InnoDB
ubica las primeras 32 páginas individualmente.
Luego de ello, comienza a ubicar áreas enteras en el segmento.
InnoDB
puede adicionar a un segmento grande hasta 4
áreas de páginas cada vez, para asegurar una adecuada secuencialidad
de los datos.
Algunas páginas en el espacio de tablas contienen bitmaps de otras
páginas, por lo tanto unas pocas áreas en un espacio de tablas
InnoDB
no puede asignarse a segmentos como un todo,
sino solamente como páginas individuales.
Cuando se consulta el espacio libre disponible en el espacio de tablas
mediante una sentencia SHOW TABLE STATUS
,
InnoDB
informa las áreas que están totalmente libres
en el espacio de tablas. InnoDB
siempre reserva
algunas áreas para depuración y otros propósitos internos; estas áreas
reservadas no se cuentan en el espacio libre.
Cuando se eliminan datos de una tabla, InnoDB
reduce
los correspondientes índices B-tree. Depende del patrón seguido por
las eliminaciones, si se liberan páginas individuales o áreas del
espacio de tablas, de forma que el espacio desocupado quede disponible
para otros usuarios. Eliminar una tabla, o todas las filas que contiene,
seguramente servirá para liberar el espacio, pero no hay que olvidar que
las filas eliminadas solamente desaparecen físicamente cuando dejan de
ser necesarias para cancelar transacciones o de integrar lecturas
consistentes.
Si se producen inserciones o eliminaciones aleatorias en los índices de una tabla, los índices pueden resultar fragmentados. Esto significa que el orden físico de las páginas de índice en el disco no guarda relación con el orden de los registros en las páginas, o que hay muchas páginas en blanco en los bloques de 64 páginas que se asignan al índice.
Un síntoma de la fragmentación es que una tabla ocupa más espacio del que 'debería' ocupar. Es difícil determinarlo con exactitud, ya que todos los datos e índices en InnoDB se almacenan en estructuras B-tree, cuya porporción de espacio ocupado (fillfactor) puede variar entre el 50% y el 100%. Otro síntoma de fragmentación es que una consulta que examine toda la tabla:
SELECT COUNT(*) FROM t WHERE a_non_indexed_column <> 12345;
toma más tiempo del que debería. (En la consulta anterior, se ha “engañado” al optimizador SQL para que examine el índice agrupado, no un índice secundario). La mayoría de los discos pueden leer entre 10 y 50 MB por segundo. Esto puede usarse para estimar la velocidad con que debería examinarse una tabla.
Se puede acelerar el examen de los índices si periódicamente se lleva a
cabo una operación ALTER TABLE
“neutra”:
ALTER TABLE tbl_name
ENGINE=INNODB
Esto provoca que MySQL reconstruya la tabla. Otra forma de ejecutar una desfragmentación es emplear mysqldump para obtener un volcado de la tabla en un fichero de texto, eliminar la tabla, y volver a crearla a partir del fichero de volcado.
Si las inserciones en un índice se producen siempre en orden ascendente
y los registros se eliminan solamente desde el final, el algoritmo de
gestión de espacio en fichero que tiene InnoDB
garantiza que no se produzca la fragmentación del índice.
El tratamiento de errores en InnoDB
no siempre es como
se especifica en el estándar SQL. De acuerdo a éste, cualquier error
durante la ejecución de una sentencia SQL debería ocasionar su
cancelación. InnoDB
a veces sólo cancela una parte de
la sentencia, o la transacción completa. Los siguientes puntos describen
cómo InnoDB
lleva a cabo el tratamiento de errores:
Si el espacio de tablas agota su espacio disponible en disco, se
obtiene el error de MySQL Table is full
(La tabla
está llena) e InnoDB
cancela la sentencia SQL.
Un interbloqueo (deadlock) en una transacción o un exceso de espera
(timeout) en una espera por bloqueo provocan que
InnoDB
cancele la transacción completa.
Un error de clave duplicada cancelará la sentencia SQL, si ésta no
contiene la opción IGNORE
.
Un error row too long error
(registro demasiado
largo) cancela la sentencia SQL.
Los demás errores son, en su mayoría, detectados por la capa de código
MySQL (por encima del nivel del motor de almacenamiento
InnoDB
) y causarán la cancelación de la
correspondiente sentencia SQL. Los bloqueos no se liberan al cancelar
una única sentencia SQL.
Durante una cancelación implícita, así como durante la ejecución de un
comando SQL ROLLBACK
explícito, SHOW
PROCESSLIST
muestra Rolling back
en la
columna State
de la conexión afectada.
La siguiente es una lista con los errores específicos de
InnoDB
más comunes, con información acerca de su
causa y su solución.
1005 (ER_CANT_CREATE_TABLE)
No se puede crear la tabla. Si el mensaje del error hace referencia
al errno
150, la creación de la tabla falló
debido a una restricción de clave foránea incorrectamente formulada.
1016 (ER_CANT_OPEN_FILE)
No se puede hallar la tabla InnoDB
en los
ficheros de datos, si bien existe el fichero
.frm
para esa tabla. Consulte
Sección 15.17.1, “Resolver problemas de las operaciones del diccionario de datos de InnoDB
”.
1114 (ER_RECORD_FILE_FULL)
InnoDB
se ha quedado sin lugar en el espacio de
tablas. Se debería reconfigurar el espacio de tablas para agregar un
nuevo fichero de datos.
1205 (ER_LOCK_WAIT_TIMEOUT)
Expiró el tiempo de espera para realizar un bloqueo. La transacción se canceló.
1213 (ER_LOCK_DEADLOCK)
Ocurrió un deadlock durante una transacción. Deberá ser repetida.
1216 (ER_NO_REFERENCED_ROW)
Se está intentando agregar una fila, pero no hay una fila padre, lo que hace que una restricción de clave foránea falle. Se debe insertar antes la fila padre.
1217 (ER_ROW_IS_REFERENCED)
Se está intentando eliminar una fila padre que tiene filas hijas, lo que hace que una restricción de clave foránea falle. Se deben eliminar primero las filas hijas.
Para ver el significado de un número de error del sistema operativo, se utiliza el programa perror, que viene con la distribución de MySQL.
La siguiente tabla proporciona una lista con algunos códigos de error de sistema comunes en Linux. Para una lista más completa comsulte El código fuente de Linux.
1 (EPERM)
Operación no permitida
2 (ENOENT)
No existe el fichero o directorio
3 (ESRCH)
No existe el proceso
4 (EINTR)
Llamada de sistema interrumpida
5 (EIO)
Error de E/S
6 (ENXIO)
No existe el dispositivo o dirección
7 (E2BIG)
Lista de argumentos demasiado extensa
8 (ENOEXEC)
Eror de formato ejecutable
9 (EBADF)
Número de fichero erróneo
10 (ECHILD)
No hay procesos hijos
11 (EAGAIN)
Intente nuevamente
12 (ENOMEM)
Memoria agotada
13 (EACCES)
Permiso denegado
14 (EFAULT)
Dirección errónea
15 (ENOTBLK)
Se necesita un bloque de dispositivo
16 (EBUSY)
El dispositivo o recurso está ocupado
17 (EEXIST)
El fichero ya existe
18 (EXDEV)
Vínculo de dispositivos cruzado (Cross-device link)
19 (ENODEV)
No existe el dispositivo
20 (ENOTDIR)
No es un directorio
21 (EISDIR)
Es un directorio
22 (EINVAL)
Argumento inválido
23 (ENFILE)
Desbordamiento de tabla de fichero
24 (EMFILE)
Demasiados ficheros abiertos
25 (ENOTTY)
Ioctl no apropiada para el dispositivo
26 (ETXTBSY)
Fichero de texto ocupado
27 (EFBIG)
El fichero es demasiado grande
28 (ENOSPC)
Espacio agotado en el dispositivo
29 (ESPIPE)
Búsqueda ilegal
30 (EROFS)
Fichero de sistema de sólo lectura
31 (EMLINK)
Demasiados vínculos
La siguiente tabla proporciona una lista con algunos códigos de error de sistema comunes en Windows. Para una lista completa consulte el Microsoft sitio web.
1 (ERROR_INVALID_FUNCTION)
Función incorrecta
2 (ERROR_FILE_NOT_FOUND)
El sistema no puede hallar el fichero especificado
3 (ERROR_PATH_NOT_FOUND)
El sistema no puede hallar la ruta especificada
4 (ERROR_TOO_MANY_OPEN_FILES)
El sistema no puede abrir el fichero.
5 (ERROR_ACCESS_DENIED)
Acceso denegado.
6 (ERROR_INVALID_HANDLE)
El manejador es inválido.
7 (ERROR_ARENA_TRASHED)
Los bloques de control de almacenamiento fueron destruidos.
8 (ERROR_NOT_ENOUGH_MEMORY)
No hay suficiente almacenamiento disponible para procesar este comando.
9 (ERROR_INVALID_BLOCK)
La dirección del bloque de control de almacenamiento es inválida.
10 (ERROR_BAD_ENVIRONMENT)
El entorno es incorrecto.
11 (ERROR_BAD_FORMAT)
Se intentó cargar un programa con un formato incorrecto.
12 (ERROR_INVALID_ACCESS)
El código de acceso es inválido.
13 (ERROR_INVALID_DATA)
El dato es inválido.
14 (ERROR_OUTOFMEMORY)
No hay suficiente espacio de almacenamiento para completar esta operación.
15 (ERROR_INVALID_DRIVE)
El sistema no puede hallar la unidad especificada.
16 (ERROR_CURRENT_DIRECTORY)
El directorio no puede eliminarse.
17 (ERROR_NOT_SAME_DEVICE)
El sistema no puede mover el fichero a una unidad de disco diferente.
18 (ERROR_NO_MORE_FILES)
No hay más ficheros.
19 (ERROR_WRITE_PROTECT)
El medio de almacenamiento eatá protegido contra escritura.
20 (ERROR_BAD_UNIT)
El sistema no puede hallar el dispositivo especificado.
21 (ERROR_NOT_READY)
El dispositivo no está listo.
22 (ERROR_BAD_COMMAND)
El dispositivo no reconoce el comando.
23 (ERROR_CRC)
Error de datos (verificación de redundancia cíclica)
24 (ERROR_BAD_LENGTH)
El programa emitió un comando pero la longitud del comando es incorrecta.
25 (ERROR_SEEK)
La unidad no puede hallar un área o pista específica en el disco.
26 (ERROR_NOT_DOS_DISK)
No puede accederse al disco o diskette especificado.
27 (ERROR_SECTOR_NOT_FOUND)
La unidad no puede hallar el sector solicitado.
28 (ERROR_OUT_OF_PAPER)
La impresora no tiene papel.
29 (ERROR_WRITE_FAULT)
El sistema no puede escribir en la unidad especificada.
30 (ERROR_READ_FAULT)
El sistema no puede leer desde la unidad especificada.
31 (ERROR_GEN_FAILURE)
Un dispositivo conectado al sistema está fuera de funcionamiento.
32 (ERROR_SHARING_VIOLATION)
El proceso no puede acceder al fichero porque está en uso por otro proceso.
33 (ERROR_LOCK_VIOLATION)
El proceso no puede acceder al fichero porque una parte fue bloqueada por otro proceso.
34 (ERROR_WRONG_DISK)
La unidad contiene el diskette incorrecto. Inserte %2 (Número de Serie de Volumen: %3) en la unidad %1.
36 (ERROR_SHARING_BUFFER_EXCEEDED)
Demasiados ficheros abiertos en modo compartido.
38 (ERROR_HANDLE_EOF)
Se alcanzó el fin del fichero.
39 (ERROR_HANDLE_DISK_FULL)
El disco está lleno.
87 (ERROR_INVALID_PARAMETER)
El parámetro es incorrecto. (Si se obtiene este error en Windows, y
se ha establecido innodb_file_per_table
en
my.cnf
o my.ini
, entonces
debe agregarse la línea
innodb_flush_method=unbuffered
en el fichero
my.cnf
o my.ini
.)
112 (ERROR_DISK_FULL)
El disco está lleno.
123 (ERROR_INVALID_NAME)
El nombre de fichero, de directorio, o la etiqueta de volumen, tienen la sintaxis incorrecta.
1450 (ERROR_NO_SYSTEM_RESOURCES)
No hay suficientes recursos de sistema para completar el servicio requerido.
Una tabla no puede contener más de 1000 columnas.
La longitud máxima interna de una clave es 3500 bytes, pero MySQL la restringe a 1024 bytes.
La longitud máxima de fila, excepto para columnas
VARCHAR
, BLOB
y
TEXT
, es ligeramente inferior a la mitad de una
página de base de datos. Es decir, cerca de 8000 bytes. Las columnas
LONGBLOB
y LONGTEXT
deben ser de
menos de 4GB, y la longitud total de la fila, incluyendo las columnas
BLOB
y TEXT
, debe ser de menos
de 4GB. InnoDB
almacena los primeros 768 bytes de
una columna VARCHAR
, BLOB
, o
TEXT
en la fila, y el resto, en páginas separadas.
En algunos sistemas operativos antiguos, los ficheros de datos deben ser de menos de 2GB.
El tamaño combinado de los ficheros de log de
InnoDB
debe ser inferior a 4GB.
El tamaño mínimo del espacio de tablas es de 10MB. El tamaño máximo es de cuatrocientos mil millones de páginas de base de datos (64TB). Este es también el tamaño máximo para una tabla.
Las tablas InnoDB
no admiten índices
FULLTEXT
.
Las tablas InnoDB
no admiten tipos de columna
espaciales.
ANALYZE TABLE
determina la
cardinalidad
efectuando 10 accesos al azar en cada
uno de los árboles de índices y actualizando la cardinalidad del
índice con una estimación acorde. Dado que son solamente estimaciones,
distintas ejecuciones de ANALYZE TABLE
pueden
producir resultados diferentes. Esto convierte a ANALYZE
TABLE
en una herramienta rápida sobre tablas
InnoDB
, pero no con el mismo nivel de exactitud que
si considerara todas las filas al hacer el recuento.
MySQL emplea las estimaciones de cardinalidad de los índices solamente
para la optimización de uniones. Si una unión no se optimiza en la
manera adecuada, se puede intentar el uso de ANALYZE
TABLE
. En los pocos casso en que ANALYZE
TABLE
no produce valores suficientemente buenos para las
tablas, se puede emplear FORCE INDEX
en las
consultas para forzar el uso de un índice en particular, o establecer
el valor de max_seeks_for_key
para asegurarse de
que MySQL dará preferencia a las búsquedas en índices por sobre el
examen de las tablas. Consulte Sección 5.3.3, “Variables de sistema del servidor”. Consulte Sección A.6, “Cuestiones relacionadas con el optimizados”.
En Windows, InnoDB
siempre almacena internamente en
minúsculas los nombres de tablas y bases de datos. Para mover bases de
datos en formato binario desde Unix a Windows o a la inversa, se
deberán haber escrito en minúsculas todos los nombres de tablas y
bases de datos.
Advertencia: ¡No
deben convertirse las tablas de sistema de MySQL de la base de datos
mysql
desde su formato original
MyISAM
a InnoDB
! Esta es una
operación no admitida. Si se lleva a cabo, MySQL no se podrá ejecutar
hasta que se recuperen las tablas de sistema anteriores desde una
copia de respaldo o se las regenere con el script
mysql_install_db.
InnoDB
no lleva una cuenta interna de las filas en
una tabla. (Esto sería realmente complicado a causa de la
multiversión). Para procesar una sentencia SELECT
COUNT(*) FROM T
, InnoDB
debe examinar un
índice de la tabla, lo cual lleva algún tiempo si el índice no está
completamente dentro del pool de buffer. Para disponer de un recuento
más rápido, se debe crear una tabla de recuento y hacer que la
aplicación la actualice a medida que se producen inserciones y
eliminaciones. Si una tabla no se modifica a menudo, utilizar el cache
de consultas (query cache) de MySQL es una buena solución. También
puede emplearse SHOW TABLE STATUS
si es suficiente
un recuento aproximado de filas. Consulte Sección 15.11, “Consejos de afinamiento del rendimiento de InnoDB
”.
Para una columna AUTO_INCREMENT
, siempre se debe
definir un índice para la tabla, el cual debe contener solamente a la
columna AUTO_INCREMENT
. En tablas
MyISAM
, la columna
AUTO_INCREMENT
puede formar parte de un índice
junto a otras columnas.
InnoDB
no admite la opción
AUTO_INCREMENT
en sentencias CREATE
TABLE
o ALTER TABLE
, la cual sirve para
establecer el valor inicial de la secuencia. Para especificar este
valor en InnoDB
, debe insertarse una fila con un
valor que sea uno menos que el deseado, y luego borrarla, o insertar
la primera fila especificando un valor determinado.
Luego de reiniciar el servidor MySQL, InnoDB
puede
reutilizar un valor antiguo para una columna
AUTO_INCREMENT
(esto es, un valor que se hubiese
asignado a una transacción finalmente cancelada).
Cuando una columna AUTO_INCREMENT
sobrepasa el
máximo valor que es capaz de almacenar, InnoDB
coloca la columna en -9223372036854775808
(si es
BIGINT
) o en 1
(si es
BIGINT UNSIGNED
). Sin embargo, como los valores
BIGINT
tienen 64 bits, hay que notar que si se
insertara un millón de filas por segundo, se demoraría cerca de
trescientos mil años en agotar los números disponibles. Con otros
tipos de columnas enteros, ocurre un error de clave duplicada. Esto es
similar al funcionamiento de MyISAM
, ya que es en
mayor medida el comportamiento general de MySQL y no pertenece a
ningún motor de almacenamiento en particular.
DELETE FROM
no regenera la tabla
sino que elimina todas sus filas, una por una.
nom_tabla
TRUNCATE
se implementa en
tbl_name
InnoDB
como DELETE FROM
y no inicializa el
contador de tbl_name
AUTO_INCREMENT
.
SHOW TABLE STATUS
no proporciona estadísticas
precisas en tablas InnoDB
, excepto para el tamaño
físico reservado por la tabla. El recuento de filas es solamente una
estimación utilizada para la optimización SQL.
En MySQL 5.0, la operación LOCK TABLES
establece
dos bloqueos en cada tabla si innodb_table_locks=1
,
que es el valor por defecto. Adicionalmente al bloqueo de tabla en la
capa MySQL, también se establece un bloqueo de tabla en
InnoDB
. En versiones antiguas de MySQL no se
establecía el bloqueo en InnoDB
, para volver a este
comportamiento debe especificarse
innodb_table_locks=0
. Si no se establece el bloqueo
InnoDB
, LOCK TABLES
se completa
aún cuando algunos registros de las tablas estén bloqueados por otras
transacciones.
Todos los bloqueos InnoDB
efectuados por una
transacción se liberan cuando la transacción se confirma o se cancela.
Por lo tanto, no tiene mucho sentido invocar LOCK
TABLES
en tablas InnoDB
cuando se está en
el modoAUTOCOMMIT=1
, porque los bloqueos
establecidos sobre una tabla InnoDB
se liberarán
inmediatamente.
Algunas veces sería útil bloquear tablas extensas en el curso de una
trasacción. Desafortunadamente, LOCK
TABLES
, en MySQL, emite implícitamente un
COMMIT
y un UNLOCK
TABLES
. Está planeada una variante para InnoDB de
LOCK TABLES
que puede ejecutarse dentro de una
transacción.
La sentencia LOAD TABLE FROM MASTER
empleada para
la replicación de servidores esclavos no funciona aún con tablas
InnoDB
. Una solución temporal es cambiar a
MyISAM
la tabla en el servidor amo (master),
efectuar la carga, y volver a cambiar la tabla en el amo (master)
a su motor original InnoDB
.
El tamaño por defecto de cada página de base de datos en
InnoDB
es de 16KB. Se puede establecer en valores
entre 8KB y 64KB recompilando el código. Se deben modificar los
valores de UNIV_PAGE_SIZE
y
UNIV_PAGE_SIZE_SHIFT
en el fichero fuente
univ.i
.
En MySQL 5.0, los disparadores (triggers) aún no son activados por modificaciones efectuadas en cascada a través de claves foráneas.
Por regla general, cuando una operación falla o se tienen sospechas de
un error, se debe inspeccionar el log de errores del servidor MySQL,
que normalmente tiene un nombre como
,
o posiblemente nombre_host
.errmysql.err
en Windows.
Durante la resolución de problemas, usualmente es mejor ejecutar el
servidor MySQL desde la línea de comandos, en lugar de utilizar
mysqld_safe o como servicio de Windows.
Ejecutándolo como se indica, se podrán ver los mensajes que
mysqld imprime en la pantalla, y hacerse una mejor
idea de lo que está sucediendo. En Windows, el servidor debe iniciarse
con la opción --console
para que la salida se
dirija a la ventana de DOS utilizada.
Pueden utilizarse los Monitores InnoDB
para ovtener
información sobre un problema. Si el problema está relacionado con el
rendimiento, o el servidor parece estar congelado, se debería utilizar
innodb_monitor
para ver información acerca del
estado interno de InnoDB
. Si el problema es con
bloqueos, debe utilizarse innodb_lock_monitor
. Si
el problema es en la creación de tablas u otra operación del
diccionario de datos, debe emplearse
innodb_table_monitor
para imprimir los contenidos
del diccionario de datos interno de InnoDB
.
Si se sospecha que una tabla está corrupta, hay que ejecutar
CHECK TABLE
sobre ella.
Un problema específico de las tablas es que el servidor MySQL mantiene
la información relativa al diccionario de datos dentro de ficheros
.frm
que guarda en los directorios de las bases de
datos, en tanto que InnoDB
también almacena la
información dentro de su propio diccionario de datos, en el interior de
los ficheros de espacio de tablas. Si se mueven los ficheros
.frm
o si el servidor sufre una caída durante una
operación de diccionario de datos, los ficheros
.frm
pueden quedar con diferencias respecto al
diccionario de datos interno de InnoDB.
Un síntoma de que ha ocurrido esto es si falla una sentencia
CREATE TABLE
. Si esto sucede, se debería observar el
registro (log) de errores del servidor. Si el registro indica que la
tabla ya existía dentro del diccionario de datos interno de
InnoDB
, se tiene una tabla que ha quedado únicamente
dentro de los ficheros de espacio de tablas de InnoDB
y que no tiene el correspondiente fichero .frm
. El
mensaje de error tiene este aspecto:
InnoDB: Error: table test/parent already exists in InnoDB internal InnoDB: data dictionary. Have you deleted the .frm file InnoDB: and not used DROP TABLE? Have you used DROP DATABASE InnoDB: for InnoDB tables in MySQL version <= 3.23.43? InnoDB: See the Restrictions section of the InnoDB manual. InnoDB: You can drop the orphaned table inside InnoDB by InnoDB: creating an InnoDB table with the same name in another InnoDB: database and moving the .frm file to the current database. InnoDB: Then MySQL thinks the table exists, and DROP TABLE will InnoDB: succeed.
Se puede eliminar la tabla que causa el conflicto siguiendo las instrucciones del mensaje de error. Esto es, crear una tabla InnoDB con el mismo nombre en otra base de datos y mover al directorio de la base de datos actual el fichero .frm resultante. MySQL asumirá que la tabla ya existe, y se podrá eliminar con DROP TABLE. creará.
Otro síntoma de un diccionario de datos desactualizado es que MySQL
emite un mensaje de error donde dice que no puede abrir un fichero
.InnoDB
:
ERROR 1016: Can't open file: 'child2.InnoDB'. (errno: 1)
En el registro de errores puede encontrarse un mensaje similar a este:
InnoDB: Cannot find table test/child2 from the internal data dictionary InnoDB: of InnoDB though the .frm file for the table exists. Maybe you InnoDB: have deleted and recreated InnoDB data files but have forgotten InnoDB: to delete the corresponding .frm files of InnoDB tables?
Esto significa que hay un fichero .frm
que no tiene
la correspondiente tabla dentro de InnoDB
. El fichero
.frm
puede ser borrado manualmente para
solucionarlo.
Si MySQL cae durante una operación ALTER
TABLE
, puede aparecer una tabla temporal huérfana dentro del
espacio de tablas InnoDB
. Empleando
innodb_table_monitor
se verá listada una tabla cuyo
nombre es #sql-...
. En MySQL 5.0, se pueden llevar
a cabo sentencias SQL sobre tablas cuyo nombre contenga el caracter
'#
' si se encierra el nombre dentro de acentos graves
(ASCII 96). De esa forma, se puede eliminar esta tabla huérfana del mismo
modo que las mencionadas anteriormente. Hay que tener en cuenta que al
copiar o renombrar un fichero en el shell de Unix, se necesitará colocar
el nombre del fichero entre comillas dobles si éste contiene un caracter
'#
'.
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.