召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3317|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能9 f: m0 o6 z# W
有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成
  `" t/ {% V* ^/ s. R一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列
0 T" E* ~- w& P( l! w: _/ g' u在InnoDB中,只有事务提交后才会解锁! c8 }$ d7 t7 w" Y- i. N

- W* ^1 |1 r6 y索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只6 W' `4 V. C% Z% X7 y2 p$ q
能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。+ Y, k! H3 D, ~( B5 R

4 I1 @" ]& M% {1 T) B1 WB-TREE
. A3 d6 Q6 C0 P' W4 _能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找
3 n4 l( W1 ^9 ]9 F* V% K- _使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。  E" n* |9 e! `5 M- }8 l

  1. ' U( x7 c1 @# c9 ^! _2 ~" e
  2. CREATE TABLE People(# N5 x8 }* W2 g( C& R% F$ s
  3. last_name varchar(50)   not  null, O% C0 }& n) o- |( i' @$ A# z& f
  4.           first_name  varchar(50)     not   null7 h9 z- e$ ^3 J, p7 @
  5.           dob  date      not    null8 q8 k" Q# o6 V# N, R! b' X$ j% F
  6.       gende       enum('m','f')    not    null2 A' P5 \4 C: r- ~
  7.         key(last_name,first_name,dob)
复制代码
匹配全名
: t2 M* b1 |. `. D0 f+ }8 Z% Q全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。
4 O9 N: I; n" x+ S" S% ?的人。
4 |8 i+ h& q/ d+ B; S匹配最左前缀
! h" `6 G5 B5 l7 D0 `# e6 AB-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。
$ x1 S+ t4 {- {4 f) U5 \匹配列前缀. [9 c# ^4 q% I/ Y# p& k
可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。1 Z/ C, y8 O" r
匹配范围值7 i5 R: @8 C6 j
这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.
! F3 B. W/ n  C3 _( ]精确匹配一部分并且匹配某个范围中的另一部分
) g, K, n2 z6 O& f# q& L这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last& n$ X9 g! ^6 G7 v- l+ t  S
列并且对first name列进行了范囤查询。$ Q  K- k5 i1 X; b: B' Z, q2 r, Y
name
, w  }/ K/ a* |: W, y只访问索引的查询( E$ w' _% n  h2 N7 R' r
B-Tree索引通常能支持只访问索引的查询,它不会访问数据行。2 D& @- ~: `2 U: l: s; G) t. v

) z" }+ r4 b: S+ X" d由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,
" r$ o; J% `" W如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查! r% H" }8 @; _' k3 k; I
找方式也可以同等地应用于ORDER BY。9 e  @% T! S. }. o- {" i
0 R# i1 ^( T  g' R
下面是B-Tree索引的一些局限:
+ m( m4 c& M6 R, v* I  u( Q( D3 c/ k0 H; H2 |
1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,3 Z, l1 h  C0 R: X( d8 w
也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓
+ i* A' @' j7 h7 K/ Q4 H氏以特定字符结尾的人。
, b8 G# A$ b/ I5 i2 \( v) T9 d: M9 R8 C, F
2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定6 ~7 O2 ?. }2 J% {/ a" {- o
义first_name列的值,MySQL就只能使用索引的第一列。8 z# S, O/ `# F; J+ ~) ]
+ n( B1 F( C2 d/ N) r
3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是
9 L- x8 S/ B( W7 d范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而$ \& O& Y; v+ H: J
不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。2 J3 Y+ Q; p- s/ Q8 T. m  e$ [

0 P% @1 K4 q! l3 a/ {哈希索引,空间索引和全文索引等,暂时没有设计7 S; P! ^5 [( A

  n% e( _/ F5 O1 n- R高性能索引策略: U5 O/ I7 t6 K1 K
! z& Z" d5 Y! e1 r9 A- |7 h9 c
1,隔离列,意思就是不要对查询条件中列进行计算等操作/ b5 E- Y5 R4 y. c
2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引
5 v0 d" F/ z9 G' u% \  USelect count(distinct 列) /count(*) from table;
# A, N' B' I3 R6 X3 v  {看看这个值时多少,如0.0312
" ^$ Y9 r+ h" \, m. ?2 o- y7 {那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算
* t% [' `* i9 N; Z1 ~,这对于大表很有用。
! @, e3 R2 m% M/ K- dSelect  count(distinct left(列,3)) /count(*)  as  sel1,
! Q$ E4 I$ w1 ^/ ~, B& [! L8 L count(distinct left(列,4)) /count(*)  as  sel1  ,  q# l+ a( H$ }' i; p* S
count(distinct left(列,5)) /count(*)  as  sel1,; A( Z+ b2 q# l5 B$ a
count(distinct left(列,6)) /count(*)  as  sel1,
, m! e% h4 Z% X count(distinct left(列,7)) /count(*)  as  sel1   from table;3 j4 y% y3 t0 G) p0 y
找到接近0.0312即可。
$ }  T$ ?4 {/ |
, W. F# j8 e# b* x0 X* ~  JAlter table table_name add key (列(7))
& h8 l! Z6 W  D) n3,覆盖索引
$ D# W% p. ]5 H- d: v% j$ p包含或者覆盖所有满足查询的数据索引叫做覆盖索引- R1 f% L6 R. c
explain时,extra中的会显示using index+ a8 V. f' \+ j4 m7 S5 s+ ]
这里一个重要的原则是
5 Q2 K4 `$ s/ b( Oselect后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列* M9 i2 M; F# K9 c2 i' m
如select id from table_name;
! [& T2 A$ \$ s$ h
8 I0 i0 H7 R8 H8 D2 m很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不
! k+ N( _5 d8 v  F一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。# U+ h3 ]& V3 _: u$ {7 Q. Y: g1 ~
) l  f# J- D0 X4 }+ v: Y+ M
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';
    1 ?* ~9 u' ]$ b
  2. Extra:using where
复制代码
该索引不能覆盖查询的原因:# G2 d8 x+ p9 S/ I$ s' M
1,
5 t4 Y' ?, F' }0 Z' q没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。
3 h4 S6 n! \; u* O$ _! o/ m* C2,
. X' f! W+ i. q! K& o4 DMySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。
! d% I  {  F( R" i; @5 A有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:3 J4 |8 z8 J3 w2 N3 j  y1 k
& s" x9 W2 k0 G
4,为排序使用索引扫描+ \8 ]4 p# w+ j& D" B' N6 a
mysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。2 @- v7 }4 F+ n+ g: w) j5 J! d
explain输出type为index,表示mysql会扫描索引
5 p' b9 F1 J; {. V8 c. G" A0 N% z' l
9 J4 D& F. e1 X$ S扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.
+ \  T# ~5 D0 Q+ m9 J
& ~9 L% M3 d- M+ {' xMySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。
) g) m* \* \: X7 O+ K7 d6 z
# _: e) r6 X. _9 p按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。
9 k6 }+ j, c$ a# m3 l, J9 c* N, U% r# R
ORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。
5 X  S5 Z6 Y# ~3 C
8 h5 g* T8 u2 I3 d- r- a3 U使用join可能情况会有不同' _' }4 S$ t7 [$ U; ^) ^0 q

3 l: l9 {  o8 b/ k' `2 M: D5,压缩索引(myisam)
; Q% A8 F! M. X6,多余和重复索引(应该避免)+ v( [% ~7 U. @1 G) Z( ~, _
# n( R. x" G( T6 H
多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)/ T4 M. z4 P4 l7 E
上有索引,那么另外一个列(A)上的
$ k$ N( ~" ?: R2 D索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)
- ]7 u) z9 j# Z/ m然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。. s4 u3 n* s+ M+ V6 _
  _8 e9 _5 G' }6 |% P) @" @
要点:
0 \) o, X, F% s" o6 n5 a在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.
" _6 J0 N$ h' h1 _7 u4 O0 G7 F0 g/ K% c3 i2 Y$ R0 _
即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行
$ I- Q, }% [5 s, A# O1 l3 I) R* F4 H  ?, u4 L
; V  N. S" i! Q) a# V4 U5 T4 E- r' V
. m/ Y7 m2 |1 z3 u4 |
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,. c, n9 T8 }' j: e( s* o3 c% U9 j/ j
$ N6 @% o6 W8 u+ S9 u/ g" n
拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引# q% Y# N% D8 p7 C$ _
! m3 A2 w0 H# T" p! y+ H2 S
8 e# m  C4 n, ~1 b( ^$ G- R' [$ a' z

& W& v8 A! s1 V4 j9 S* V一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索
, A# \6 Z" D2 G! H) E) }9 o' `, G- I. A/ r
引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。$ c7 \, ~; {3 c1 B; N

! f5 O2 V& }! v, Y) [9 X" Y( E, {# |9 a5 @

! j/ \7 ?1 l- V! r8 r一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。# r2 N3 d& P& F; p$ e* y
# L# z1 T+ K& T
例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理# i; y1 S6 o  }" I1 ~4 I" h

  m. J* A. k: j- L7 a8 x这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式  p9 [1 B$ N" t1 a

; c9 G* h/ y: z3 X3 T0 o& O; B1 f# c
) Q) H5 Y+ b3 F/ N9 W% m, O
避免多个范围条件,只能对其中一个使用索引
6 a# q0 N4 Q" u; s" \4 @5 G+ m  p( [5 y. v% E

: n6 N% V, `, p. J( f& w+ d; C; n% @7 s4 F
索引和表维护: j3 h- f1 L& k4 J
: Q0 R6 q+ l6 _7 L' G4 f0 v9 s
表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.
1 C' s+ t; q- [$ e3 e8 B7 q
: V& o9 _. \4 Ocheck table table_name;
* k+ `) p. i; B6 j8 J+ o9 zrepair table table_name;/ o9 J) a' |+ ], x
Show index from table_name;检查索引的基数性
6 L3 A6 A" ]4 T4 n
4 ?7 Z3 B7 `% u3 O4 ^4 A8 C主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量
  D8 u% r, }% f6 J8 _: |1 D; b2 B
6 ^$ r# C0 _# {% F  A$ o( i+ c; K
& V, h& p+ O( y. h7 F! f+ t
& O; K2 `7 n0 W; C3 |* q. s2 x7 }B-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。7 @: |( z0 A" a. ]8 R1 p( }0 C
+ S0 D& }# v' n% D; i
表数据也能变成碎片化。两种类型:2 k$ g- l6 K% W; G% G+ W
) h  F6 @3 r/ B" I; U
1,行碎片- {- X5 `1 r" b+ k& {, [: Z9 x
0 \+ F0 y, K% D. m
当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。/ G' w. H5 a0 q: Q

; G/ ~7 H- ?0 F6 k8 A, c9 N8 [6 d% [- d8 j, A
1 Y1 K+ j' n. ~: p/ S. U' Y
2,内部行碎片" L( l2 \- z1 E$ c$ g9 }# _& K1 `
! p" R( x' W3 ?$ o# k8 k
当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和
  E0 C, l" C5 U1 V' N
1 ~7 U. v' F; m& B聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。% y3 g. L+ \! x& K, ]7 V

& X  O- z$ D) z6 `0 f! G- ?& b7 w
4 U, b: P" q3 Q* t8 ?0 P
, C3 U! t3 q; _& B为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。
$ y% V  q" Q* c; u# u+ K8 [: k; R* b6 Z  v" {7 X6 N, D

. X" T7 s* C6 B3 X
0 l$ c% |4 d4 U  K4 G9 U- g( _5 FALTER TABLE <table> ENGINE=<engine>; n0 d; x: I; r' ?* }3 C

3 l: r0 R+ J* v$ C4 v7 O$ V) u8 f+ w6 g2 {6 l+ h

2 z% N8 M4 P: r9 ?3 Z& [: b( `加速ALTER TABLE! O+ G: k# B* O6 ~3 \- ~
( u! B# v4 H% u0 w0 Q
8 p8 L% M/ N& d$ h3 A! a1 \
: z1 ]/ `( u. |0 h7 E
MySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需/ P8 _0 S) F; F" I8 k9 l

" C6 x1 p% f3 q- S% x要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,' G, K  W6 b( K. w
$ G/ u1 \7 ]! T7 `; q3 n0 o, K* d* ^
而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。/ r0 L; h& s  [" ~, p2 ?+ ?  G2 ]

8 F5 v- G& V/ r4 Y传统:* Y6 i! B3 d3 x8 _! d# v6 H* r! h

* ^) |) R' g4 m$ F, W) kALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;
. J" p. H7 G  U2 s8 ^  w理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更
% D6 l/ x9 x3 P2 U( b改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。2 q5 r- e8 y' S3 e8 x% q
7 `% Q- H  X* {! J
变化:- X' m' k0 i2 o# L* z

0 w/ _( [7 N" L6 K. ?/ A6 ]+ yALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;
! I- F3 k3 W% K' Y/ H这个命令更改了.frm文件并且没有改动表。它非常快。7 J0 @* K  x. ?/ C* I! w
还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-1 08:28 , Processed in 0.038663 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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