Aquí hay un punto de referencia de MariaDB (10.0.19) con 10 millones de filas (usando el complemento de secuencia ):
drop table if exists test;
CREATE TABLE `test` (
`id` MEDIUMINT UNSIGNED NOT NULL,
`is_active` TINYINT UNSIGNED NOT NULL,
`deleted_at` TIMESTAMP NULL,
PRIMARY KEY (`id`),
INDEX `is_active` (`is_active`),
INDEX `deleted_at` (`deleted_at`)
) ENGINE=InnoDB
select seq id
, rand(1)<0.5 as is_active
, case when rand(1)<0.5
then null
else '2017-03-18' - interval floor(rand(2)*1000000) second
end as deleted_at
from seq_1_to_10000000;
Para medir el tiempo uso set profiling=1
y ejecuta show profile
después de ejecutar una consulta. Del resultado del perfil tomo el valor de Sending data
ya que todo lo demás es en total menos de un mseg.
TINYINT índice:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Tiempo de ejecución:~ 738 ms
MARCA DE TIEMPO índice:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Tiempo de ejecución:~ 748 ms
Tamaño del índice:
select database_name, table_name, index_name, stat_value*@@innodb_page_size
from mysql.innodb_index_stats
where database_name = 'tmp'
and table_name = 'test'
and stat_name = 'size'
Resultado:
database_name | table_name | index_name | stat_value*@@innodb_page_size
-----------------------------------------------------------------------
tmp | test | PRIMARY | 275513344
tmp | test | deleted_at | 170639360
tmp | test | is_active | 97107968
Tenga en cuenta que mientras TIMESTAMP (4 bytes) es 4 veces más largo que TYNYINT (1 byte), el tamaño del índice no es ni siquiera el doble. Pero el tamaño del índice puede ser significativo si no cabe en la memoria. Así que cuando cambio innodb_buffer_pool_size
desde 1G
a 50M
obtengo los siguientes números:
- TINYINT:~ 960 ms
- MARCA DE TIEMPO:~ 1500 ms
Actualizar
Para abordar la pregunta de manera más directa, hice algunos cambios en los datos:
- En lugar de TIMESTAMP uso DATETIME
- Dado que las entradas rara vez se eliminan, uso
rand(1)<0.99
(1 % eliminado) en lugar derand(1)<0.5
(50% eliminado) - El tamaño de la tabla cambió de 10 millones a 1 millón de filas.
SELECT COUNT(*)
cambiado aSELECT *
Tamaño del índice:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Dado que el 99% de deleted_at
los valores son NULL, no hay una diferencia significativa en el tamaño del índice, aunque un DATETIME no vacío requiere 8 bytes (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Al eliminar ambos índices, ambas consultas se ejecutan en aproximadamente 350 mseg. Y soltando el is_active
columna el deleted_at is null
la consulta se ejecuta en 280 ms.
Tenga en cuenta que esto todavía no es un escenario realista. Es poco probable que desee seleccionar 990 000 filas de 1 millón y entregárselas al usuario. Probablemente también tendrá más columnas (quizás incluyendo texto) en la tabla. Pero muestra que probablemente no necesite el is_active
columna (si no agrega información adicional), y que cualquier índice es, en el mejor de los casos, inútil para seleccionar entradas no eliminadas.
Sin embargo, un índice puede ser útil para seleccionar filas eliminadas:
SELECT * FROM test WHERE is_active = 0;
Se ejecuta en 10 ms con índice y en 170 ms sin índice.
SELECT * FROM test WHERE deleted_at is not null;
Se ejecuta en 11 ms con índice y en 167 ms sin índice.
Soltando el is_active
columna se ejecuta en 4 mseg con índice y en 150 mseg sin índice.
Entonces, si este escenario de alguna manera se ajusta a sus datos, la conclusión sería:elimine el is_active
columna y no cree un índice en deleted_at
columna si rara vez selecciona entradas eliminadas. O ajuste el punto de referencia a sus necesidades y saque sus propias conclusiones.