回答

收藏

在索引布尔列与日期时间列上查询的性能

技术问答 技术问答 178 人阅读 | 0 人回复 | 2023-09-14

如果索引是在datetime类型列而不是boolean类型列上设置(并且查询是在该列上进行的),则查询性能是否存在显着差异?
# p3 }. I& |9 t$ {- F在我目前的设计中,我有2列:
. V0 b* k& _* [# d8 G7 xis_activeTINYINT(1),已 索引
- \: b1 O* ~! Y/ k) ^" z1 Tdeleted_at 约会时间
/ P1 x3 H9 n, Z( c: e

( Q' t7 `9 t" A7 ^查询是 SELECT * FROM table WHERE is_active = 1;
5 u. `3 M1 F1 x% n" c如果我deleted_at改为在列上创建索引并运行这样的查询,会不会更慢SELECT * FROM table WHERE deleted_at is$ C4 U8 L+ y6 B9 y/ A6 O8 a/ r
null;?, U/ Y4 |6 p/ k3 v' f0 u; H
               
1 |: ~* t% }7 M3 E4 x解决方案:1 A1 d! ?! ]; `* q+ k1 @
                7 g& a& v" T3 D; F; h6 W% t

# }% a: t. m9 D! M4 o
" b( z- a' Q9 E& t5 v) Y: |                这是一个具有1000万行的MariaDB(10.0.19)基准测试(使用sequence插件):  [' H* C# a/ |2 @. ?5 j
drop table if exists test;
) ~0 M* }: l* f6 {CREATE TABLE `test` (
  n+ N3 I; c' H    `id` MEDIUMINT UNSIGNED NOT NULL,
& }  h. n7 z  ~' o4 R  O    `is_active` TINYINT UNSIGNED NOT NULL,
- O; i' z8 H  `- ^    `deleted_at` TIMESTAMP NULL,3 e( s; c. S% ?' e' T/ W
    PRIMARY KEY (`id`),
, J+ ^6 ?6 i# V, y3 S/ H) z    INDEX `is_active` (`is_active`),
/ i% S& u+ E1 b! I    INDEX `deleted_at` (`deleted_at`)
  Q* F5 ]0 P2 |8 J+ L) ENGINE=InnoDB$ m. q8 d! y$ B7 ?7 d
    select seq id- Q% q+ [$ g1 o) i
        , rand(1)为了衡量我执行查询后使用set profiling=1和运行的时间show profile。从概要分析结果中,我可以得出的价值,Sending
& R. A! n8 \2 ^9 t! G+ q: N* z" mdata因为其他所有内容的总和都小于一毫秒。1 r! g) H% M7 X; r) V% l+ q  i
TINYINT 索引:) _8 h4 D& F% p- N; ~7 b
SELECT COUNT(*) FROM test WHERE is_active = 1;4 c3 E2 R1 k' v9 I3 t" Y! I+ }- C( R
运行时间: ?738毫秒# J: W  {) o! [$ R$ N& F3 E5 |2 Z
TIMESTAMP 索引:# J8 N8 {/ P+ s8 y) g" x* [
SELECT COUNT(*) FROM test WHERE  deleted_at is null;
9 a, M% v3 d* T2 K运行时间: ?748毫秒
8 y* ?) w0 b4 i" h索引大小:! F8 O( z0 M% s: A3 m
select database_name, table_name, index_name, stat_value*@@innodb_page_size! x. E: {+ }( E
from mysql.innodb_index_stats
$ J: ^; N2 g, R5 d2 v( |2 zwhere database_name = 'tmp': a) |, Z! ?/ r% v
  and table_name = 'test'" R3 N1 v* B; v9 @
  and stat_name = 'size'! e5 ~  F" |. Y5 E! @8 u* a- ]
结果:& ]" M$ N8 p& C3 W- F
database_name | table_name | index_name | stat_value*@@innodb_page_size
' e- ]5 j5 N1 V) ?-----------------------------------------------------------------------
7 _1 p" ~: Q( b' {) z0 l) stmp           | test       | PRIMARY    | 275513344   h6 t0 u2 S6 h5 d1 [
tmp           | test       | deleted_at | 170639360
& K3 J# L8 @$ d  }# d6 Ytmp           | test       | is_active  |  97107968$ g0 c9 C( v( C
请注意,虽然TIMESTAMP(4字节)是TYNYINT(1字节)的4倍,但索引大小甚至不是两倍。但是,如果索引大小不适合内存,则索引大小可能很大。因此,当我innodb_buffer_pool_size从更改为时1G,50M我得到以下数字:
/ u# N* R) R: R. }, b( A4 QTINYINT: ~ 960 msec
6 i/ X  \, S) PTIMESTAMP: ~ 1500 msec
/ G* q6 D7 r0 a7 A

# n7 Q. \) a4 A' L3 b; S更新
. W/ Z% s% |; \/ S: q  A) f. w为了更直接地解决这个问题,我对数据做了一些更改:
+ b- q+ v. w# F& ~: w' n5 i, b3 x我使用DATETIME代替TIMESTAMP) [- C4 {) ^0 J
由于条目通常很少被删除,因此我使用rand(1)- R/ F; |0 J& }4 \) ?
表大小从10M行更改为1M行。
& B- |$ y* l0 W. s* KSELECT COUNT(*) 变成 SELECT *) a$ B; I8 I) P2 \; v# A
) j( m8 N3 j- a8 `% l, Q
索引大小:  h0 [* Q' U9 l) Q$ D
index_name | stat_value*@@innodb_page_size  Q$ h0 a8 t* I9 h" V
------------------------------------------
  y- \2 i; S/ ]% IPRIMARY    | 25739264
' w) b% x% ]' h( ]9 gdeleted_at | 12075008( h, m' r. p6 w: r5 N% M6 r
is_active  | 11026432
9 k. T- N% G) Q8 n) Fdeleted_at尽管非空的DATETIME需要8个字节(MariaDB),但由于99%的值为NULL,因此索引大小没有显着差异。
' I8 s* c) C; Q4 `$ OSELECT * FROM test WHERE is_active = 1;      -- 782 msec
' W. _* b7 O9 P+ b/ X( `SELECT * FROM test WHERE deleted_at is null; -- 829 msec
; s) p2 ?- j3 U7 @* B. @- a: f删除两个索引后,两个查询将在大约350毫秒内执行。并删除查询将在280毫秒内执行的is_active列deleted_at is null。$ z: J7 _4 l) E1 A- [5 @. M
请注意,这仍然不是现实的情况。您不太可能希望从1M中选择990K行并将其交付给用户。表中可能还会有更多列(可能包括文本)。但是它表明,您可能不需要该is_active列(如果它不添加其他信息),并且在最好的情况下,任何索引对于选择未删除的条目都是无用的。
8 W# v( Z% w) G3 L! d' f但是,索引对于选择已删除的行可能很有用:
: l0 G; z/ d2 `* n6 aSELECT * FROM test WHERE is_active = 0;
5 l; S. I$ @, D& p+ K有索引的执行时间为10毫秒,无索引的执行时间为170毫秒。
5 p! v# f8 w( }( U9 I5 M9 o- tSELECT * FROM test WHERE deleted_at is not null;
2 J  x) e5 C) l" ]. A有索引的执行时间为11毫秒,无索引的执行时间为167毫秒。2 r4 w+ Z1 ]- b, p
删除该is_active列时,它在有索引的情况下以4毫秒执行,在没有索引的情况下以150毫秒执行。& R! }0 \# D9 d: p9 s% A. v
因此,如果这种情况能以某种方式适合您的数据,那么结论将是:如果您很少选择已删除的条目,则删除该is_active列,并且不要在该列上创建索引deleted_at。或根据您的需求调整基准并做出自己的结论。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则