- N, G, {, R5 m- O& b- U1 P! ^6 cmysql索引和explain的详解索引原理分析4 \ u/ @" Z+ d- U+ E7 u! w, N1 ^* O
& w1 s) A( @1 j- _' C& z3 _: o1 a
索引存储结构
- d' _5 v# f0 T# ]. Z" F& t索引是在存储引擎中实现的,也就是说不同的存储引擎,会使使用不同的索引# o3 p+ `2 E& V+ ~' ^
MyISAM和InnoDB存储引擎:只支持B+ TREE索引, 也不能够更换
$ V1 `9 {- z ^MEMORY/HEAP存储引擎:支持HASH和BTREE索引5 j c8 Q- ?2 Z, g& |- ~! {9 [0 Y
P( P7 T/ J3 N; @! a
B树图示9 W# b, S) U) k# B! O) m
+ Y+ J4 h# s8 Y1 f& q- Z4 MB树是为了磁盘或其它存储设备设计的一种多叉(下面你会看到,相对于二叉,B树每个内结点有多个分支,即多叉)平衡查找树。 多叉平衡。
) f {7 u, i% R: T8 |* m6 S& f
6 [- m3 y# \. f( P$ H- y0 r
+ ~3 a& Q; |* M$ B' R
( [9 X' k3 u* |$ t0 X( y# [/ U
$ m# {, l. n+ g( Y" NB树和B+树的区别:' _' j& J7 y4 L6 Z$ V% V9 g
B树和B+树的最大区别在于非叶子节点是否存储数据的问题
2 J* ]* s. A' L/ V1 `3 v. O
/ g9 v3 U: L/ {6 I$ f9 y4 ?6 N4 X在结构上:9 W5 f# S0 l2 G9 K
(1) B树是非也只节点和叶子节点都会存储数据。; g- F! R2 ^/ }) \
(2) B+树只有叶子节点才会存储数据,而且数据都是在一行上,而且这些数据都是指针指向的,也是有顺序的。$ M9 m/ Y/ F9 S8 {) x0 x
% W$ T' A6 B _: c, S7 v1 C+ T" g在性能上:
+ D8 h; d, T( u& l+ I c9 w(1)对于B-树相对于B+数据,B-Tree因为非叶子结点也保存具体数据,所以在查找某个关键字的时候找到即可返回。而B+Tree所有的数据都在叶子结点,每次查找都得到叶子结点。所以在同样高度的B-Tree和B+Tree中,B-Tree查找某个关键字的效率更高。B-Tree在单条数据读写有着更强的性能。
2 L* Z5 p8 D% R& ~& |& Z(2)但由于B+Tree所有的数据都在叶子结点,并且结点之间有指针连接,在找大于某个关键字或者小于某个关键字的数据的时候,B+Tree只需要找到该关键字然后沿着链表遍历就可以了,而B-Tree还需要遍历该关键字结点的根结点去搜索。这个也决定当连表查询的时候mysql比起mongo有显著的优势。更重要的是由于B-Tree的每个结点(这里的结点可以理解为一个数据页)都存储主键+实际数据,而B+Tree非叶子结点只存储关键字信息,而每个页的大小有限是有限的,所以同一页能存储的B-Tree的数据会比B+Tree存储的更少。这样同样总量的数据,B-Tree的深度会更大,增大查询时的磁盘I/O次数,进而影响查询效率。
9 s& [( V+ O0 O9 c6 Q4 p+ P: y7 t; O; _* c5 g u A
聚集索引(MyISAM)/ L8 f& y5 L7 e% Q% K+ e1 G9 w% C+ M
B+树叶节点只会存储数据行(数据文件)的指针,简单来说数据和索引不在一起,就是聚集+ X9 L8 {9 a: z/ v
索引。
5 G, _' z4 @1 k! c: Z. v+ s/ k+ q聚集索引包含主键索引和辅助索引都会存储数据指针的值。7 R% }8 V4 H W: R
) R0 m, c; Y _3 x$ L% ]
P8 L1 `" X; G1 h4 T
1 m \! [2 d" M- [. A8 W辅助索引(次要索引)
+ }6 q6 I4 W2 v, L. t# Y$ A% s0 a$ X在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,. B" K5 \6 A# W& C# h: ~
而辅助索引的 key 可以重复。如果我们在 Col2 上建立一个辅助索引,则此索引的结构如下图所示! B) y" l0 ]0 _
7 l4 ]- x- Y* p. ?2 I& J# C
同样也是一颗 B+Tree,叶子节点中保存数据记录的地址。因此,MyISAM 中索引检索的算法为首先按照B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其data 域的值,然后以 data 域的值为地址,读取相应数据记录。$ P& f8 f+ n( v& s( E3 y
0 e( z: s: ]/ t; W: a: Y$ ^6 v9 m y# o1 i
聚集索引(InnoDB)" d% a: r3 u: H( ]* U( @
1 F* p% I( T% j主键索引(聚集索引)的叶子节点会存储数据行,也就是说数据和索引是在一起,这就是聚集索引。
: _( _' z) P& g+ M辅助索引只会存储主键值
. O6 T* ^+ }1 f( H/ s1 l如果没有没有主键,则使用唯一索引建立聚集索引;如果没有唯一索引,MySQL会按照一定规则创建聚集索引。
y- J0 @+ d% T7 ]4 r5 J: X" g6 Z3 T2 k( Z1 O1 ]" N6 K/ ~ I
主键索引
+ A) @: J" Z$ c8 T1.InnoDB 要求表必须有主键(MyISAM 可以没有),如果没有显式指定,则 MySQL系统会自动选择一个可以
! `2 u2 n# u* b7 ?5 F唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,类型为长整形。
% {# |' Z, k& ?5 I& e% s. `0 [& P! L0 M8 l- V' j! ^0 ?
2 i: P& f$ J5 K* Q" d! T
0 ]% |5 [0 Q8 R/ b8 g4 r
- O, x# M% A0 p! {( H- N. l: c. t7 s上图是 InnoDB 主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为 InnoDB 的数据文件本身要按主键聚集。
& H8 ~' r; U' q) l
/ U& x" f( w' \, U# D
^7 p! i* d: a# c8 n: z4 R
2 m" g) W7 T2 U
" g' K9 U* i) E
/ i9 X8 `0 J& ]1 ~# N# Q1 j/ a* n+ r" U3 U5 m6 X9 D ~; ?
mysql创建索引的时候和用法与索引息息相关,要建立合适的索引和理解一些索引的执行计划,就需要认识索引的结构。
, `" i2 u R! I" L" W
4 _4 O5 d+ _8 \explain的详解
' M( M9 e' }' j7 n Q9 i4 g% E% O# ] t/ k( Q* \: u) Z
参数说明:
! T$ z9 c7 x9 x, ~explain后会出现十列数据,下面将介绍这下面的十列数据。4 S* w) d* ^4 k1 |* W' C
/ r6 `/ K# u! |& W9 }, k& k
id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra
. i8 L$ ?7 x' y! P- u) W1 ~
% O$ g* ^+ ?$ ~/ n9 ]( @先附上案例表:
, q5 j1 ^ a, L3 T6 O+ F, t5 z! `
! P1 _' d/ ?, s9 z' Q& pCREATE TABLE `taddr` (
; f, G( p7 J2 S, R$ B' I `id` int(11) NOT NULL AUTO_INCREMENT,
" C3 _& u7 T. O6 o$ a `country` varchar(100) DEFAULT '',2 T7 D9 A- i, G7 Y9 p* o
`province` varchar(100) DEFAULT '',0 ]8 ]% |7 o4 T! m; i
PRIMARY KEY (`id`)( a, c; l8 v& ]" t) _& t
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8+ y! L+ K2 g1 I4 c- M7 V4 h
( Q' f- |/ K' f# A* p5 U$ N& |CREATE TABLE `user` (& z1 O8 y! N/ o: u7 N# Y
`id` int(11) NOT NULL AUTO_INCREMENT,9 i3 j' y% x) H0 b# {/ H; U; I
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, l" O5 K+ ~3 n$ G3 j
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,% ?$ S, B* @- y a: i& t9 C; p
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
Q' `: @6 \: w# ^; l9 g' ?# ?0 T `addr_id` int(11) NULL DEFAULT NULL,8 Q, [5 N0 x* Y$ ]
PRIMARY KEY (`id`) USING BTREE,
3 W; E* u6 A& o2 C1 e A- Y" q INDEX `addr_id`(`addr_id`) USING BTREE& X" o* E" j6 F1 ?* R2 ]5 j! K
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
. [2 I y1 C8 J" ]" J% d- s
! h% H2 N/ v0 L) B7 K5 F5 I! ^* X/ A+ A% h7 j5 k
CREATE TABLE `type_time` (
, O' p% h6 @, w- v O `id` int(11) NOT NULL AUTO_INCREMENT,( R. n( d. C1 m8 n
`time` varchar(255) DEFAULT '[]',+ V, d }2 B/ x
`name` varchar(100) DEFAULT '',
F h( s) s% t8 p PRIMARY KEY (`id`),
! C- |, \! l% M% }1 ? INDEX `name_time_index`(`name`,`time`) USING BTREE V9 d* g" f* T( K) ]
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
) Q h8 X+ Z6 S7 P6 s
5 h% s7 d3 S1 } K8 Y' _5 V一、id
& R( t9 R/ {# Q. I8 L& W5 d每个 SELECT语句都会自动分配的一个唯一标识符.
L" v$ H0 \% Y& A' P( Z3 B表示查询中操作表的顺序,有三种情况:
1 l/ q+ p. H1 [; Oid相同:执行顺序由上到下" ]9 @. N4 p- i$ x
id不同:如果是子查询,id号会自增,id越大,优先级越高。
3 H& N/ }+ P1 }. p! t5 b- oid相同的不同的同时存在$ \" s1 x+ W7 t' {% I8 g1 Y( E2 R2 J& w
id列为null的就表示这是一个结果集,不需要使用它来进行查询。
3 Q+ U/ n9 c# B c; v: h# }* k' y
- G I# _* O9 I5 J/ K/ |% k) B二、select_type
& @, _4 N9 L5 W5 \) ]
* Z8 v7 q+ t, _( A查询类型,主要用于区别普通查询、联合查询(union、union all)、子查询等复杂查询
8 V. c, ]3 @& g$ _/ {. F9 P+ L: s3 Z) r( S! {% Z
2.1、simple
B/ M/ I8 `8 Y7 U% j表示不需要union操作或者不包含子查询的简单select查询。有连接查询时,外层的查询为simple5 ~; B1 {1 z0 J; U% a U' r
) L7 t( V" W* _3 I" d% T5 s5 z
EXPLAIN select * from user5 {7 I( H) ^# S" Y
- G$ X- F4 |/ |8 {( w* }
: m* G6 _; F1 C/ m. t
S0 ^& ^% B% ?% X' Q' VEXPLAIN select u.id,u.addr_id,a.* from user u inner join taddr a on u.addr_id=a.id- Q3 s ?* q1 Z8 W( q" {" S, h
( f( G' X8 w, I% G2 P/ l) Z4 i7 V3 s) C
2.2 primary
1 R* v( Q+ P9 e" f# ~一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type为primary。0 }/ h0 V- j( L* ]& z) u; E
1 }/ w" w z4 w& Mexplain select * from taddr t inner join () p0 e _9 K* a( \" l
select addr_id from user ) u on t.id=u.addr_id
& j+ J7 v* a- |+ @7 n( e
9 O3 \) }& z" t9 S9 V) O9 L) |explain select * from user u where u.addr_id =1q
+ D! O' J5 p4 R/ F2 \' j/ e! M1 runion all+ z8 k' k- P2 n, A. R: \& f! l" D
select * from user u where u.addr_id =2
1 @9 w R/ r; z/ N. o
" n& a/ d7 m& ^! j9 P1 n" k9 ` L, R$ Z# P5 @* ?( ]
2.3 subquery
: P) c, T' P% l% ]0 T除了from字句中包含的一查询外,其他地方出现的子查询都可能是subquery
4 v. d4 L8 P. n" u% F. Y
& R2 a/ H- M$ U) C% h$ P- `1 k- K, d2.4 dependent subquery% D x4 b+ d4 x# O
y# O$ z3 d, n9 V7 X9 T与dependent union类似,表示这个subquery的查询要受到外部表查询的影响
, I) a; p5 x& [. T2 a: t u r/ f* U* K* R* Y+ o' S1 E9 J3 Z
explain select u.name,(select t.province from taddr t where u.addr_id=t.id) from user u
; U2 {( Z7 D$ d1 {. c7 ?4 j
7 B) H3 _0 a6 g6 E& M2.5 union, E. y O, k: G, u; |
union连接的两个select查询,第⼀个查询是PRIMARY,除了第一个表外,第二个以后的表select_type都是union
; c& ]( j5 v6 B& f/ ?5 d$ F" E- V! O0 F T% w
三、table5 K) A* a3 y' h; q; b
显示的查询表名,如果查询使用了别名,那么这里显示的是别名4 F8 m$ ?9 W0 G7 q @- ~2 E& W6 Z7 X
如果不涉及对数据表的操作,那么这显示为null
! c5 g2 N/ G; O+ u" d6 E- C' t' r$ Y如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。
( M2 a1 j0 ?' z) D! ~" p0 @如果是尖括号括起来的<union M,N>,与类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。
! `5 s! _3 m7 z" u6 ~+ y% i; d+ ^4 ?+ G$ J/ H6 t: E
四、type9 e4 q- ^$ @9 Q& p& W& z
0 x7 Y' ^9 X! U
依次从好到差:2 ?# h w" N9 I
system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,: B; O! l9 g# q+ t' l4 ]7 _
index_subquery,range,index_merge,index,ALL
. c6 W4 O$ N! e- |/ e
3 u: S1 j8 _1 Z0 [除了all之外,其他的type都可以使⽤到索引,除了index_merge之外,其他的type只可以用到一个索引
1 l5 w% d$ V5 \! b0 x: M9 L( V
' j/ C, u, I5 x* W8 Z4、1 system
: r4 t L( x' _; O, |表中只有一行数据或者是空表。# {) {9 c2 S* s T4 R8 ^; K/ b
5 O- g( j4 C5 R" A" v: r) \( ]
4、2const8 v: S& i/ g- O; e. c
使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。其他数据库也叫做唯一索引扫描。5 d2 ^6 u& \4 e' ~( t
6 K0 |1 i$ R% I8 \' [! S R$ v
4、3 eq_ref" i h0 F/ [( C! p$ g6 ?) r- P
关键字:连接字段主键或者唯一性索引。
3 o/ A5 e* q, J; F/ j3 O$ l# r; ~9 f此类型通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较较操作通常是 ‘=’, 查询效率较高.
! c8 b9 e6 z8 l/ c% a6 F! Y! D6 {2 e
EXPLAIN select u.id,u.addr_id,a.* from user u inner join taddr a on u.addr_id=a.id! e6 q/ K0 `8 {0 e! y" S
+ }" Z# E/ d9 N) Z( K+ N$ c: y+ L8 X9 T. X9 A1 R5 E' R
4 h6 {* r4 x. O/ n4 ]% I
! m; q7 [$ s4 m: O3 ^, c8 O
" [* c5 b; k4 d# z6 e- @4、4 ref
! w/ d$ F. f, s, o: E; n3 g针对非唯一性索引,使用等值(=)查询非主键。或者是使用了最左前缀规则索引的查询。 EXPLAIN select u.id,u.addr_id,a.* from taddr a left join user u on u.addr_id=a.id
# v5 c4 z U5 r5 |) Q- c+ J l! N0 T5 F. s; \9 ]! ?
4.5 fulltext/ `! n8 a6 s- h1 c/ l3 C$ ]. O
全文索引检索,要注意,全文索引的优先级很高,若全高索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引
2 L2 h1 W4 c2 Q/ s$ ~, y5 H( }3 j5 c$ s- I* E% D x
4、6 unique_subquery; A9 J. N% k8 C% }
用于where中的in形式子查询,子查询返回不重复值唯一值+ C! b! Q5 U# {- ]; I$ J/ s- H
1 ~" |) y: x4 o7 }/ O2 U+ [4、7 index_subquery% [1 T) y4 M8 k! P6 i
用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
2 ]9 P4 ?+ E; p# L$ D r! U0 I6 z& I3 U: r, R
4、8 range
. h' E0 k$ ^8 \0 u' {: O索引范围扫描,常用于使用>,<,is null,between ,in ,like等运算符的查询中。
9 S$ N2 y/ n! l/ u; X* L: j% k' R3 R5 [
explain select * from type_time a inner join (
3 M& {2 G, K6 k$ _select id from type_time where name =‘2’ and time in (‘2’,‘3’,‘4’) ) b on a.id=b.id; ^- [: u; h3 r4 s6 E( L
8 w$ ^0 c9 u F! Y u1 ]
- Y/ R5 w4 o" N
; L2 R+ L+ X! h2 w# g: O T2 R8 Z2 x" ? U P. x2 ^1 \4 F5 t% y
4、9 index
) i% |3 n& q) {( V键字:条件是出现在索引树中的节点的。可能没有完全匹配索引。
2 q( \8 d( @% G7 S" i索引全表扫描,把索引从头到尾扫一遍,常用于使用索引列就可以处理不需要读取数据文件的查询、可以使使用索引排序或者分组的查询。
* M" i6 h( j0 |5 r: Z* c1 o. ?( }/ V8 p* o4 o
explain select * from user group by addr_id
1 L+ N8 Q" e$ g# r; h* t+ v6 A6 h/ R0 e& [) C
# ^* a( `# ^% w) [# D- f/ r
^# y! B" ?* H' m, ?, U+ n
0 C, q% g) ~; v' q$ |& L* x2 p2 N8 b2 o \; Y* B5 n
explain select addr_id from user! G. N0 ]6 ~, o% Z o! X
8 S# I% i% n3 e' @" m+ M, G2 @* P
& Q7 r9 r6 l+ `! j7 E0 m1 y1 Y R9 V1 E
1 }0 |5 E u- ^
) O: T8 A: P8 X! \
4、10 all
9 D. `% q. T9 a! W5 O5 @! v* D9 U这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。
9 n5 C# t) X/ |7 j; m3 g9 r: s- s; f" g* E
五、possible_keys
; _' X8 z; [( M4 m# N Q! ~" x% E" M$ q- J
此次查询中可能选用的索引,一个或多个
3 q5 H6 D$ l& _3 v& n6 l4 \7 i
5 t& e. T' W6 t: V2 D* c六、key
# w+ ~* A! U! q- w- |查询真正使使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。& K l G1 A2 l0 }' N+ Y: v
8 {. ]$ u9 b/ y7 C, z. y
七、key_len
8 Z4 v7 M: V$ e; N/ ?+ P! ~7 S2 O
: k. J3 |7 q/ _9 q2 w1 F% Q4 M/ b用于处理查询的索引长度度,如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查' _2 F7 z6 r% [: P& y3 b8 F
询不一定都能使用到所有的列,具体使用到了多少个列的索引,这里就会计算进去,没有使用到的,这里不会计算进去。留意下这个列的值,算下你的多列索引总长度就知道有没有使用到所有的列了。+ f* E, r1 _' I, f9 c/ l0 v
另外,key_len只计算where条件用到的索引长度,而排序和分组就算使用到了索引,也不会计算到key_len中。
) M0 @- p8 a d; z+ cexplain select id from type_time where name =‘2’ 用到长度303
/ Z+ Y& \1 [" q# e+ o% g) J# B$ D# }7 V6 }1 |3 `
2 A/ g) c) ^4 {explain select id from type_time where name =‘2’ and time in (‘2’,‘3’,‘4’) 用到长度 10711 h' Y/ W+ t; R4 [' u w
3 @( ]9 ], g6 X- Y$ P( {
/ {8 D% Q* W+ Y& k' { R3 l" M
, P z$ G$ r1 f, ?八、ref
5 y, Y5 {) S/ d6 d7 ] D; k# F _如果是使用的常数等值查询,这里会显示const6 E- y: f5 W, k: }( {% `
如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段4 ?' s/ C. N1 T W" b8 G1 D
如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
3 s' I J" E+ X- w$ q3 V9 r
$ t7 h. m. Y; r九、rows
' O; Z, z- z8 _* x; m这里是执行计划中估算的扫描行数,不是精确值(InnoDB不是精确的值,MyISAM是精确的值,主要原因是InnoDB使用了MVCC并发机制)3 Z S) {+ Z3 Y" s+ O/ f
! X0 z8 ~: [. B: L, Y& @十、extra3 U4 D4 [5 [3 N& R: l- |! U
这个列包含不适合在其他列中显示但十分重要的额外的信息,其中比较常见有一些:4 A5 ]% H+ f5 F: R9 w& N0 {1 V1 k" c
2 s1 Y/ a1 h2 W" ~+ ~% M4 V10、1 using temporary
2 d* D; i$ U. E表示使用了临时表存储中间结果。 Y) V+ \7 a$ o1 M/ P7 G ^
MySQL在对查询结果order by和group by时使用临时表; s5 E% w# S, R3 d$ H, T% e
临时表可以是内存临时表和磁盘临时表,执行计划中看不出来,需要查看status变量,
. N% q; Y& h$ W9 vused_tmp_table,used_tmp_disk_table才能看出来。; v. \8 @2 Q' a4 J- ^
( X+ a2 o" D/ f) ?; K& Cexplain select * from user u inner join taddr t on u.addr_id=t.id GROUP BY t.id
' h' Q% W3 e$ _* d3 I1 E9 `: W) l2 a% B+ n( \; w+ n
w/ X: t2 D4 E9 c
1 N5 z3 U, I- i7 j; I# d0 O( S( y10、2 using filesort1 N( ]6 [9 E. b! H' M
排序时无法使用到索引时,就会出现这个。常用于order by和group by语句中7 ~8 ^1 v# h: Y
+ s; {; V4 ]7 J1 ^说明MySQL会使用个外部的索引排序,而不是按照索引顺序进行读取。+ E+ Z! m7 e/ G1 l- K: f2 N
MySQL中无法利索引索引完成的排序操作称为“文件排序“
% t( C" d% P) r4 E9 v- |2 B( [
, u% R! ]/ L, {! V5 w10、3 using index
/ H% n* s/ A% \( |查询时不需要回表查询,直接通过索引就可以获取查询的数据。! M+ t! }( }& H
表示相应的SELECT查询中使用到了覆盖索引(Covering Index),避免回表访问数据行,效率不
* E7 S& \3 B9 B& L: w错。
) f( I k3 c6 u: l) ^: v* ?4 {如果同时出现Using Where ,说明索引被用来执行查找索引键值
* O/ Q: o5 R) P7 s( T如果没有同时出现Using Where ,表明索引用来读取数据来执行查找动作。
g# C* i$ R; ]8 G9 O
; n2 I, T1 _ o! X3 K这里对索引的原理和explain做了一些介绍,需要索引需要建立之后对其改变查询方式可能会更能深刻理解 InnoDB 使用覆盖索引和非覆盖索引造成区别。这也是建立索引和使用sql需要特别考虑的问题。
0 ~4 T, e ` q————————————————
, z. M6 p9 [, O' Q, ?: L版权声明:本文为CSDN博主「筏镜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
8 ?- u0 C2 ~+ L$ j原文链接:https://blog.csdn.net/fajing_feiyue/article/details/105616629' K5 k) d' g0 C' k
. I+ h7 u3 U& G, X4 P6 Z5 G8 }
1 [% }. d' z. a |