Mysql
 sql >> база данни >  >> RDS >> Mysql

Изпълнение на заявката за индексирана булева колона спрямо колона Datetime

Ето бенчмарк на MariaDB (10.0.19) с 10 милиона реда (с помощта на приставката за последователност ):

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;

За измерване на времето използвам set profiling=1 и стартирайте show profile след изпълнение на заявка. От резултата от профилирането вземам стойността на Sending data тъй като всичко останало е по-малко от една мс.

TINYINT индекс:

SELECT COUNT(*) FROM test WHERE is_active = 1;

Време на работа:~ 738 msec

TIMESTAMP индекс:

SELECT COUNT(*) FROM test WHERE  deleted_at is null;

Време на изпълнение:~ 748 msec

Размер на индекса:

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'

Резултат:

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 

Имайте предвид, че докато TIMESTAMP (4 байта) е 4 пъти по-дълъг от TYNYINT (1 байт), размерът на индекса не е дори два пъти по-голям. Но размерът на индекса може да бъде значителен, ако не се вписва в паметта. Така че, когато променя innodb_buffer_pool_size от 1G до 50M получавам следните числа:

  • TINYINT:~ 960 msec
  • TIMESTAMP:~ 1500 msec

Актуализиране

За да отговоря на въпроса по-директно, направих някои промени в данните:

  • Вместо TIMESTAMP използвам DATETIME
  • Тъй като записите обикновено се изтриват рядко, използвам rand(1)<0.99 (1% изтрит) вместо rand(1)<0.5 (50% изтрити)
  • Размерът на таблицата е променен от 10M на 1M реда.
  • SELECT COUNT(*) променен на SELECT *

Размер на индекса:

index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY    | 25739264
deleted_at | 12075008
is_active  | 11026432

От 99% от deleted_at стойностите са NULL, няма значителна разлика в размера на индекса, въпреки че непразен DATETIME изисква 8 байта (MariaDB).

SELECT * FROM test WHERE is_active = 1;      -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec

Отпадането на двата индекса и двете заявки се изпълняват за около 350 msec. И премахване на is_active колоната deleted_at is null заявката се изпълнява за 280 msec.

Имайте предвид, че това все още не е реалистичен сценарий. Малко вероятно ще искате да изберете 990K реда от 1M и да ги доставите на потребителя. Вероятно ще имате и повече колони (може би включително текст) в таблицата. Но това показва, че вероятно нямате нужда от is_active колона (ако не добавя допълнителна информация) и че всеки индекс в най-добрия случай е безполезен за избор на неизтрити записи.

Индексът обаче може да бъде полезен за избор на изтрити редове:

SELECT * FROM test WHERE is_active = 0;

Изпълнява се за 10 msec с индекс и за 170 msec без индекс.

SELECT * FROM test WHERE deleted_at is not null;

Изпълнява се за 11 msec с индекс и за 167 msec без индекс.

Отпадане на is_active колона се изпълнява за 4 мсек с индекс и за 150 мсек без индекс.

Така че, ако този сценарий по някакъв начин отговаря на вашите данни, заключението би било:Изхвърлете is_active колона и не създавайте индекс на deleted_at колона, ако рядко избирате изтрити записи. Или коригирайте референтния показател според вашите нужди и направете свое собствено заключение.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Проблем с MySQL актуализиране на поле DATETIME от ISO 8601 формат

  2. Изчислете възрастта с десетични знаци от датата на раждане

  3. Трябва ли да съхранявам цената като десетична или цяло число в Mysql?

  4. Как да конфигурирате fluent nHibernate с MySQL

  5. MySQLDataReader извлича проблем с нулева стойност в C#