¿Por qué InnoDB no almacena el recuento de filas?

19

Todo el mundo sabe que, en las tablas que usan InnoDB como motor, las consultas como SELECT COUNT(*) FROM mytableson muy inexactas y muy lentas, especialmente cuando la tabla se hace más grande y hay inserciones / eliminaciones de filas constantes mientras se ejecuta esa consulta.

Como lo entendí, InnoDB no almacena el recuento de filas en una variable interna, que es la razón de este problema.

Mi pregunta es: ¿por qué es así? ¿Sería tan difícil almacenar esa información? Es una información importante para saber en tantas situaciones. La única dificultad que veo si tal recuento interno se implementaría es cuando se trata de transacciones: si la transacción no se confirma, ¿cuenta las filas insertadas por ella o no?

PD: No soy un experto en bases de datos, solo soy alguien que tiene MySQL como un simple pasatiempo. Entonces, si acabo de preguntar algo estúpido, no seas excesivamente crítico: D.

Radu Murzea
fuente
66
Lento si. Inexacto, no. Es lento porque da el resultado exacto. Cuando tiene una tabla de 200 millones de filas, y posiblemente muchas otras transacciones que se insertan / eliminan en la misma tabla, posiblemente muchas filas por segundo, otra pregunta es "¿necesita el número exacto?"
ypercubeᵀᴹ
@ypercube Sé que vi algunas veces en phpmyadmin algunos valores de recuento de filas que estaban muy apagados. Además, hay un comentario que dice algo como "puede que no sea exacto".
Radu Murzea
1
@RaduMurzea phpMyAdmin usuarios un método alternativo para calcular los recuentos de tablas para tablas InnoDB por las razones de velocidad que conoce. Aquí es donde entra en juego la inexactitud que mencionaste. Las SELECT COUNT(*) FROM ...consultas reales son precisas. Si lo prefiere, phpMyAdmin se puede configurar para usar siempre recuentos de filas exactos a expensas de la velocidad. Más información: stackoverflow.com/questions/11926259/…
DOOManiac

Respuestas:

9

Estoy de acuerdo con @RemusRusanu (+1 por su respuesta)

SELECT COUNT(*) FROM mydb.mytableen InnoDB se comporta como debería hacerlo un motor de almacenamiento transaccional. Compárelo con MyISAM.

MyISAM

Si mydb.mytablees una tabla MyISAM, el lanzamiento SELECT COUNT(*) FROM mydb.mytable;es como ejecutar SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Esto desencadena una búsqueda rápida del recuento de filas en el encabezado de la tabla MyISAM.

InnoDB

Si se mydb.mytabletrata de una tabla InnoDB, obtienes un montón de cosas que suceden. Tienes MVCC en marcha, que rige lo siguiente:

  • ib_logfile0 / ib_logfile1 (Rehacer registros)
  • ibdata1
    • Deshacer registros
    • Rollbacks
    • Cambios en el diccionario de datos
  • Buffer Pool Management
  • Aislamiento de transacciones (4 tipos)
    • Lecturas repetibles
    • Leer comprometido
    • Leer no comprometido
    • Serializable

Pedirle a InnoDB un recuento de tablas requiere navegar a través de estas cosas siniestras. De hecho, uno nunca sabe si solo SELECT COUNT(*) from mydb.mytablecuenta las lecturas repetibles o si incluye las lecturas que se han confirmado y las que no se han confirmado.

Puede intentar estabilizar un poco las cosas habilitando innodb_stats_on_metadata .

De acuerdo con la documentación de MySQL en innodb_stats_on_meta_data

Cuando esta variable está habilitada (que es la predeterminada, como antes de que se creara la variable), InnoDB actualiza las estadísticas durante las declaraciones de metadatos como SHOW TABLE STATUS o SHOW INDEX, o cuando se accede a las tablas INFORMATION_SCHEMA tables TABLES o STATISTICS. (Estas actualizaciones son similares a lo que sucede para ANALYZE TABLE). Cuando está desactivado, InnoDB no actualiza las estadísticas durante estas operaciones. Deshabilitar esta variable puede mejorar la velocidad de acceso para esquemas que tienen una gran cantidad de tablas o índices. También puede mejorar la estabilidad de los planes de ejecución para consultas que involucran tablas InnoDB.

Deshabilitarlo puede o no darle un conteo más estable en términos de configurar planes EXPLAIN. Puede afectar el rendimiento de SELECT COUNT(*) from mydb.mytableuna manera buena, mala o de ninguna manera. Pruébalo y verás !!!

RolandoMySQLDBA
fuente
16

Para empezar, no existe el 'conteo actual' para almacenar en una variable. Una consulta como SELECT COUNT(*) FROM ...está sujeta al nivel de aislamiento actual y a todas las transacciones pendientes concurrentes. Dependiendo del nivel de aislamiento, la consulta puede ver o no ver filas insertadas o eliminadas por transacciones pendientes no confirmadas. La única forma de responder es contar las filas que son visibles para la transacción actual.

Tenga en cuenta que ni siquiera toqué el tema aún más espinoso de las transacciones concurrentes que comienzan o terminan durante el conteo. Sin mencionar los retrocesos ...

Remus Rusanu
fuente
1
Ok, entonces depende del nivel de aislamiento, eso tiene sentido. Pero aún se puede implementar.
Radu Murzea
@SoboLAN Hay muchas razones por las que no debería y no puede ser, la mayoría de las cuales se enumeran anteriormente. ¿Lo implementaría manteniendo una lista de conteos por tabla por inicio de transacción (cualquiera que sea el SCN de Oracle en MySQL)? La gestión de estos recuentos sería una sobrecarga masiva: piense en una base de datos con 100 o 1000 sesiones simultáneas, cada una de las cuales realiza grandes cantidades de INSERT / DELETE en la misma tabla. Imposible de mantener.
Philᵀᴹ
Implementar esto es bastante difícil. Solo piense que el recuento tiene que persistir en la base de datos, eso significa en algún lugar de los metadatos, y este recuento debe mantenerse en cada transacción que inserte o elimine una fila. ¿Cómo bloquearías esos metadatos? ¿Y cómo manejarías los retrocesos? Está lejos de ser trivial. Y el resultado sería utilizable para un subconjunto muy limitado de consultas.
Remus Rusanu
3
@JackDouglas Interesante. Por lo que he visto en el pasado, las COUNT(*)consultas rara vez se necesitan en la realidad y, por lo general, son el resultado de la inexperiencia del desarrollador (¡cuente las filas antes de seleccionarlas!) O del mal diseño de la aplicación.
Philᵀᴹ
1
@ SoboLAN: no, no lo haría. Tener un servicio que actualice algún tipo de tabla de estadísticas en intervalos de tiempo predefinidos es mucho mejor. Imagine tener una base de datos grande y varios administradores que consultan la mayoría de las tablas SELECT COUNT(*), agregue una no optimizada WHEREa la tabla y tendrá unos pocos usuarios poniendo el db de rodillas para varios contadores de estadísticas cuestionablemente útiles.
NB
0

Si bien en teoría sería posible mantener un recuento preciso del número de filas para una tabla dada con InnoDB, sería a costa de muchos bloqueos, lo que afectaría negativamente el rendimiento. También diferiría según el nivel de aislamiento.

MyISAM ya realiza el bloqueo a nivel de tabla, por lo que no hay costo adicional allí.

Raramente necesito un recuento de filas para una tabla, aunque sí uso COUNT (*) bastante. Generalmente tengo una cláusula WHERE adjunta. Usando un índice eficiente en un pequeño conjunto de resultados, encuentro que son lo suficientemente rápidos.

No estoy de acuerdo con que los recuentos sean inexactos. Los recuentos representan una instantánea de los datos, y siempre los he encontrado exactos.

En resumen, MySQL le deja a usted implementar esto para InnoDB. Puede almacenar un recuento e incrementarlo / disminuirlo después de cada consulta. Sin embargo, la solución más fácil es probablemente cambiar a MyISAM.

Marcus Adams
fuente
2
Es que no es posible mantener una cuenta exacta de la de filas de un sistema transaccional. Porque hay tantos recuentos de filas diferentes (y correctos) como transacciones activas.
a_horse_with_no_name
55
Di un -1 aquí para 'Sin embargo, la solución más fácil es probablemente cambiar a MyISAM'. Nunca recomendaría cambiar a MyISAM simplemente para obtener el recuento de filas.
Derek Downey,
@a_horse_with_no_name, por lo que acepta que habrá un recuento de filas "correcto" para cada transacción. Me parece posible.
Marcus Adams
1
@DTest, nunca dije "simplemente para obtener el recuento de filas".
Marcus Adams
@a_horse_with_no_name, eso no parece correcto. Seguramente solo estamos contando el número de filas cuando las transacciones se confirman, ¿verdad?
Pacerier