召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3395|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能# O8 m2 P0 t% f* p* P$ X
有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成  K5 h# Z% O/ Y/ l! S: g7 q. i
一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列( w1 ~8 \5 {' b+ _* K
在InnoDB中,只有事务提交后才会解锁& t8 w: V% S9 t8 [! n

0 z! G9 A  N7 o' j9 y  P' C索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只2 y3 B8 B' o6 F
能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。
2 X' Y5 v* _9 D3 p" o
7 W5 ~7 K/ ~3 OB-TREE: P; o5 Z( Y- a/ ?1 b1 d% [
能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找, _) B4 c9 t0 `* {+ x7 m
使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。5 h) i0 I2 n1 a5 A3 k. c& S

  1. " f9 x& w( z; I7 R2 l
  2. CREATE TABLE People(  [8 C# z4 h; E7 P9 S) q# R
  3. last_name varchar(50)   not  null  Y& E. |# d+ w
  4.           first_name  varchar(50)     not   null
    9 f/ E" r3 E  J9 B$ m  y
  5.           dob  date      not    null
    . q& M+ M: ?) A) H- t* s9 R! n
  6.       gende       enum('m','f')    not    null( z* e( p( k9 [- R3 c
  7.         key(last_name,first_name,dob)
复制代码
匹配全名4 h  S5 J+ u7 e; D  Q7 |. [; Z
全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。3 G1 B( J* c2 d
的人。
  r8 W8 L' S' F) l! d4 ^匹配最左前缀( e( q9 S9 [* h% ?7 p5 A1 M3 \
B-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。5 H$ u9 \4 @# G
匹配列前缀
) A; g# z0 F: k8 R* Z9 v可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。+ Q5 U; R# D' k( }% E0 C7 w- E
匹配范围值
% `4 o% _, H( H+ O; H: |6 f; a9 Q这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.
, A- o: `  N: K& Q: c* j  v精确匹配一部分并且匹配某个范围中的另一部分
6 a  |- @6 F  X) `+ V2 G/ W. y这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last
+ U# ?7 t$ B0 C6 B) Z9 |( N/ J/ y列并且对first name列进行了范囤查询。# R! T' ]3 ~0 e% I4 _
name
4 c; ~: m. I5 Q; P只访问索引的查询+ D! A" g& z# ]& j1 B
B-Tree索引通常能支持只访问索引的查询,它不会访问数据行。
; z- N3 i8 d, ]" R' z# `) f( b' v. T1 f) v9 l
由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,
. [- I4 D, d9 x+ w. g如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查
4 G/ ]/ ^) W* ^: A- P! t/ N; Y找方式也可以同等地应用于ORDER BY。5 R8 {. L& i$ U; ^. A) P, b6 x
2 L9 x- [" b4 K$ d9 o4 m" B
下面是B-Tree索引的一些局限:
+ b4 ^7 j' B! z6 ]# w( f, V/ q% G# h5 \8 C/ L; S
1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,2 {% S* C; Z! p  n* ?! c3 F
也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓
- M" s8 e$ a$ {& L  ^- u5 y7 \氏以特定字符结尾的人。
1 S" M* n3 R1 i. B
+ e* z& t" S0 O0 j9 t# q# L. R2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定
0 r. M6 Z% J+ A. W; k( R义first_name列的值,MySQL就只能使用索引的第一列。
, Z& W' S: j) E8 m& i0 K' y+ I/ y- t/ {  P' U/ a. [" `
3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是
' Y, r0 N/ s" Z9 x* J$ A4 l范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而, D$ ?2 F" u( e  d
不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。  b  e3 x" ]+ ^( p# H: ~
& ~' h# `6 u4 T
哈希索引,空间索引和全文索引等,暂时没有设计% b* n/ {, F- k2 Z' a8 I: E0 U
8 w% s% L  T, `
高性能索引策略- G. k9 o7 {) n+ Q6 A
1 h, [- o. \: _) c4 i) d
1,隔离列,意思就是不要对查询条件中列进行计算等操作
; e& @: H* S6 g5 }2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引" @/ C) ?, P3 X7 Z5 g! E
Select count(distinct 列) /count(*) from table;" d1 ]% }7 M$ `6 z' G
看看这个值时多少,如0.0312
" Q) M8 c6 x' d# f7 w6 }- N! k那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算+ x- l8 {& p7 f: X; B" o& O
,这对于大表很有用。
! A2 Y3 b0 i0 g! `Select  count(distinct left(列,3)) /count(*)  as  sel1,9 \8 x0 z! Z1 X8 e* V% V
count(distinct left(列,4)) /count(*)  as  sel1  ,
" U- p" M5 \2 Z) k. ] count(distinct left(列,5)) /count(*)  as  sel1,
: [0 `0 e: P, e1 ^ count(distinct left(列,6)) /count(*)  as  sel1,# O$ G  }5 I6 E! `- a
count(distinct left(列,7)) /count(*)  as  sel1   from table;8 m- D( t8 f% o! x/ `
找到接近0.0312即可。( ~/ W3 V; A* }

, D8 T! C" c4 h" iAlter table table_name add key (列(7))
  s, e8 D2 @" l$ a; k, g7 I3,覆盖索引
5 m9 d# g4 C7 U# m: F5 `4 ?: H包含或者覆盖所有满足查询的数据索引叫做覆盖索引
2 W/ j# o6 g9 {explain时,extra中的会显示using index
6 q" L( b  P. o3 n5 O( D: H2 ^这里一个重要的原则是" ?8 n. h& l# ~0 N
select后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列
! ]5 ^8 s" g" e如select id from table_name;
8 J6 w, s9 J3 g: r$ N$ R
( ]3 s5 f% O5 F很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不
4 U1 T& t) ]! U. G3 R0 S8 r一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。
! z% ^$ l5 q/ D) X0 r) Z7 ~! t( o; n) w4 h% ?
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';
    + z2 M! q' j! U
  2. Extra:using where
复制代码
该索引不能覆盖查询的原因:
: r, t& |  Q, U1,
6 `8 o8 C# K- R. f/ }4 c3 [没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。
- w& H, e' j; b1 P, v( {" j2,
) y7 p1 N% S/ H- U* ?MySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。$ w0 H  a8 [1 p
有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:
$ `* o9 t: p4 m" l: r
0 w# }! I% L+ n' _. [6 h4,为排序使用索引扫描! b6 }% [* j+ R" o
mysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。
6 ]. ?& u/ ]6 j/ ^, ?" Eexplain输出type为index,表示mysql会扫描索引! Q6 O7 R  ]. ~( p5 B6 E
" |3 [. _/ m) ~6 U6 [' U
扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.
- K$ \+ A/ T8 |& [7 l! r9 ^, s, u
MySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。
9 e" ]3 ?/ M! D
* c! I+ ?5 K) G: n' A按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。
4 r/ a( L" Y+ x9 u" @! L
, n+ }) y$ A; a# QORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。$ a( |: R" j! |: L& _1 G! l
: A! h  M% i2 S. M3 i: u, ~+ B
使用join可能情况会有不同4 c* t% ]. |( t5 _
( m5 h, F% _4 X3 m0 N
5,压缩索引(myisam)
7 r9 x1 j7 f1 K6,多余和重复索引(应该避免)
- @, U/ A/ v; p1 @, i  u  F1 Z0 t1 q: Q0 y) G: T
多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)
! [6 f/ c, L) ^7 K  e1 `上有索引,那么另外一个列(A)上的
8 f3 r3 z4 u9 e9 b7 b  w: u4 |: P* J$ g索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)' @4 o6 t2 F( ?$ g: ?
然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。# W( @! k) a9 N

7 i$ h) \( y8 B* N要点:
7 \( c% e. }7 s4 r- z3 u9 o: Q在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.
8 u3 X  [7 e3 u$ ]- K* V; `- T% R
& o2 K1 m" v8 X+ C即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行  h- e1 l* I3 Z( w. D, x" ?

3 m0 j3 `( C/ ]$ n' S7 g% w+ e# C0 T( ]! t% Y$ P& y7 P

9 j: \5 k# L+ k/ @) V
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,
' |( V' W# |4 q' f7 `% z
3 s% {# w3 m; Q" q% X拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引
9 M0 F' R- j& o7 d: j, x* r4 U/ Z4 s' |/ N! W9 t* P# L' F% o' R

4 q7 F: K) P# a
) k% R* C5 R: u% A$ b一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索( X" Y: f; h2 ^* H6 L

& M: o0 Z! z, y1 J: N- T9 R7 _引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。8 O2 X2 }% l% h1 x
# z* |) w7 J* @8 u+ L! B- [  X4 _, r

7 ^% Y% ~, s6 n1 L
' x7 M! M% ?* H0 F一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。
" U/ f. b6 t9 o. o  S; Q) i' _1 Q$ u! x" s
例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理3 I* p3 N4 a3 N% A# j0 Z5 g9 q

) p  D- b( `3 a, e/ L这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式) J3 _: C: e6 I
8 V* L; G0 a; S) k: E0 K: x$ o

1 `# G" n3 T; y0 g# }( }7 K1 m9 p8 U( {; F6 D/ k5 C7 f
避免多个范围条件,只能对其中一个使用索引
& C6 N" ~# _" O/ H1 ^9 H+ K1 [; Z; L) B0 i4 X# v  K
, w$ y7 P# Q4 y" C% N4 J) @
3 Q5 Z# q6 ?* V6 H% d
索引和表维护
4 ~+ B% z$ H- D6 e  o0 l  W5 ?
  C+ p! _8 R3 @2 D5 D# p6 S表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.4 j5 e$ C5 T  Q/ ^

) E% r' A! a) [$ Kcheck table table_name;' Z1 g+ n8 s& H3 C% P7 I( [  X+ z5 J
repair table table_name;: a) R& Z, L# m# P% C
Show index from table_name;检查索引的基数性
& m: _' f2 \0 x0 D: ]6 r$ Q
# a0 M" o  r0 S- K/ |" {" _主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量
9 G7 d$ Z/ i8 h6 ^( V- B% r, P! u# D% n9 X" Q  p' i7 ?

& ~4 a" n% P" _+ P* r0 o9 @# T
- W5 J) p6 p; t; ]3 k* F/ _B-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。: k9 \# r' P. D/ u, e
, g! Z6 w+ U6 e! ]7 m* i# a
表数据也能变成碎片化。两种类型:8 V: P( T: x1 G3 o/ v

, }  o& ]& @% v, S1,行碎片
7 v& d0 e; d( Z% U2 J
  m- }& r. P, B) v9 l  S$ o! i当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。
: C9 p3 \- _; _7 g3 s! [# g5 i6 F
+ }( t! O5 I3 R5 D! X) m4 v% v* [$ i- ?4 ^) v

) U  y6 l4 \! ~1 d! E+ ~' l( _: \/ O2,内部行碎片
5 j/ O2 }! \8 v: e6 q1 [  G) \0 z/ N, n
当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和# `- R; {# L, ^# M3 Z

4 Q/ H% l6 [! R: y$ t聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。
* n3 A& f1 s7 n
9 q- ^4 N6 [$ H" y5 @6 }
+ \& K* }. F. o' [$ R4 d% P( d& z* V2 {/ v7 y; y1 i/ d! @4 T  J; ?
为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。
  R3 L# N% O! E8 J" I6 r3 R) d0 I6 V  L' q
, l# N( D9 w9 f# {: C0 p  B; ~2 ~
/ @7 @! C% J6 E
ALTER TABLE <table> ENGINE=<engine>) D( }; m' S9 o8 R" h/ k. {: r
# W& P' k. I  \! b4 m, n/ b, ^8 d* u! J

! _. U7 b( h# o& M7 [6 k/ T7 \$ ~- ~0 ~! \- k
加速ALTER TABLE: w, w5 _; N: |% w
+ n1 D& a  I9 `, @9 [" @' ^

. g6 c& V2 |! ^/ Q- N0 e. Q8 ^! ]3 ~( Y) s" u* S
MySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需
+ \/ V/ n: z" t. h# M8 Q7 [7 t: b+ s" M# K$ W+ @3 O8 k, i* A
要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,6 |$ D! w- g- f% {( r
2 K0 \, T* p9 q: y" Q, k# e% _  O6 J: u
而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。
7 @* x$ R+ J# r  g3 v+ R  ~* G1 x
+ e/ P$ |0 `* `7 t% t8 Y传统:  g) f% E' g! @6 E9 X& \

' E6 O! U) ^* b4 B, qALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;6 o, k- d& ~' J$ v
理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更
/ k6 S+ X' g. j改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。
% T8 o$ }; {) g5 E/ ]6 b' `0 i7 P, K. p' n7 `' F
变化:" g! U+ P8 ~, v

* _9 F! u5 U: D5 yALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;8 q) q1 J, {* H3 [4 W% e8 |
这个命令更改了.frm文件并且没有改动表。它非常快。
6 |) D  {& X6 @/ H* w还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-28 15:20 , Processed in 0.043469 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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