召隆企博汇论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2757|回复: 1

MySQL索引详解和优化技巧

[复制链接]

24

主题

5

回帖

199

积分

公司现有员工

积分
199
发表于 2019-12-9 11:33:39 | 显示全部楼层 |阅读模式
索引(MySQL中也叫“键(Key)")在数据越大的时候越重要。规模小、负载轻的数据库即使没有索引,也能& x$ h" P/ H+ n) Z. H' o" ]; d
有好的性能,但是当数据增加的时候,性能就会很快下降。理解索引如何工作的最简单的方式就是把索引看成) o) @7 ]8 B3 x6 E" \+ W% f
一本书。为了找到书中一个特定的话题,你须要查看目录,它会告诉你页码。索引会让查询锁定更少的列
! K# }4 |, e, `& ?; v! n在InnoDB中,只有事务提交后才会解锁1 P6 P/ m" c7 _5 P% Z( J
' f! ]' e1 x) ~! d8 k! _
索引包含了来自于表中某一列或多个列的值。如果索引了多列数据,那么列的顺序非常重要,因为MySQL只6 I% R2 y7 w& T) j! _% b
能高效地搜索索引的最左前缀(Leftmost Prefix)。如你所见,创建一个双列索引和两个单列索引是不一样的。3 Q# I  |' D. l) {

- C+ z. I+ V: \/ H- o) G: B+ C' ]" XB-TREE
( V0 s% z7 ^# f, {9 l能使用B-Tree索引的查询类型。B一Tree索引能很好地用于全键值、键值范围或键前缀查找。它们只有在查找, c# v& \2 X" L/ p' q/ L) X
使用了素引的最左前缀(Leftmost Prcfix)的时候才有用。上节中的索引对于以下类型的查询有用。
6 `  K6 g( g7 ?; r
  1. ( p5 G7 I3 P/ s4 Q+ _
  2. CREATE TABLE People(
    8 ]- [$ u, }9 {' W* E' Y/ e1 W
  3. last_name varchar(50)   not  null
    5 G: y9 U- U* s3 ]
  4.           first_name  varchar(50)     not   null* _6 O$ }- T" V! `7 ~
  5.           dob  date      not    null
    # m5 _, W7 x/ ^- |; a' v
  6.       gende       enum('m','f')    not    null
    % _; f( R+ f7 ?4 ^
  7.         key(last_name,first_name,dob)
复制代码
匹配全名! H, [6 i, y* ?, c7 b: f1 Z
全键值匹配指和索引中的所有列匹配。例如,索引可以帮你找到一个叫CubaAllen并且出生于1960-01-01。
- H. e' ?* m  @! l的人。
) _- u2 R$ \  F* p# i8 ?* U: h. U# n匹配最左前缀
: R4 K8 ^2 e* [1 tB-Tree索引可以帮你找到姓为Allen的所有人。这仅仅适用了索引中的第一列。
+ s! g( }$ o% k9 y1 ^5 r匹配列前缀
2 f* V+ c% l! U6 f! C$ A4 J可以匹配某列的值的开头部分。这种索引能帮你找到所有姓氏以J开头的人。这只会使用索引的第1列。% A+ Y. v- y2 J
匹配范围值
4 D& U; N1 i/ p) [: ~$ q. V这种索引能帮你找到姓大干Allen并且小干Barrymore的人。这也只会使用索引第一列.
, p6 L8 H$ _8 a  o精确匹配一部分并且匹配某个范围中的另一部分
  s) B/ h2 \' d2 U  X/ |4 D这种索引能帮你找到姓为Allen并且名字以字母K(Kim、Karl等)开头的人。它精确匹配了last
- M+ {1 N% j9 I& h' L6 u1 ^9 z; q% B列并且对first name列进行了范囤查询。
: s$ r9 T  v5 F, A" pname! P2 e: w% S" ~1 b$ A. r( o
只访问索引的查询4 G2 ~: A1 h' J" A, @6 r
B-Tree索引通常能支持只访问索引的查询,它不会访问数据行。
( ^! ^8 ]& a+ Z5 t1 I2 \$ r- \
! @8 J8 f% x% D# D# V. x由于树的节点是排好序的,它们可以用于查找(查找值)和ORDER BY查询(以排序的方式查找值)。通常来说,! N# O$ v( }' {. M* C; J, a, I
如果B-Tree能以某种特殊的方式找到某行,那么它也能以同样的方式对行进行排序。因此,上面讨论的所有查
* b: q/ ^% C7 `0 s8 w找方式也可以同等地应用于ORDER BY。2 k. U, E* [3 {% Y

6 B0 N' }9 Y( t" C% g+ p下面是B-Tree索引的一些局限:
4 |& Q7 D& p# B7 M
  h, Q0 V' U5 V1,如果查找没有从索引列的最左边开始,它就没什么用处。例如,这种索引不能帮你找到所有叫Bill的人,/ `- e" o0 d& m% L
也不能找到所有出生在某天的人,因为这些列不在索引的最左边。同样,你不能使用该索引查找某个姓( z; j& ^! e4 q" P6 j
氏以特定字符结尾的人。
7 I  W2 q! j- l9 V$ J: v0 Q6 h& ~. i( k/ }5 }- r! n  b
2,不能跳过索引中的列。也就是说,不能找到所有姓氏为Smith并且出生在某个特定日期的人。如果不定
( ]3 j2 S% y! c) G9 r  {义first_name列的值,MySQL就只能使用索引的第一列。
1 U- x2 B- _, M
: O/ I; |0 K# h3 M, z3,存储引擎不能优化访问任何在第一个范围条件右边的列.比如,如果查询是where last_name='Smith' AND first_name LIKE 'J%' and dob ='1967-12-23',访问就只能使用索引的头两列,因为LIKE是
; ]- y. E8 d: V! l7 B8 ~# g5 M范围条件(但是服务器能把其余列用于其他目的)。对于某个只有有限值的列,通常使用等干条件,而. X8 }- T% n) F8 Y( x' G3 D6 j' B
不是范围条件来绕过这个问题。本章稍后的索引案例中我们会举出详细的例子。- R! r7 b# k# ?- l

. A6 n- @! F2 k0 k哈希索引,空间索引和全文索引等,暂时没有设计
/ h) @1 J1 {. n+ R* n2 X& D) O" p$ w* e9 Y7 h
高性能索引策略; T" R! `9 G7 ^; g
3 H0 G8 y# J! ]. n/ H* t  N
1,隔离列,意思就是不要对查询条件中列进行计算等操作/ P/ S, U5 x9 t' T3 V; F
2,前缀索引,针对blob和text,较长的varchar类型,使用前缀索引6 c3 d2 Y0 w5 `* I) J
Select count(distinct 列) /count(*) from table;6 ?! Z  l! J5 i; y
看看这个值时多少,如0.0312
% \! c6 g9 i2 Q# p* S9 t0 c$ y那么就是说,如果前缀的选择率能够接近0.0312,基本就可以了。可以在同一个查询中对不同长长度进行计算
, \& y" p4 N4 ]7 j% m4 I9 R,这对于大表很有用。
4 j/ _6 [1 \% P0 vSelect  count(distinct left(列,3)) /count(*)  as  sel1,* P. c* F" D/ S  w+ F
count(distinct left(列,4)) /count(*)  as  sel1  ,9 p6 e) u! j3 x% q7 r
count(distinct left(列,5)) /count(*)  as  sel1,, {6 g) C0 g% N& D; r  M/ f
count(distinct left(列,6)) /count(*)  as  sel1,' ]0 H. Q) J. j  }7 C; u# m: i
count(distinct left(列,7)) /count(*)  as  sel1   from table;0 V1 D0 i# ^0 o8 E! b; Y: u
找到接近0.0312即可。4 T! }% Q$ Z/ Z/ o7 ^, a

& X: f) H; x9 x2 ^  G5 a7 M1 JAlter table table_name add key (列(7))
5 B# H1 E8 O0 Q' l! B2 d3,覆盖索引
# Z( Y$ B: z8 }  Z; [1 i9 K& l包含或者覆盖所有满足查询的数据索引叫做覆盖索引
& G* a; S' M7 ?$ n! xexplain时,extra中的会显示using index: m+ v) f3 f$ e$ H
这里一个重要的原则是
9 O5 j9 l2 B9 u5 C" ~select后面的列不能使用*,要使用单独的需要查找的列,使用带索引的列2 D3 i0 v2 U1 [( P
如select id from table_name;
" p0 s2 g/ C" {& H: ~/ ?' U% ^2 Y: x& k! [# a0 k
很容易把Extra列的“使用索引(Using Index)”和type列的“索引(index)”弄混淆。然而,它们完全不
0 l0 ~; P6 ?% @7 [+ X一样。type列和覆盖索引没有任何关系,它显示了查询的访问类型,或者说是查询查找数据行的类型。
. B2 S: a# D) W  Q  b, A' T1 V* k0 i
  1. Explain Select * from table_name where col ='nam' and col1 like '%name%';
    . I5 e: Q' j1 y  r3 U. w  p
  2. Extra:using where
复制代码
该索引不能覆盖查询的原因:
% G; L( z$ [% f7 X1,
) M: @. [' T! C' z( q" w没有索引覆盖查询,因为从表中选择了所有的列,并且没有索引覆盖所有列。MySQL理论上有一个捷径可以使用,但是,WHERE子句只提到了索引覆盖的列,因此MysQL可以使用索引找到col并检查col1是否匹配,这只能通过读取整行进行。
0 X4 H# V6 r" u! h, h/ P: _; [2,& S& g6 O5 j  u+ ?$ G+ f
MySQL不能在索弓l中执行LIKE操作。这是低层次存储引擎API的限制,它只允许在索引进行简单比较。MysQL能在索引中执行前缀匹配的LIKE模式是因为能把它们转化为简单比较,但是查询中前导的通配符是存储引擎无法转化匹配的。因此,MySQL服务器自己将不得不提取和匹配行的数据,而不是索引值。  |, \  |. D: j/ h
有办法可以解决这个问题,那就是合并索引及重写查询。可以把索引进行延伸,让它覆盖(artist,title,prod_id)并且按照下面的方式重写查询:0 y7 h' F1 n5 d: z  o

- w7 M) D- J1 u4,为排序使用索引扫描
7 j& o2 h- E7 smysql有两种产生排序结果的方式:使用文件排序(fileSort),或者扫描有序索引。
/ w; T& z& P1 s* [7 F0 |, }explain输出type为index,表示mysql会扫描索引
. u( @4 S. ]; T* L9 D3 X$ ^1 i! P# z# R2 w4 \
扫描索引本身是很快的,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用索引覆盖查询,就不得不查找在索引中发现的每一行。这基本是随机I/O的,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对于I/O密集的工作负载.$ S( ^& Y0 k* M, `2 S, X

+ c; h. D& |* f# m. {$ _9 [MySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。
4 H5 G5 a/ R, {1 H* M+ Y
5 c" D3 |( K8 N; r6 k按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前级。在其他所有情况下,MySQL使用文件排序。
) ^- j7 ~; [- O8 _$ Q! ^  l: \; D5 n7 z- d& O, `2 m% I
ORDER BY无须定义索引的最左前级的一种情况是前导列为常量(也就是说第一个索引不能是范围查询,如果是组合索引应该以此为常量)。如果WHERE子句和JOIN子句为这些列定义了常量,它们就能弥补索引的缺陷。9 z3 s2 w) r: q
! l. i7 y1 [6 H5 n' y
使用join可能情况会有不同
$ j' c  J* G" |5 G$ L, v' B+ ?7 q& u% H2 S
5,压缩索引(myisam). j, }; V! w  N/ z, Z5 Y
6,多余和重复索引(应该避免)
" u! R! C/ m5 ^
+ r0 k  u1 H/ I+ ?6 W8 a多余索引(Redundant Index)和重复索引有一些不同。如果列(A,B)3 u* M2 r8 m5 u- a* S/ {
上有索引,那么另外一个列(A)上的- T) s% B# p" L8 i$ z
索引就是多余的。这就是说,(A,B)上的索引能被当成(A)上的索引。(这种多余只适合于B一Tree索引。)
; N1 _* n# R0 ?5 |然而,(B,A)上的索引不会是多余的,(B)上的索引也不是,因为列B不是列(A,B)的最左前缀。还有,不同类型的索引(例如哈希或全文索引)对于B一Tree索引不是多余的,无论它们针对的是哪一列。; s0 k$ S9 ~. s

4 V  K3 v" t4 |- `( L要点:
1 Q1 u5 z: g! W! w8 c2 n, e在任何可能的地方,都要试着扩展索引(之前是一个列A上面有索引,现在两个列A,B上建立索引),而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。如果不知道查询的分布,就要尽可能地使索引变得更有选择性,因为高选择性的索引通常更有好处.5 P3 H; _4 p7 j
! w6 O2 D) O3 R" J' }* Z# y" B0 F
即使InnoDB使用了索引,它也能锁定不需要的行,这个问题在它不能使用索引找到并锁定行的时候会更严重:如果没有索引,mysql不管是否需要行,都会进行全表扫描并锁定每一行
& E$ |7 O5 {3 F- l" o2 \
) y% c5 J$ e+ s; V/ `8 o$ g
" l0 w1 e0 l0 z# r) l( C2 }$ E; [- v5 S) ]. @3 p
回复

使用道具 举报

24

主题

5

回帖

199

积分

公司现有员工

积分
199
 楼主| 发表于 2019-12-9 11:34:27 | 显示全部楼层
创建索引时,
$ v8 w2 Q6 D: j- P3 i; s" M
2 O5 }1 o% j/ F8 `' b- ?拥有唯一值的列选择性最高,那些具有很多相同值的不适合创建索引
8 I6 D8 k- V7 I' o* r. }8 {2 L$ U' L7 h) l$ E

4 U& `+ B7 V$ ]7 f% _+ O5 \. `/ b9 ]+ h" H/ C. ^$ C+ A) i: B
一个通用的规则:保持表上的所有选项。当你设计索引的时候,不要只想着已有查询需要的索3 z4 A5 G$ t' @1 l1 ^! d

: p+ Z) Y6 i: @9 n/ t6 k4 w5 R引,也要想着优化查询。如果看到需要某个索引,但是一些查询会因它而受到损害,就要问问自己是否应该改变这些查询。应该一起优化查询和索引,以找到最佳的折中。没有必要闭门造车,以得到最好的索引。
3 p) d/ a, l4 u, k0 S7 y, K1 }. N: A* z5 S3 P/ R& T5 q

, |2 k, Z) B* p4 O8 ^) y- |# J2 D: w0 i9 _% O
一个在多列上面的索引,为了是这个索引生效,必须满足最左原则。& j8 U- ?- w! v- B2 F

( y+ @) Q6 I- J8 [0 K例如inex(a,b,c),这个时候如果只是用了a,c。没有使用b这个时候就不会使用索引。怎么处理
# h4 V" a) S. J+ C/ Y5 Y, W1 Y! o6 \! B4 m0 y+ o
这里如果b是一个可以枚举的类型那么可以使用in(…),将b全部列出。这样相当于b没有起到筛选的作用,但是却可以是索引发挥作用。这个方法也不能滥用,因为会出现n*n的结果,如果枚举数相乘过大,应该选择其他方式
# T* g) ^/ N- F2 t4 d2 m' t
2 d" W: ]2 N6 Y' D: W2 `
, T0 k6 T2 t% F& C2 I3 x& v* F
避免多个范围条件,只能对其中一个使用索引
* k( k! Y' [8 Y% E" e% o  \
5 N( f% P1 ^$ D$ J; M1 J: i' c. c4 P* d  r2 b7 B7 O; ~  Z

; ]4 o5 y( |5 }, `/ _8 `% p# u索引和表维护
4 {  K4 B7 F5 m! P/ k$ H5 i! X& u4 x3 V
表维护的主要目标:查找和修复损坏,维护精确的索引统计,并且减少碎片.
# T4 ~$ C5 q/ r! E' P' \2 \
4 t# d- T8 ]/ C) C  dcheck table table_name;: w& D$ C; _3 e1 F# {. e
repair table table_name;$ a5 T4 l' Q& n9 A1 k. ?. B
Show index from table_name;检查索引的基数性; B; u, C" I1 L' y' ^6 G
) J3 j. T* h% ]
主要关注cardinality列,显示存储引擎估计的索引中唯一值的数量$ \, b! O8 G9 k# U) c2 _+ [; }: |

# ?# a0 D  a- d; H4 L4 b4 Q
" b- m6 A% P, H7 w) r
9 ]( a0 p$ q$ ^+ nB-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。
( N; J- n# M7 \+ y$ w3 Q9 g" s9 @2 R1 u
表数据也能变成碎片化。两种类型:- z" V7 a6 X3 r& F0 J9 f
- G, B" [$ Z( L) X1 N
1,行碎片0 D/ \) G& ~6 X* C4 F; Z

7 U( a0 C" f4 A) F8 r当行披存储在多个地方的多个片段中时,就会是这种碎片。即使查询只从索引中找一行数据,行碎片也会降低性能。2 u* [% V5 Y3 Q3 W6 ]' p+ K+ K
1 a  H- _- R( j7 `0 W8 i0 j7 Y
# V  y: U/ Z3 n; w) C
$ W1 v5 L; d. ~* \# r: i& c! ~% z
2,内部行碎片
. ?4 s, D5 z. t3 g: L- p/ T% c) I& {8 Q* M
当逻辑上顺序的页面或行在磁盘上没有被顺序存储的时候,就会产生这种碎片。它影响了诸如全表扫描和
$ P7 p2 y4 r& h5 V; z" R/ c' t- R- K8 A0 v
聚集素引范围扫描这样的操作。这些操作通常从磁盘上的顺序数据布局得益。
) N& c0 H2 F$ M0 s0 c8 e% \, }6 N1 z' C( {6 e5 ^& S9 h; V- K
9 D2 z9 b' G# Q: T" @# J# `* k; z
. v& b( q$ p7 w4 X
为了消除碎片,可以允许OPTIMIZE TABLE或转储并重新加载数据。
  Z% X, @9 y, x2 Y5 ], A3 L
2 v9 r* |' f4 ~7 g; o# j! e% i+ [, I( L! M, h% v5 c  C

3 Y5 \$ B: o% M  ]" Z: }# P7 PALTER TABLE <table> ENGINE=<engine>9 j4 @' l4 V$ W' k
9 @3 O6 \& [! J  ^: w3 j

* A1 L+ A4 q5 f# h9 y7 U2 B, C7 H5 N; Y1 M+ |1 D  @
加速ALTER TABLE: I) f6 Q) O- Z* R+ ^+ O# A$ c$ @3 M/ @

5 _& w/ C7 Y; v. \! m$ {: \( X! v. E+ Q7 r7 g3 Z

2 J% j6 p) ~! iMySQL的ALTER TABLE的性能在遇到很大的表的时候会出问题。MySQL执行大部分更改操作都是新建一个需* k5 P! N7 N1 l+ U, H
% _% ^- a; P9 s
要的结构的空表,然后把所有老的数据插入到新表中,最后删除旧表.这会耗费很多时间,尤其是在内存紧张,
4 e, X& I6 j! \! ?% j
: g- u  a- @# {# M" q) O而表很大并含有很多索引的时候.许多人都遇到过ALTER TABLE操作需要几小时或几天才能完成的情况。
* x0 y( N7 Y; Q! @0 x" ^) M4 A
. X6 x3 C' X" y6 k: D% R% h传统:
# u' n0 W+ X) D
+ b: K! Q9 P( a% h7 L, G. SALTER TABLE table_name MODIFY COLUMN col TINYINT(3) NOT NULL DEFAULT 5;
/ u# n1 Y2 C7 ~/ U  t0 C7 h理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更* ~% Z' w6 m' M
改它。MySQL没有使用这种优化,然而,任何MODIFY COLUMN都会导致表重建。/ q7 _; V/ b9 a- v) y/ e. h( \
6 b6 c% b4 B4 s" w& g: F1 x
变化:8 J  K  o2 I2 o- C$ F$ U
0 O) m1 K# Q% a/ @3 S, w/ W
ALTER TABLE table_name ALTER COLUMN col SET DEFAULT 5;
  \, B4 i. V# l这个命令更改了.frm文件并且没有改动表。它非常快。
; N( r% B0 y1 m# p6 @1 V/ Z还有一个CHANGE COLUMN
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-1-1 20:34 , Processed in 0.042606 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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