sql >> Database teknologi >  >> RDS >> Mysql

Ydeevne af forespørgsel på indekseret boolesk kolonne vs Datetime kolonne

Her er et MariaDB (10.0.19) benchmark med 10 mio. rækker (ved hjælp af 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;
 

For at måle tiden bruger jeg set profiling=1 og kør show profile efter at have udført en forespørgsel. Fra profileringsresultatet tager jeg værdien af ​​Sending data da alt andet er mindre end én msek.

TINYINT indeks:

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

Kørselstid:~ 738 msek

TIMESTAMP indeks:

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

Kørselstid:~ 748 msek

Indeksstørrelse:

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

Bemærk, at mens TIMESTAMP (4 bytes) er 4 gange så lang som TYNYINT (1 byte), er indeksstørrelsen ikke engang dobbelt så stor. Men indeksstørrelsen kan være betydelig, hvis den ikke passer ind i hukommelsen. Så når jeg ændrer innodb_buffer_pool_size fra 1G til 50M jeg får følgende tal:

  • TINYINT:~ 960 msek
  • TIMESTAMP:~ 1500 msek

Opdater

For at besvare spørgsmålet mere direkte har jeg lavet nogle ændringer i dataene:

  • I stedet for TIMESTAMP bruger jeg DATETIME
  • Da poster normalt sjældent slettes, bruger jeg rand(1)<0.99 (1 % slettet) i stedet for rand(1)<0.5 (50 % slettet)
  • Tabelstørrelsen er ændret fra 10 mio. til 1 mio. rækker.
  • SELECT COUNT(*) ændret til SELECT *

Indeksstørrelse:

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

Siden 99 % af deleted_at værdier er NULL, der er ingen signifikant forskel i indeksstørrelse, selvom en ikke-tom DATETIME kræver 8 bytes (MariaDB).

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

Slet begge indekser, udføres begge forespørgsler på omkring 350 msek. Og slippe is_active kolonnen deleted_at is null forespørgslen udføres på 280 msek.

Bemærk, at dette stadig ikke er et realistisk scenarie. Du vil næppe vælge 990K rækker ud af 1M og levere det til brugeren. Du vil sandsynligvis også have flere kolonner (måske med tekst) i tabellen. Men det viser, at du sandsynligvis ikke har brug for is_active kolonne (hvis den ikke tilføjer yderligere information), og at ethvert indeks i bedste fald er ubrugeligt til at vælge ikke-slettede poster.

Et indeks kan dog være nyttigt til at vælge slettede rækker:

SELECT * FROM test WHERE is_active = 0;
 

Udføres på 10 msek med indeks og på 170 msek uden indeks.

SELECT * FROM test WHERE deleted_at is not null;
 

Udføres på 11 msek med indeks og på 167 msek uden indeks.

Sletning af is_active kolonne, den udføres i 4 msek med indeks og i 150 msek uden indeks.

Så hvis dette scenarie på en eller anden måde passer til dine data, ville konklusionen være:Slip is_active kolonne og opret ikke et indeks på deleted_at kolonne, hvis du sjældent vælger slettede poster. Eller juster benchmark til dine behov og lav din egen konklusion.



  1. Hvordan forbinder man Android med PHP og MySQL?

  2. php artisan migrate throwing [PDO-undtagelse] Kunne ikke finde driveren - bruger Laravel

  3. 12c IDENTITY kolonner

  4. Får en fejl ved at bruge mysqli_escape_string-funktionen