|
|
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能/ i$ W! h' k7 h" W) B8 r
有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成, Q3 v5 d; Y1 K! D4 w; v
一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列
' B" ^, Y+ d( N; R在InnoDB中,只有事务提交后才会解锁$ Z6 z3 Q, S+ T& k
( k! x* W/ D3 `9 O, p
索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只
; f( V3 u& ~5 {5 E& W能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。# e+ v/ G4 T2 L4 o/ W% P
/ T9 D" ]" `3 `9 i4 i
B-TREE: e( K$ O# v# a# {
能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找
8 V2 ?# l3 r+ N' a使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。: S* u/ k. Y% u: ^% x
- ) R" ^: ~$ n# N$ p: j
- CREATE TABLE People(
9 e p1 {' n+ Z- |1 e8 G - last_name varchar(50) not null9 d' {% y" [) i" U ]
- first_name varchar(50) not null; R3 G# w7 d0 j8 ]/ {
- dob date not null
5 W4 S3 }% ?# o/ J0 S - gende enum('m','f') not null
& s" x3 S' K, r I. v - key(last_name,first_name,dob)
复制代码 匹配全名
( I' x* s6 G6 D/ n$ W全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。
2 r- j" O* }/ _$ w的人。
" V4 k5 }" _& \" n( B2 _+ F匹配最左前缀
& m) h) d5 B+ a/ \B-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。5 J% y+ g h/ n7 m5 C
匹配列前缀: F( O( V+ e" ^5 A# D# S
可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。
6 e5 H( `* B5 L# W5 G匹配范围值
& B7 t/ p3 y! _! |3 ~ q: L这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.# `: e! a3 d7 \9 z+ A! I, q/ e# u
精确匹配一部分并且匹配某个范围中的另一部分" q" J2 ^' Y- R, }6 p
这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last3 Z1 X0 R0 s! J* b
列并且对first name列进行了范囤查询。
3 }' z+ |0 J% Z3 E- M7 hname
; y5 s9 c1 D5 ?2 u E+ c只访问索引的查询
% Y7 c& ]8 @( ^+ f1 d5 Y0 R( s1 nB-Tree索引通常能支持只访问索引的查询,它不会访问数据行。
+ L. u' s- t: T2 ]7 G, \! v) N& ]# v
由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,. L' A8 H4 b' [% `# j
如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查- x; L+ l! p% @7 `' [3 S1 o# F' @
找方式也可以同等地应用于ORDER BY。
- y: y* ?' K: O3 z% E% Z$ R0 D; _# y% s$ V" D
下面是B-Tree索引的一些局限:
. |4 H+ b+ Q( @1 \ p: p" p8 K8 C0 C K: `5 Z$ g- y5 f7 A. d% ~
1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,
3 i. I, I6 Y& M- F9 L也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓$ ~6 s+ ^) d R/ X; l
氏以特定字符结尾的人。
. C o4 _8 A. n+ n( e$ x# y$ O4 E5 V# i5 X# U1 \6 W: c
2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定
, o4 ]# M. S! f7 |/ t7 R w义first_name列的值,MySQL就只能使用索引的第一列。0 |0 F% K+ m3 {" h( f( G+ I) r4 a
" u9 Z$ ` U6 X/ P8 F) l# u3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是0 p' w3 p( A/ U4 T, `% k) r
范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而
8 U$ @' q, D' D# @不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。: P, K/ J4 C& Y& Q
3 W" G1 \2 Q! {% Y
哈希索引,空间索引和全文索引等,暂时没有设计: B& ^% L: `6 G7 p# v
2 M; o5 i& |9 o
高性能索引策略6 G) i0 m+ y: J6 ]
7 K5 R1 Q+ J8 x9 q6 A1,隔离列,意思就是不要对查询条件中列进行计算等操作7 S. n' ?+ ~2 d: }
2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引1 g+ ?7 G8 o! ]- E( t
Select count(distinct 列) /count(*) from table;
s+ R7 t; z+ c. m% X" Y看看这个值时多少,如0.0312
* R0 g+ g, Y0 S6 p) r$ U那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算
$ l% {( N7 v( G3 Y% h,这对于大表很有用。
1 t R5 K" C+ ~/ }Select count(distinct left(列,3)) /count(*) as sel1,
' |3 U# {: m% {& g, m, w0 z count(distinct left(列,4)) /count(*) as sel1 ,# s S5 e/ @) ?, n3 w
count(distinct left(列,5)) /count(*) as sel1,
! o/ }/ F0 e( i2 z8 X# n% p% o* _ count(distinct left(列,6)) /count(*) as sel1,
! o% \9 S+ t" h. z% V count(distinct left(列,7)) /count(*) as sel1 from table;
2 q/ \! Y0 H+ z. h+ E找到接近0.0312即可。
$ E$ Z7 \# h) O: u, S1 E4 I" m: a! C2 c* J6 R; d! J& _; r. r
Alter table table_name add key (列(7))
, w2 P9 g( a, A u0 I3,覆盖索引* y9 @- e; _ Y: h7 [
包含或者覆盖所有满足查询的数据索引叫做覆盖索引
! [7 C0 L' K1 I2 kexplain时,extra中的会显示using index
. Y8 n6 h4 l3 @! T0 |这里一个重要的原则是
5 `, i y$ j, U6 C# P( z$ H$ I5 \select后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列$ ?, `- v. x5 z# a3 \
如select id from table_name;
( k. |( G# G% z4 D% y3 k
% L" @" l: ^1 B8 n" s很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不3 R, g1 D1 I. l$ A' z
一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。9 `$ R1 Y7 l* @: g
: ~) }8 h: d3 V8 L2 v( k; z G
- Explain Select * from table_name where col ='nam' and col1 like '%name%';
! r4 i# i7 K3 I2 T% g" ~3 ~ - Extra:using where
复制代码 该索引不能覆盖查询的原因:
. U) u6 s! _0 H& y D1,, H$ y4 @( _) C; R0 J# N; W) A
没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。
$ U# O/ X/ D, h2 }6 M( Q$ C2,$ K$ n0 R. l! g* I/ g. x/ l) ]
MySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。) J7 A, E! Y! b' B5 S3 k, j8 p
有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:* G' o/ B2 i" J; [
' a, t' }" @- s, u. a
4,为排序使用索引扫描+ n* w/ M5 [% z: \5 E
mysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。: P* l( F, L5 C J6 n
explain输出type为index,表示mysql会扫描索引
* B* z- c4 ]/ G! a0 `/ `( G4 d5 |) I8 N3 [+ l8 J) @; Y
扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.
. X: P; m) a" C- o
+ J2 ~7 D: v8 Q. j& |: NMySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。
( O5 O7 M/ p" B! H2 U
3 k# d) s- P* m2 N9 o! W按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。
# Z, R: B W" @7 ?' ?9 C
, v8 I5 a. q( o& a( u& mORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。
; S! o/ Q. d) G8 h% s5 G& b# s m+ Z0 {( d7 ?- l1 u: i1 b; W
使用join可能情况会有不同
5 m. o0 l( H0 ?+ h! P4 p
% N& m( \ H; Q y9 q5,压缩索引(myisam)$ o1 {; A+ z1 J. x) S' w6 H# s
6,多余和重复索引(应该避免)
% `# D, G; V* {/ G, k; t5 t( ?. z7 {0 S; \6 L+ T( f$ M9 g+ i
多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)" P! u' ]8 R# t! y" u
上有索引,那么另外一个列(A)上的" q& c B, V' }, a
索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)9 k$ T2 S+ X2 l1 l4 @& n: K. i
然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。
4 V* S/ W9 d) ]# y0 e( b
\. X( d; W$ i8 c要点:
, ], C/ _9 R" z. j/ ~在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.% X2 S( T$ r$ M1 Z9 c. X% K. v
: |( w) p E c. Z6 h: P1 C; b
即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行" D3 y1 n! `3 R. l- ?! d2 X$ k; X" n
- q3 |! |; e- R' B3 Z$ |! A
# o. n8 w2 f, M0 g7 s
4 A1 Z- C. v4 Q, ~+ f" I+ k
|
|