|
|
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能" v- y* n1 V% B9 e0 Q; l
有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成( ?3 I$ O+ b) Z5 y5 n/ b
一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列9 g; T3 L) d. i: a i
在InnoDB中,只有事务提交后才会解锁8 V7 u! R7 b# \
2 y" P% n% z: o* m/ L
索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只' [- H; ?7 \) Y3 V
能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。
1 o8 q ~3 z4 G2 }' |& i. B( F, d* ~1 W: g) \' ~3 U' j
B-TREE& v1 X% A. v( s2 U4 Q0 F* \" r
能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找1 h5 N5 l( a! ?) U* @$ t( [
使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。4 R: ]" J# d4 {7 Z2 d
- 3 ~# i! n5 q* j4 Z! A
- CREATE TABLE People(. j# m7 B2 h& }& V$ b* L ?, o
- last_name varchar(50) not null
$ Z( w8 ?6 S& A - first_name varchar(50) not null
% b+ n1 }$ r e6 L% @! ] - dob date not null
/ G! k. V# d2 a: k& w - gende enum('m','f') not null
" I" k2 M2 K9 m% ? - key(last_name,first_name,dob)
复制代码 匹配全名) J# o9 c2 ]4 k; c
全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。
- u8 n* @" _; ]5 j0 U) W的人。
" `$ }/ Y( q0 D! z匹配最左前缀7 c. H2 F" f% q2 ~! s! M% J
B-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。
7 b! n( l# n* J# ?/ _匹配列前缀
3 Y5 z3 i& c& }+ i: ^& ]可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。
; U H: z1 u: Q4 K3 X匹配范围值 X1 ?$ ~ _# h; q2 K. b2 A
这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.
[5 Z; x1 M, M& `0 c' ]5 w- X精确匹配一部分并且匹配某个范围中的另一部分 ?- P3 r! X- y- ?" E$ u& |
这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last8 x/ Z5 S4 I; Y: h J$ X
列并且对first name列进行了范囤查询。
; I! ]' `6 t3 }, o- C s. Uname
% h% Q8 y" ] n1 @% T只访问索引的查询/ `; m7 H# ?# ?; J) g
B-Tree索引通常能支持只访问索引的查询,它不会访问数据行。
& N3 I' j/ \5 u' e
@: F( M$ ]$ J" o由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,3 w- j9 Q- G1 B$ n) K! h
如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查/ H; \( g5 s+ C. y4 H: Q6 i
找方式也可以同等地应用于ORDER BY。, y1 `+ X7 Y: o
" }* H% _2 `7 z/ `1 Y c
下面是B-Tree索引的一些局限:
3 O% X" O( w: u4 H4 T8 r3 z
+ Y0 G8 F W: H6 b1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,
% Q4 n6 G& y& a7 X7 n. ]: P' ?6 b也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓
3 T% Z) ]* T' i( A; k6 K9 G0 o氏以特定字符结尾的人。
; g3 p2 L! }0 A- H8 Q1 m- U6 S4 ]8 P
2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定& {/ T) ~* l6 s# b" z* d; Z$ D$ C
义first_name列的值,MySQL就只能使用索引的第一列。; k! H7 H* O3 d0 S' P
1 d( ^8 U- K2 n5 E1 d5 Y4 v3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是! {6 B- A( S* j9 H/ B5 @
范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而# d8 v+ N) A) a( X8 V3 Q' u1 O
不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。; ^! q C s9 m! Z- q
5 h8 c! v* B/ p# _2 q9 z哈希索引,空间索引和全文索引等,暂时没有设计; q+ f* k3 n! P
. w5 q6 ?) M) ]8 s$ ~高性能索引策略6 e6 H' Q! J5 R# X( c
5 q. r% ^5 [+ a! x: p% [1,隔离列,意思就是不要对查询条件中列进行计算等操作
$ [9 Y+ `& r( H5 \2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引* {/ v8 o3 H4 S0 s+ S( r9 R" P
Select count(distinct 列) /count(*) from table;
3 }- n6 U8 ]4 v! S- Q4 L! p看看这个值时多少,如0.0312% q; ]6 k- F3 i3 [6 F
那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算
7 C8 V( D; Q8 ^: G, H,这对于大表很有用。
6 h) K; f' t8 p+ m$ R: v# L# U* ISelect count(distinct left(列,3)) /count(*) as sel1,
; k7 c0 A+ s! l* d# C) ^; L" p: l count(distinct left(列,4)) /count(*) as sel1 ,
# D7 D4 D# z) X2 Q, {. \4 u. S/ i count(distinct left(列,5)) /count(*) as sel1,
4 c K7 c- H, t" @5 M: M# \! d$ { count(distinct left(列,6)) /count(*) as sel1,
0 \+ Q7 L; \9 _% D( K3 C count(distinct left(列,7)) /count(*) as sel1 from table;2 x2 z4 y# I* g+ V
找到接近0.0312即可。
2 x# P, _* l% d' H- [, ]) ^; x$ U3 F) z, e# f$ A- e5 Y6 \7 ~1 f
Alter table table_name add key (列(7))
* K8 C5 G* J9 U3,覆盖索引
" p7 u5 n! Y5 k包含或者覆盖所有满足查询的数据索引叫做覆盖索引4 [ D& y, }9 @! `5 D2 z
explain时,extra中的会显示using index
" b5 s1 V1 y' l1 u这里一个重要的原则是
6 o- h8 X7 X: \+ K; `select后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列
4 L9 S! O R8 R+ @如select id from table_name;; W) m4 \; Q1 {. q8 e# d4 X
5 }4 l% Q+ g5 X# ]8 q很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不
, _7 G- j7 }3 k/ C一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。
5 g9 G+ R" W& N+ H& {7 }
1 D$ k: y" d% @0 y- Explain Select * from table_name where col ='nam' and col1 like '%name%';
# t5 S3 V- j! G0 J! Y: s! L2 A - Extra:using where
复制代码 该索引不能覆盖查询的原因:# d4 C# V$ Z! }& T! m& X) e
1,. t. o/ W: }7 T5 [! T0 _ X9 ?
没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。
, H0 p4 \9 U i! }2,% H- ~# P* C m
MySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。
' }4 u. J9 ]# ~7 w8 _# q有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:! u7 k, N; e- r, B8 f" Z
/ p& ^" p9 f" x0 r) ?+ g4,为排序使用索引扫描( Y9 z0 \; S. q1 T$ _8 @
mysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。
: V4 G3 E8 X. m/ Y" Dexplain输出type为index,表示mysql会扫描索引/ a& \; U3 P, a# N! Z% L8 N
5 k1 v( l6 s- ~% F9 y: l2 H
扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.
% {; s4 C G) k6 H+ X0 H
9 G: M) y7 i% p) x' q) |MySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。
) w1 L9 C; v2 T' J; P
' k. w5 N: x' X( t9 u( n' o按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。- |" T, Q( B# Z, ]% j1 [
2 s3 X8 Z, D& }/ V7 @ORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。8 R! @0 k+ N7 Z+ ~. ]3 G
) ]8 k5 y; P- K, m9 U# X
使用join可能情况会有不同
. f4 M# u, j, P# H% U# v7 `# s+ }# i4 P$ f, a+ f
5,压缩索引(myisam)( f% B" P# J: ^9 J
6,多余和重复索引(应该避免)
, h+ n( Z8 B- B. E4 @0 r- q
9 a0 M+ J$ j0 U; p7 o4 a6 Y; j" {多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)
& W% E3 W9 s" r! r1 u上有索引,那么另外一个列(A)上的
1 b* j- z$ S. {( L, Q! Y" t7 d索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)
% i9 V1 x0 B" U. x6 i; n$ o然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。
$ r& ?0 F+ O- G& c
5 u; K# p- q( z/ p3 M* L& h# \要点:1 ~1 t6 \$ \# j- q% e
在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.
@1 v$ [) ?/ A, e$ N+ K7 u f Q' ]. F9 X
即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行
9 C. p. w5 Q3 W' R3 ~' n% A) m( R0 b, B- @/ _
) [% r( r3 y9 I$ g6 e, E% h- d. I9 D3 V! X* n( f ^
|
|