召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3143|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能
* Y; e: u9 r2 _. @& A8 m# n; G有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成
" d/ }( \9 @' [3 p& ^, B一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列; u' r# E/ r1 V2 H6 C. F
在InnoDB中,只有事务提交后才会解锁
1 [. U0 A  {' I' `" |
# S$ T1 k3 D. C' E2 Z+ i4 V5 {索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只! u# a  v& J* ~7 E
能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。2 D5 ~: K& i' o
4 v% ?, i# M* F+ S
B-TREE4 T1 a& S" I7 X0 e5 o7 Z% @4 f. X9 t
能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找# t4 n8 Z( G! V' {7 T% i. N
使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。
& y' K& \" T, m9 e
  1. ! h% E3 W6 \# l# V8 r  Z# A
  2. CREATE TABLE People(1 w; M$ f  E8 u2 ~
  3. last_name varchar(50)   not  null1 k) Q; j. V# B  c
  4.           first_name  varchar(50)     not   null1 T7 Q3 v( x/ s4 F
  5.           dob  date      not    null8 q8 g& n2 t8 z7 h/ ?6 _  J. m
  6.       gende       enum('m','f')    not    null' c2 ^' A2 r8 G3 k% T
  7.         key(last_name,first_name,dob)
复制代码
匹配全名+ z& j4 B  C3 p( C7 T2 ~1 v2 I
全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。
7 f; x* C- I/ z2 d2 H的人。1 \! ], D6 H/ }  ~  a8 {3 R
匹配最左前缀6 m9 h# E8 L# I$ c# J
B-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。! l- G: B& O) f8 b8 s$ O% y
匹配列前缀
& d8 n  `( i; Q; n2 q可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。" E+ n& a9 ~- |! K6 d
匹配范围值* _# p* ]( }2 W9 A* E* `
这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.
& K* o( L. j, E: G6 g0 ?' A) s精确匹配一部分并且匹配某个范围中的另一部分: ~& @8 o" [1 T2 w4 d- w
这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last
/ c% M1 N4 w/ |4 j0 c列并且对first name列进行了范囤查询。9 ]  Z) ^0 U" T4 [6 ?) v# `3 A
name6 U0 ?3 Z7 m/ H; ?1 h
只访问索引的查询
; v' Y! u' w, J7 kB-Tree索引通常能支持只访问索引的查询,它不会访问数据行。
- G# u( {1 k6 u! l: x7 @: R
' @' S8 [  D/ m9 d% k, j7 J7 ~由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,& c2 M% P- B# k) e* x
如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查
& r$ a& t1 z; k. y, K6 E0 O6 ?0 i' C+ r找方式也可以同等地应用于ORDER BY。* ?% K; X  v$ p9 j* G
5 i- m4 F! [3 l
下面是B-Tree索引的一些局限:
3 v5 L+ |9 |4 p) R% M
: M! q9 c/ B! G: c9 j1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,& Y( w. {, C% Z/ D9 Y
也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓
& {; ~; o5 C& n氏以特定字符结尾的人。, _! J3 f# D/ `, E: g( X

" ?/ y* _* z; E7 _2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定
* D4 k' _( P. O4 z& I( N义first_name列的值,MySQL就只能使用索引的第一列。
! c, D. h4 D* A1 i0 m/ @/ a7 e% U$ m9 E
3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是
) s! `4 D: W7 a& R/ U1 V" h1 H" w范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而
/ ]2 T1 k  o; S4 U' i/ i. K0 C4 k9 W不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。
' E6 z* c# s3 b! z, h0 L% E$ v* @' r, Q5 l9 N# i
哈希索引,空间索引和全文索引等,暂时没有设计
( a0 D% C$ t9 M8 Y) Z8 K% R
( x* {1 t' b8 f& o! N高性能索引策略
/ ~' N7 a* K4 U" p% u- a
, `/ t' x6 E) c2 j4 i1,隔离列,意思就是不要对查询条件中列进行计算等操作
; B* O, S! s0 ]' ]2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引4 L" p, D) L" n: ~! D2 b
Select count(distinct 列) /count(*) from table;
3 z2 {& C( q5 h& {" R$ @看看这个值时多少,如0.0312
/ a( @+ K: o0 c! G0 A那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算- t! X# @5 P- q' s& D/ {
,这对于大表很有用。. R, e2 H: V2 Y7 G% G9 I7 J, U4 D
Select  count(distinct left(列,3)) /count(*)  as  sel1,- K( G, t  o& M
count(distinct left(列,4)) /count(*)  as  sel1  ,% K1 W. y6 V- G0 w; _) w
count(distinct left(列,5)) /count(*)  as  sel1,0 X3 i' y6 B% _$ I! M) }  J
count(distinct left(列,6)) /count(*)  as  sel1,2 O  ]8 G6 _: d7 Y% P/ L
count(distinct left(列,7)) /count(*)  as  sel1   from table;/ |7 p8 {, B  S3 E2 w
找到接近0.0312即可。
: F* i' ^# P$ U0 g7 \6 s: s9 O! R- x/ m  @5 r) x
Alter table table_name add key (列(7))
0 S% v! Q8 X  I5 ?3,覆盖索引  B  C( j  y% ^2 r$ G
包含或者覆盖所有满足查询的数据索引叫做覆盖索引
) Y$ p- X: X9 Q9 `explain时,extra中的会显示using index& U  }3 W: S6 V, q9 K* [
这里一个重要的原则是
  V' y; K8 l( W0 m0 e9 iselect后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列0 F+ Z+ @. i; M- i" }
如select id from table_name;/ j9 T; ^/ D, g! l  E
* u# h  B  {! T' p
很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不
; y" ?. u. m( V一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。
6 |4 _6 D; g8 m2 b" X/ R9 I) N
# Z7 k3 C2 N3 c/ |5 F7 \% t
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';3 I4 C3 V; B* r: V# \' f. e
  2. Extra:using where
复制代码
该索引不能覆盖查询的原因:1 Y# h6 j- v5 C4 k! T; l! u% t
1,
2 v) W' P# w! j" ?1 C$ d没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。+ L- O) G- Q, m7 F  D
2,
. K$ {# M6 |" C# aMySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。
0 ^1 @* ?/ P# A% x; `( ]有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:
# g6 k; s: ?! U6 g, t7 n7 s' |& S$ G6 P( U
4,为排序使用索引扫描
! b; M: e5 z+ N  Pmysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。) k  ^( a% P9 A/ I) L6 o1 p- @5 B% G
explain输出type为index,表示mysql会扫描索引
: r# j. f( f- m8 j# f! D- ?3 V+ @, p) z5 L5 D$ o% W, q$ P( a
扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.
6 I. V9 W3 Y+ ^2 G3 G0 l! C- c2 h; X" f1 G# J7 D" D
MySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。: f% o: J; k) `

9 i+ q, N2 C- y( c, {1 D6 g7 }按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。
; `$ \2 u2 v& t9 y; Q' _# U% z1 t( O6 \! Y0 ?2 V$ }, a
ORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。+ \+ o; y1 ~" Y5 u  c9 w1 ]) D7 \

5 |3 x* |1 I+ ]# o使用join可能情况会有不同
! h: U7 ?: i+ u0 R% D8 `
1 J: m0 X" R# T7 c; ^5,压缩索引(myisam)8 j, ?: X* r, s
6,多余和重复索引(应该避免)
4 \( W. o0 T! A
% Y; n! V" a1 L: q! j7 x( S多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)) j' {4 ?: G9 l, d+ U6 D
上有索引,那么另外一个列(A)上的
. F2 w# V3 `& Q索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)
& G. l- ^7 }, C7 G: Y然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。) z. |2 R( @  Q  d

3 ?; X( n4 w$ \1 O0 _. i" A! V要点:
5 W3 H& R; T/ Z) {4 I5 O2 k# s在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.
8 L1 M& d9 c7 w3 Z; g6 N9 e6 ?" z3 s9 E$ z
即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行
8 E( d5 o  O9 @- ~+ e
9 y/ m1 O, U/ k
7 w1 n2 ?$ y8 }  @8 |; n! \
* b; D1 p. h! b$ b2 x5 o8 R
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,! P1 G/ @( {6 T0 a
/ t% \" |3 ?8 a( G  c9 r9 \
拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引
: r. u# a9 _) C' K  r4 J0 l5 q' d+ T) V. i: y& f

  I0 o2 b5 T) I' G7 T* f! E+ L* h9 L& m) R& i( {% O" K& ]) J( s0 Z
一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索
' J, y: n$ `  [$ }( K$ y! h) W
2 e* f6 `6 B& f: R引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。/ b) t4 z' w3 f. A+ g
) i; K4 `* h' e. e

. n  t4 O, @0 e% `/ u
! Q  X4 A$ x/ o! v" o8 L一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。
4 M* C9 U1 ~0 e% w& C5 b
& ~! ?- ~- q; t2 W, {4 o" B6 l例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理
' U! E/ C8 c0 K) O( M2 e9 X! N' ^/ A# F
这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式
5 T( U; E1 D" X6 `
9 f2 n# ^$ D0 f: o: o
  ^. K2 w2 u6 Y8 b3 @2 l9 R5 k3 N5 d. H' Z
避免多个范围条件,只能对其中一个使用索引
; f+ D8 p5 f7 i$ ~8 F
+ }' p- T7 |# p. _. c4 k# g( Z
& R$ c1 ]' u8 ~9 `7 k7 @( t$ P
  Y* {. t3 i# I' P: P索引和表维护$ G1 w' v! I  l! g2 Q$ |$ r

. `5 A. g- _2 O表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.6 \" ?) S0 B3 e2 c9 Z

& U* P$ P! r; K, h6 ~, S) B. \8 Rcheck table table_name;
8 Y* M2 M% E$ o  @# Srepair table table_name;
8 d2 g" G" O" s( p0 V6 Z) F! jShow index from table_name;检查索引的基数性
! l6 R& Y' R0 B$ F3 j$ M! V' ^& K1 L: S* D( j8 I/ ]0 y: b; \
主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量
7 U1 D9 Y) L, t8 l5 t7 i8 ]2 D9 \7 _5 h+ m4 F! _5 \* `3 g7 s
9 N/ G* P  d$ e; m/ |

9 e3 {+ W- X5 {  LB-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。
0 a5 m/ k# u5 ]& v+ _+ ~, O: n' i6 c
表数据也能变成碎片化。两种类型:% L# @! D- X1 b$ [( t, ?

7 [7 H9 I7 m3 @: h. }5 m: i1,行碎片! ?! I' E8 H$ ^6 v5 H
9 n% T6 A% H% p1 x7 D: p
当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。
: ^; e6 g( V, T' X% v( Q" P9 E
" J6 ]. @- g( f# v# U
1 J& x2 l) l& C2 |( s+ @5 Z1 K- n4 e% P  j! w2 H* |
2,内部行碎片
; ~9 ^7 G' d5 I1 J/ E
7 I* t# S! g: Y4 K当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和  Y9 E8 Q- M, q
2 Y" f6 W$ Q/ o% F
聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。6 @' E. p* R( u% R1 I$ J
3 ~. P; g# Q3 E8 e! H! D
( Y& q2 z# Z/ A/ q# p, @0 p

1 r* f' \, V7 z4 o( T5 Z为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。$ g9 m5 J* P. s' L7 e+ H! Y2 M

- B! o, d8 i2 p* j0 J( e6 s. d9 r
) h4 V  ?6 }. A: O& `/ l* ?" ~
& }: l$ Y5 f/ p3 `1 t. XALTER TABLE <table> ENGINE=<engine>
9 x5 d4 B) j! j9 s2 ]9 K8 t
3 B1 u" C7 W& ^; C
. x& i; n' m2 U# P  j. _! x
3 ~  x  g+ L3 X# l/ v加速ALTER TABLE% `; v" J' I& y8 k4 G
' I! e- b: N3 [; B0 U8 f
" z5 k$ T" n+ I' @- l- P/ ~  W) \
8 z# R' `  ?0 @+ P' d
MySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需3 [4 Y& j" U/ A( m7 g& t
, [- P5 n" {0 D" ^
要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,
* r- {4 O7 l% X1 B! @: z( S1 `
2 h; u( `# K$ u# c) _5 @而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。6 Q$ j5 g: U4 w' r7 ?8 x! c1 D
4 g4 @! B4 u+ S( ?& {; d* t
传统:4 M  a5 S" B8 v8 E# J' e& z: ~( O

: r4 R" i  y) a; r5 wALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;4 e' o2 l! X: u/ s1 B, `- r" n
理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更4 y8 N7 i8 O0 _5 M: L' u6 p; K9 i3 {
改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。4 h4 V7 v1 v1 S2 |6 z2 q5 t) Q
1 p! {% G# i( E* p" U; C: }/ _: j
变化:
0 S! x- E( A8 a. t$ B7 P% p
0 C, j  z( `+ s, ZALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;
% T" X: c5 `/ j6 f这个命令更改了.frm文件并且没有改动表。它非常快。; r# \6 j1 I, L; Q8 ^
还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|召隆企博汇 ( 粤ICP备14061395号 )

GMT+8, 2026-7-23 10:37 , Processed in 0.048547 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表