召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3265|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(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
  1. 3 ~# i! n5 q* j4 Z! A
  2. CREATE TABLE People(. j# m7 B2 h& }& V$ b* L  ?, o
  3. last_name varchar(50)   not  null
    $ Z( w8 ?6 S& A
  4.           first_name  varchar(50)     not   null
    % b+ n1 }$ r  e6 L% @! ]
  5.           dob  date      not    null
    / G! k. V# d2 a: k& w
  6.       gende       enum('m','f')    not    null
    " I" k2 M2 K9 m% ?
  7.         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
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';
    # t5 S3 V- j! G0 J! Y: s! L2 A
  2. 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  ^
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,
3 A5 L/ E; u1 n% z  H9 m( J$ E- b6 s: ^$ C& M- \1 _
拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引9 y3 q5 e5 q2 W$ E/ [. l2 G

  u! P1 V- M7 T4 X/ I( H* z5 D! \2 L. D! N. |: ], f+ @8 X. C
3 \* x( N% E( x! k
一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索4 _2 ?# B, o  p" G5 v

) X' _0 n& ^; s引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。4 a- `, q' \, W: b0 e

8 e  L' }- _" t3 V1 @& T0 ]5 k8 R1 b+ P* D' H9 g- |
; _  y4 @0 {1 F) D; y: m5 c5 x
一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。
3 }7 A1 J  h3 t8 |  u( H
2 x; n' I6 t5 O) G4 v6 L/ X例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理8 S, r3 F: f% x9 `
0 X8 _4 D0 Y0 `( `$ [* z0 \+ o
这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式  V, ~9 g7 m( ^

4 H, X. a7 c& I( K) k( R3 ]% s% H5 W( N

; |( w. b8 Y, I& @# M8 U. _避免多个范围条件,只能对其中一个使用索引  N' x7 {. o7 ^& Q+ i

, Z1 g' H5 s0 h: L& ^6 V2 g) ^) V( }* @
- @- E- A8 x& n8 }/ E/ s  p7 m$ c
索引和表维护1 Y$ Y' E  c8 V9 n
8 @: j; B9 [8 w6 u% h1 ^
表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.
# P0 U; `5 R2 M  ], c. A" {) j7 D- D# w; x. ^! r
check table table_name;
0 Y0 ?4 u6 G1 l# q9 irepair table table_name;
) m' h6 v% d3 i& h8 oShow index from table_name;检查索引的基数性
* g+ n  P) S5 f6 ?( H5 A; B; P7 E6 o/ Q* E" }, ^
主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量
1 L- K2 I8 i  {; |1 l
( e' W7 |3 v7 v9 s" b! B" c+ X! `" D2 w8 y; Q' u: P1 D
/ ~( P! h' j/ f: ]7 p! U0 z" f6 b
B-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。
6 r) n: R: x3 Z: r
! w; s+ {/ `, _! O表数据也能变成碎片化。两种类型:
) g) r3 @( R0 o5 G$ n$ ~, W
2 X! q8 _) T5 J) q1,行碎片* ^% \) g% l  p  C

( S$ G% V  z' k/ x. e当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。
: C+ G% i6 `, ?0 L$ D8 O$ `1 X" P! c0 Y3 q7 _6 W

  g2 e8 U$ l* X4 |8 V# |7 i4 E$ l) h
2,内部行碎片
* r. }1 t; o, Z0 @/ U7 Q
1 s6 [7 Z( B4 ^# `/ g当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和
8 E$ n9 d8 M0 m: I$ \4 G
& J4 L4 T5 D4 m聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。
% h- H# Y: L: `6 |0 e# M  m, J/ q% |! a6 r
. w# B. Q+ P4 w/ E: U8 Z7 n: C
0 E& ?) ^1 q2 G7 T
为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。# A# V0 d- d/ D' A6 D
1 `6 h6 M- X) ?' ~0 H. \
  q1 D% ]+ @, r" b/ i! N

8 j1 b- Q& ^' L" u7 I0 Y& Z+ AALTER TABLE <table> ENGINE=<engine>6 i- Z/ o2 U# w* ?

4 _+ Y% j5 G! J1 h/ g( [6 S- V; k& r# E1 q9 w2 K) U% F

' e; g7 {  e. Y' O1 A# f. U加速ALTER TABLE
5 E; L$ j0 ]# M! j8 k+ @( s7 e) y, v8 e7 r

4 H9 ^% m5 h9 s! s$ @. x) l8 G7 b9 K* N* E# U: Z+ n
MySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需
- i( D2 t% O. Z7 e
" ~4 W$ w5 F/ n要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,
2 y$ Z8 ?. r9 P- V( J. R
" Y! |' j9 i* s6 t而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。$ D' w! |( X; t# N3 x$ W1 x
. k# k. Y: p- Y" t6 G3 x% ~: F& E
传统:
4 {/ z5 a" d1 K  c
. P/ h: Y4 x: G4 [& MALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;3 h  e; W* e4 X( B5 U9 s. q! n
理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更" B5 S; w* X* y/ K& M; j  s
改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。$ `7 ]0 Z! x: z' J8 I3 ~
6 ]' T" P# h0 t
变化:% x8 {5 a: c( H/ O/ g! e/ n

' d! v  b7 N' DALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;$ }( m8 B% e, A- b1 o* P
这个命令更改了.frm文件并且没有改动表。它非常快。
- _: }4 x4 U0 L0 k9 {还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-9-6 23:23 , Processed in 0.038815 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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