Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Wydajność zapytania na zaindeksowanej kolumnie Boolean vs kolumnie Datetime

Oto test porównawczy MariaDB (10.0.19) z 10 milionami wierszy (przy użyciu wtyczki sekwencji ):

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;

Aby zmierzyć czas, używam set profiling=1 i uruchom show profile po wykonaniu zapytania. Z wyniku profilowania biorę wartość Sending data ponieważ wszystko inne trwa krócej niż jeden milisekund.

MAŁE indeks:

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

Czas pracy:~ 738 ms

ZNACZNIK CZASOWY indeks:

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

Czas pracy:~ 748 ms

Rozmiar indeksu:

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'

Wynik:

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 

Zauważ, że chociaż TIMESTAMP (4 bajty) jest 4 razy dłuższy niż TYNYINT (1 bajt), rozmiar indeksu nie jest nawet dwa razy większy. Ale rozmiar indeksu może być znaczący, jeśli nie mieści się w pamięci. Więc kiedy zmieniam innodb_buffer_pool_size z 1G do 50M otrzymuję następujące numery:

  • MAŁYINT:~ 960 ms
  • ZNACZNIK CZASOWY:~ 1500 ms

Aktualizacja

Aby odpowiedzieć na pytanie bardziej bezpośrednio, wprowadziłem pewne zmiany w danych:

  • Zamiast TIMESTAMP używam DATETIME
  • Ponieważ wpisy są zwykle rzadko usuwane, używam rand(1)<0.99 (1% usunięty) zamiast rand(1)<0.5 (50% usunięte)
  • Zmieniono rozmiar tabeli z 10 mln na 1 mln wierszy.
  • SELECT COUNT(*) zmieniono na SELECT *

Rozmiar indeksu:

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

Od 99% deleted_at wartości są NULL, nie ma znaczącej różnicy w rozmiarze indeksu, chociaż niepusta DATETIME wymaga 8 bajtów (MariaDB).

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

Usunięcie obu indeksów, oba zapytania wykonują w około 350 ms. I upuszczając is_active kolumna deleted_at is null zapytanie jest wykonywane w ciągu 280 ms.

Zauważ, że to wciąż nie jest realistyczny scenariusz. Prawdopodobnie nie będziesz chciał wybrać 990 tys. wierszy z 1 mln i dostarczyć je użytkownikowi. Prawdopodobnie będziesz mieć również więcej kolumn (może z tekstem) w tabeli. Ale pokazuje, że prawdopodobnie nie potrzebujesz is_active kolumna (jeśli nie dodaje dodatkowych informacji) i że każdy indeks jest w najlepszym przypadku bezużyteczny do wybierania nieusuniętych wpisów.

Jednak indeks może być przydatny do zaznaczania usuniętych wierszy:

SELECT * FROM test WHERE is_active = 0;

Wykonuje w ciągu 10 ms z indeksem i w ciągu 170 ms bez indeksu.

SELECT * FROM test WHERE deleted_at is not null;

Wykonuje w 11 ms z indeksem i w 167 ms bez indeksu.

Upuszczenie is_active kolumna jest wykonywana w 4 ms z indeksem i w 150 ms bez indeksu.

Więc jeśli ten scenariusz w jakiś sposób pasuje do twoich danych, wniosek będzie następujący:Usuń is_active kolumna i nie twórz indeksu na deleted_at kolumna, jeśli rzadko wybierasz usunięte wpisy. Lub dostosuj benchmark do swoich potrzeb i wyciągnij własne wnioski.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. lokalizacja katalogu danych mysql

  2. uruchom plik .sql za pomocą wiersza poleceń

  3. Najlepsza praktyka / standard przechowywania adresu w bazie danych SQL

  4. Nie znaleziono kolumny doktryny:1054 Nieznana kolumna „s.features” na „liście pól”

  5. mysql:odmowa dostępu na schemacie_informacyjnym