sql >> Databasteknik >  >> RDS >> Mysql

Prestanda för frågan på indexerad boolesk kolumn vs kolumn Datetime

Här är ett MariaDB (10.0.19) benchmark med 10 miljoner rader (med sekvensplugin a> ):

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;

För att mäta tiden använder jag set profiling=1 och kör show profile efter att ha kört en fråga. Från profileringsresultatet tar jag värdet Sending data eftersom allt annat är totalt mindre än en msek.

TINYINT index:

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

Körtid:~ 738 msek

TIDSSTÄMPEL index:

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

Körtid:~ 748 msek

Indexstorlek:

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'

Resultat:

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 

Observera att även om TIMESTAMP (4 byte) är 4 gånger så lång som TYNYINT (1 byte), är indexstorleken inte ens dubbelt så stor. Men indexstorleken kan vara betydande om den inte passar in i minnet. Så när jag ändrar innodb_buffer_pool_size från 1G till 50M jag får följande siffror:

  • TINYINT:~ 960 msek
  • TIDSSTÄMPEL:~ 1500 msek

Uppdatera

För att ta upp frågan mer direkt gjorde jag några ändringar i uppgifterna:

  • Istället för TIMESTAMP använder jag DATETIME
  • Eftersom poster vanligtvis sällan raderas använder jag rand(1)<0.99 (1 % raderad) istället för rand(1)<0.5 (50 % raderade)
  • Tabellstorleken har ändrats från 10 miljoner till 1 miljoner rader.
  • SELECT COUNT(*) ändrat till SELECT *

Indexstorlek:

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

Sedan 99 % av deleted_at värdena är NULL det finns ingen signifikant skillnad i indexstorlek, även om en icke tom DATETIME kräver 8 byte (MariaDB).

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

Om båda indexen släpps körs båda frågorna på cirka 350 msek. Och släpper is_active kolumnen deleted_at is null fråga körs på 280 msek.

Observera att detta fortfarande inte är ett realistiskt scenario. Det är osannolikt att du vill välja 990 000 rader av 1 miljoner och leverera det till användaren. Du kommer förmodligen också att ha fler kolumner (kanske inklusive text) i tabellen. Men det visar att du förmodligen inte behöver is_active kolumn (om den inte lägger till ytterligare information), och att något index i bästa fall är värdelöst för att välja icke raderade poster.

Ett index kan dock vara användbart för att välja borttagna rader:

SELECT * FROM test WHERE is_active = 0;

Körs på 10 msek med index och på 170 msek utan index.

SELECT * FROM test WHERE deleted_at is not null;

Körs på 11 msek med index och på 167 msek utan index.

Släpp is_active kolumn den körs på 4 msek med index och i 150 msek utan index.

Så om detta scenario på något sätt stämmer överens med dina data skulle slutsatsen bli:Släpp is_active kolumn och skapa inte ett index på deleted_at kolumnen om du sällan väljer borttagna poster. Eller justera riktmärket efter dina behov och gör din egen slutsats.



  1. Mysql-fråga för att ta bort dubbletter av Wordpress-kommentarer?

  2. Codeigniter - flera databasanslutningar

  3. Vilken är snabbare:korrelerade underfrågor eller gå med?

  4. MySQL - autoincrement till guid