Estamos ejecutando MySQL 5.1 en Windows Server 2008 R2.
Últimamente hemos estado haciendo algunos diagnósticos en nuestra base de datos y hemos encontrado algunos artefactos perturbadores que no podemos explicar . Agregamos algo de código para iniciar sesión cuando tuvimos consultas que tomaron mucho tiempo (> 2000 ms). Los resultados fueron sorprendentes (y posiblemente una explicación para nuestros puntos muertos).
Ocasionalmente, las consultas, que normalmente toman muy poco tiempo (<10 ms), demoran de 4 a 13 segundos. Para ser claros, estas son consultas que se ejecutan constantemente (varias veces por segundo) y no sufren estos picos de tiempo de consulta.
Hemos revisado nuestros índices en busca de errores obvios y no hemos tenido mucha suerte.
Actualizar
La mesa de la gente:
| people | CREATE TABLE `people` (
`people_id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`temp_password` varchar(10) DEFAULT NULL,
`reset_password_hash` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(32) DEFAULT NULL,
`mobile` varchar(32) DEFAULT NULL,
`iphone_device_id` varchar(160) DEFAULT NULL,
`iphone_device_time` datetime DEFAULT NULL,
`last_checkin` datetime DEFAULT NULL,
`location_lat` double DEFAULT NULL,
`location_long` double DEFAULT NULL,
`gps_strength` smallint(6) DEFAULT NULL,
`picture_blob_id` bigint(20) DEFAULT NULL,
`authority` int(11) NOT NULL DEFAULT '0',
`active` tinyint(1) NOT NULL DEFAULT '1',
`date_created` datetime NOT NULL,
`last_login` datetime NOT NULL,
`panic_mode` tinyint(1) NOT NULL DEFAULT '0',
`battery_level` double DEFAULT NULL,
`battery_state` varchar(32) DEFAULT NULL,
PRIMARY KEY (`people_id`),
KEY `email` (`email`),
KEY `company_id` (`company_id`),
KEY `iphone_device_id` (`iphone_device_id`),
KEY `picture_blob_id` (`picture_blob_id`),
CONSTRAINT `people_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `companies` (`company_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `people_ibfk_2` FOREIGN KEY (`picture_blob_id`) REFERENCES `blobs` (`blob_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4658 DEFAULT CHARSET=utf8 |
Índices:
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| people | 0 | PRIMARY | 1 | people_id | A | 3502 | NULL | NULL | | BTREE | |
| people | 1 | email | 1 | email | A | 3502 | NULL | NULL | YES | BTREE | |
| people | 1 | company_id | 1 | company_id | A | 3502 | NULL | NULL | | BTREE | |
| people | 1 | iphone_device_id | 1 | iphone_device_id | A | 3502 | NULL | NULL | YES | BTREE | |
| people | 1 | picture_blob_id | 1 | picture_blob_id | A | 3502 | NULL | NULL | YES | BTREE | |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
Tenemos ~ 5000 filas en la tabla en el servidor que nos está dando problemas.
fuente
Respuestas:
Las consultas de ACTUALIZACIÓN en sus dos preguntas anteriores ( Pregunta1 , Pregunta2 ) están golpeando la tabla 'personas' por PRIMARY KEY con bloqueo de nivel de fila. Esto es lo que dije en la Pregunta 1 el 6 de junio de 2011 10:03 a.m.
Todas las transacciones atraviesan la clave PRIMARIA. Dado que PRIMARY es un índice agrupado en InnoDB, la clave PRIMARY y la fila en sí están juntas. Por lo tanto, atravesar una fila y la CLAVE PRIMARIA son lo mismo. Por lo tanto, cualquier bloqueo de índice en PRIMARY KEY también es un bloqueo de nivel de fila.
Todavía no se ha considerado otra cosa que pueda atribuir lentitud a los índices: el uso de índices NO ÚNICOS en InnoDB. Cada búsqueda indexada en InnoDB que utiliza índices no únicos también tiene el ID de fila de cada fila asociada a la clave no única. El rowID básicamente emana del índice agrupado . La actualización de índices no únicos DEBE SIEMPRE interactuar con el índice agrupado INCLUSO SI LA TABLA NO TIENE UNA CLAVE PRIMARIA.
Otra cosa a tener en cuenta es el proceso de gestión de nodos BTREE en un índice. A veces, requiere la división de la página de los nodos. Todas las entradas en el nodo BTREE de índices no únicos contienen campos no únicos MÁS el ID de fila dentro del índice agrupado. Para mitigar adecuadamente la división de tales páginas BTREE sin alterar la integridad de los datos, la fila asociada con el ID de fila debe experimentar un bloqueo de nivel de fila internamente.
Si la tabla de 'personas' tiene muchos índices no únicos, prepárese para tener una gran cantidad de páginas de índice en el espacio de tablas, así como tener pequeños bloqueos de filas que se le acerquen de vez en cuando.
Hay otro factor que no es tan obvio: la población clave
A veces, cuando se llena un índice, los valores clave que componen los índices pueden desviarse con el tiempo y hacer que MySQL Query Optimizer cambie de búsquedas con clave a escaneos de índice y finalmente a escaneos de tabla completa. Eso no puede controlarlo a menos que rediseñe la tabla con nuevos índices para compensar la desigualdad de las teclas. Proporcione la estructura de la tabla para la tabla 'personas', el recuento de la tabla 'personas' y la salida de los índices de la tabla 'personas' .
Incluso si las consultas usan solo la CLAVE PRIMARIA, la desigualdad de las claves en los índices no únicos todavía necesita el equilibrio BTREE y la división de la página para que ocurra. Tal gestión de BTREE producirá una notable desaceleración debido a bloqueos intermitentes de nivel de fila que no tenía la intención de suceder.
ACTUALIZACIÓN 2011-06-14 22:19
Consultas de la pregunta 1
Imagen de la secuencia en eventos
Consultas de la pregunta 2
Estas dos consultas son aún más confusas porque la primera consulta está actualizando todo excepto people_id 666. Cientos de filas se están bloqueando dolorosamente con solo la primera consulta. La segunda consulta está actualizando people_id 666 ejecutando la secuencia de 5 eventos. La primera consulta ejecuta esas mismas 5 secuencias de eventos en cada fila involucrada, excepto people_id 666, pero el índice para iphone_device_id está en un curso de intersección con dos consultas diferentes. Alguien tiene que bloquear las páginas de BTREE por orden de llegada.
Frente a estos dos pares de consultas en un curso de colisión para posiblemente bloquear las mismas páginas BTREE dentro de un índice, puede ser una experiencia desgarradora para InnoDB o cualquier RDBMS compatible con ACID. Por lo tanto, una ralentización del índice es el destino de estos pares de consultas a menos que pueda garantizar que las consultas se ejecuten con AUTOCOMMIT = 1 o permitiendo lecturas sucias (aunque colisiones como estas hacen que READ-COMPROMISO y READ-NO COMPROMISO sea una pesadilla para MVCC).
ACTUALIZACIÓN 2011-06-15 10:29
@RedBlueThing: en las consultas de la pregunta 2, la primera consulta es una consulta de rango, por lo que se están logrando muchos bloqueos de fila. Observe también que ambas consultas están tratando de bloquear el mismo espacio id 0 página no 4611 n bits 152 se está bloqueando en la PRIMARY KEY, también conocido como índice agrupado.
Para asegurarse de que su aplicación, como mínimo, se ejecute según la serie de eventos que espera, hay dos opciones diferentes que puede probar:
Opción 1) Convertir esta tabla a MyISAM (al menos en un servidor de desarrollo). Cada ACTUALIZACIÓN, INSERTAR y ELIMINAR impondrá un bloqueo de tabla completo por orden de llegada.
Opción 2) Intente usar el nivel de aislamiento SERIALIZABLE . Eso bloqueará todas las filas previstas en el modo COMPARTIDO.
La secuencia de eventos que espera se interrumpirá o tendrá éxito utilizando estas dos opciones alternativas. Si ambas opciones fallan, deberá revisar su aplicación y priorizar el orden de ejecución de sus consultas. Una vez que establezca esa prioridad, simplemente puede deshacer estas opciones (para la opción 1, regrese a InnoDB, para la opción 2, regrese al nivel de aislamiento predeterminado [deje de usar SERIALIZABLE]).
fuente
MOSTRAR VARIABLES COMO 'innodb%'; - En particular, si los datos e índices simplemente no han alcanzado el tamaño del grupo de búferes, podría estar golpeando el disco mucho más fuerte que antes. I / O es el gran asesino de rendimiento.
La mayoría de sus campos son dos veces más grandes de lo necesario. BIGINT (8 bytes) es una exageración para la mayoría de los identificadores. 5000 filas solo necesitan un SMALLINT UNSIGNED (límite de 65K, solo 2 bytes). O use MEDIUMINT por un margen de seguridad.
DOBLE le da 16 dígitos significativos a un costo de 8 bytes. ¿Battery_level tiene más de 2 dígitos significativos de precisión? FLOAT toma 4 bytes.
Mi punto aquí es que "más pequeño -> más almacenable en caché -> más rápido".
Por favor, muéstranos las consultas lentas; al menos algunos de los que de repente se han vuelto más lentos. Solo podemos hacer conjeturas sin ellos. Active el registro lento y establezca long_query_time = 1; Esto ayudará a encontrar las consultas más lentas.
¿Entiende el beneficio de los índices "compuestos"?
fuente