召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2908|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(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
  1. ) R" ^: ~$ n# N$ p: j
  2. CREATE TABLE People(
    9 e  p1 {' n+ Z- |1 e8 G
  3. last_name varchar(50)   not  null9 d' {% y" [) i" U  ]
  4.           first_name  varchar(50)     not   null; R3 G# w7 d0 j8 ]/ {
  5.           dob  date      not    null
    5 W4 S3 }% ?# o/ J0 S
  6.       gende       enum('m','f')    not    null
    & s" x3 S' K, r  I. v
  7.         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
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';
    ! r4 i# i7 K3 I2 T% g" ~3 ~
  2. 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
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,& m3 I- e. o" v5 _
: ~  t* f$ W! l" y: V# ^  `2 z
拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引# h. W' ?  G$ f' D# {

/ `- J' r4 E; d  ^0 X" t8 y" J+ {9 i, U% z
9 N9 b% l7 }0 S1 w! C
一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索
) y2 x9 {* {- L' h/ {3 V( K& K6 @8 e/ ~) ]8 x
引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。; e( i* B- Z- m3 M% t2 s
8 w; t$ x- l# m2 O
; p6 C8 j3 L. P, A' s* w
2 M1 z- S# c4 N$ Y9 ]
一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。- z7 ]: {" W/ r2 g0 l( }4 t

$ M/ T* ^4 v! U: j% n4 |6 b$ c例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理. \" ^9 s  e- X

' ~# V) X, T8 R) K3 M1 J( [这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式
4 e2 p+ }2 o# F, F) o/ Q
- w; x7 M, X9 D6 o' c6 O6 O' X+ `* j4 J% ]
/ b$ f2 R' z% B& \
避免多个范围条件,只能对其中一个使用索引
3 a/ L. }+ L% z7 Y
  [- m4 x7 l$ B" `" v1 ^! K+ }% m& ?
1 _2 x, {0 w# a5 u) c& p! g7 T! i* e1 `; K" b
索引和表维护: @) t( R! v( b6 {& b" s7 F

2 D0 ?! o" s! _8 k# i表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.
0 B% r1 P5 S0 F! t, \
2 N& `$ N) T2 s  p5 ?+ }check table table_name;8 R" C/ @; O/ f: m# a  O5 h& [3 F/ ~7 s* L
repair table table_name;, \" Y8 F% e1 P  F5 o8 V
Show index from table_name;检查索引的基数性
& @2 v$ `; A5 A$ R2 c! o7 ~3 U( R0 O9 T8 [5 v* p: N% }0 \
主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量
! l: O' ]3 x2 ?9 ~" `
7 p, R) @  R1 p" b8 G& b5 K4 I" b: B" V
7 z% v6 @/ X% P5 k! V- \6 T+ B+ j& K$ t& \8 W( d; [7 Q; D! F
B-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。
% e% \1 J0 @9 k8 }' n0 k. c# X6 m/ p8 O4 q8 j! W8 P9 e
表数据也能变成碎片化。两种类型:
+ z* U5 R, i+ [$ a, n# {5 a) J9 F4 u3 r! C. y9 s
1,行碎片
- h( e; q7 M8 L. j
: }- d) k) C" Q% }, W当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。
* _- n: x- }) c& V) ^6 X8 M. E' ~
! k) O' c7 ^2 B5 S) f- f
# o+ W% S( l: o; a6 O
2,内部行碎片
, J- e1 ^& B9 X  C8 b3 R- A, y/ L3 u1 h+ W: T0 w
当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和" O/ v" p+ x- ?5 s$ Q

/ N, d1 n5 f: k' N/ x聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。
# f6 t& {3 ]5 L% J/ L3 l2 L4 _1 X! \" I4 f5 H2 F/ r

( m2 P9 a  `: P$ Q9 e& h2 T; }5 d# V) X( X& T  _
为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。( T8 K6 a5 W5 v1 _+ i' e5 ^

+ O$ E  r4 E# m2 L& z) S1 N5 l# N7 ]

7 n% X3 m# u/ s+ f, q- I6 [5 I9 vALTER TABLE <table> ENGINE=<engine>
# u$ v! ]0 J- m$ |: g1 c& a8 D8 j, U  w, Z4 [1 p2 x

3 ^% D7 w# }% c4 B' h# B* T# u$ z
7 N" \: H: j% b3 I加速ALTER TABLE$ W( O" u9 u( h/ c- Q) D" V

& d4 L: D# l9 M2 @; _- A- o' j$ m& T" E6 A( W5 D

  M- B' s! O. S* C& P, oMySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需( w2 s9 Z% n6 K8 {3 c
- m0 i9 w( q' E/ L9 P7 h. W$ M2 U
要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,
. W' V4 l9 k# b7 A0 C9 B/ W7 G- v7 Q
而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。
0 ~! Z! ^$ X1 \1 }  Q( o3 f$ X" }- J0 ]
传统:( D" z8 ^: {. p! k

' w) L# j) X3 bALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;. z- u+ |3 `& k- L* O+ C. R0 }- Z
理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更- i; X' A) T9 }+ f3 i; g9 T
改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。
- J! e- i( m: N* O# b
3 i8 Q/ u3 M" O4 G. \1 E变化:
. `7 O+ v* n+ r1 F6 P% q2 h; Q2 Y$ h  a
ALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;7 ^( F/ W6 L( f
这个命令更改了.frm文件并且没有改动表。它非常快。% {7 L, Z! n) ~" Z3 C) W- K+ E
还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-29 05:59 , Processed in 0.039967 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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