性能下降SQL慢 、执行时间长 、等待时间长:
SQL执行顺序:
索引简介:
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高校获取数据的数据结构。
①可以得到索引的本质:索引是数据结构。
②索引的目的在于提高查询效率,可以类比字典,
③你可以简单理解为"排好序的快速查找数据结构"
。
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
①为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引健值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快逮的检索出符合条件的记录。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上。索引是物理数据页存储,在数据文件中(InnoDB,ibd文件),利用数据页(page)存储。
我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉树)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)等。
索引优势:
①类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本
②通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗
索引劣势:
①实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的
②虽然索引大大提高了查询速度,同时却会降低更新表的速度,如果对表INSERT,UPDATE和DELETE。
因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
③索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句
mysql索引分类:
普通索引(NORMAL):
①单值索引
: MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一 点。
②复合索引:
用户可以在多个列上建立索引,这种索引叫做组复合索引(组合索引),复合索引可以代替多个单一索引,相比多个单一索引,复合索引所需的开销更小
<1>窄索引:窄索引是指索引列为1-2列的索引
<2>宽索引:索引列超过2列的索引
<3>设计索引的一个重要原则就是能用窄索引不用宽索引,因为窄索引往往比组合索引更有效
<4>复合索引使用注意事项:
1、何时使用复合索引,要根据where条件建索引,注意不要过多使用索引,过多使用会对更新操作效率有很大影响
2、如果表已经建立了(col1,col2),就没有必要再单独建立(col1);
3、如果现在有(col1)索引,如果查询需要col1和col2条件,可以建立(col1,col2)复合索引,对于查询有一定提高
唯一索引(UNIQUE):
索引列的值必须唯一,但允许有空值。(唯一约束就是一个唯一索引
)
①主键索引:是一种特殊的唯一索引
,不允许有空值。(主键约束就是一个主键索引
)
全文索引:
①全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
②查询操作在数据量比较少时,可以使用like模糊查询,但是对于大量的文本数据检索,效率很低。如果使用全文索引,查询速度会比like快很多倍。但也很少用到。
<1>注意:在MySQL 5.6 以前的版本,只有MyISAM存储引擎支持全文索引,从MySQL 5.6开始MyISAM和InnoDB存储引擎均支持
空间索引:
①空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建
建议:
①一张表索引不要超过5个且优先考虑复合索引
②当索引的列名太长可以转换成前缀索引。
链接:索引的类型分类、区别、优缺点
MySQL创建表时建立索引和在已存在表中添加索引:
SPATIAL 为空间索引,其中SPATIAL InnoDB 存储引擎不支持,MyISAM存储引擎支持
,Normal表示普通索引,大多数情况下都可以使用。CREATE TABLE Y1 (
COLUMN_NAME DATA_TYPE SCHEME,
[UNIQUE | FULLTEXT | SPATIAL |NORMAL] [INDEX | KEY ] INDEX_NAME([LENGTH],.....)
[ASC | DESC]
)
第一种:
ALTER TABLE TABLE_NAME ADD [UNIQUE | FULLTEXT | SPATIAL|NORMAL] [INDEX | KEY ]
INDEX_NAME(COLUMN_NAME(LENGTH),.....) [ASC | DESC]
第二种:
CREATE [UNIQUE | FULLTEXT | SPATIAL|NORMAL] [INDEX | KEY ] INDEX_NAME ON
TABLE_NAME(COLUMN_NAME(LENGTH),.....) [ASC | DESC]
唯一索引,前缀索引,全文索引:
唯一索引:
前缀索引:
对于 BLOB、TEXT 和 VARCHAR 类型的列
)开始的部分字符串
,节约索引空间,从而提高索引效率。索引的选择性,是指不重复的索引数量除以总记录数,范围是(0,1]。
选择性是一样的!
全文索引:
使用 字符串列的索引规范中的语法,您可以创建仅使用列首字符的索引 。以这种方式仅索引列值的前缀可以使索引文件小得多。为a 或 column 编制索引时 , 必须为索引指定前缀长度。例如:col_name(N)NBLOBTEXT
按索引底层的数据结构分类mysql索引:
B+Tree
和散列表(Hash表)
作为索引的底层数据结构。Hash索引:
MySQL并没有显式支持Hash索引,而是作为内部的一种优化,对于热点的数据会自动生成Hash索引,也叫自适应Hash索引。
B+Tree索引:
提B-Tree
,B-Tree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。B+ 树是基于B-Tree升级后的一种树数据结构
,通常用于数据库和操作系统的文件系统中。B+ 树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入,这与二叉树恰好相反。不要将B树,B-Tree以及B+Tree弄混淆
。首先,B-Tree就是B树
,中间的“-”是一个中划线,而不是减号,并不存在"B减树"这种数据结构。其次,就是B+Tree和B-Tree实现索引时有两个区别:B+Tree只在叶子节点存储数据
,而B-Tree的数据存储在各个节点中
。具体可见下图:索引原理与数据结构:
B+Tree结构:
因为B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),这就意味着数据量相同的情况下B+树比B树更加的”矮胖“
指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;
等值查询时可以,但无法满足范围查找
哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。
哈希索引不支持多列联合索引的最左匹配规则 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题
用B+树查找案例:
Hash索引:
BTree索引和哈希索引的区别:
Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以Hash索引的查询效率要远高于B-Tree索引。
Hash索引仅仅能满足"=",“IN"和”<=>"查询,不能使用范围查询。哈希索引只支持等值比较查询,包括=、 IN 、<=> (注意<>和<=>是不同的操作)。 也不支持任何范围查询,例如WHERE price > 100。
由于Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样。
Hash索引无法被用来避免数据的排序操作。
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;
Hash索引不能利用部分索引键查询。
对于组合索引,Hash索引在计算Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash索引也无法被利用。
Hash索引在任何时候都不能避免表扫描。
前面已经知道,Hash索引是将索引键通过Hash运算之后,将 Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
Hash索引遇到大量Hash值相等的情况后性能并不一定就会比BTree索引高。
对于选择性比较低的索引键,如果创建Hash索引,那么将会存在大量记录指针信息存于同一个Hash值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
按数据存储方式分类Mysql索引:
同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。
这个特性决定了索引组织表中数据也是索引的一部分,每张表只能拥有一个聚簇索引。
Innodb通过主键聚集数据,如果没有定义主键,innodb会选择非空的唯一索引代替。如果没有这样的索引,innodb会隐式的定义一个主键来作为聚簇索引。
辅助索引叶子节点存储的不再是行的物理位置,而是主键值。
通过辅助索引首先找到的是主键值,再通过主键值找到数据行的数据页,再通过数据页中的PageDirectory找到数据行。所以一张表可以有多个辅助索引。在innodb中有时也称辅助索引为二级索引。
若使用"where id = 14"
这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。检索Name,
到达其叶子节点获取对应的主键
。第二步使用主键
在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其他键需要建立辅助索引)聚簇索引和非聚簇索引:
:B+Tree的叶子节点存放主键索引值和行记录就属于聚簇索引;如果索引值和行记录分开存放就属于非聚簇索引主键索引和辅助索引:
:B+Tree的叶子节点存放的是主键字段值就属于主键索引;如果存放的是非主键值就属于辅助索引(二级索引)MyISAM索引和InnoDB索引总结:
Innobd中的主键索引是一种聚簇索引,
Innobd非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引。
在结构上两者没有什么区别
,它们各自存放对应主键或辅助键,最后指向对应地址的表数据。不过,主键索引中key是唯一的,而辅助索引中key是可重复的。Explain:
使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈。
Explain各个字段解释:
选择标识符
表示查询时,可能使用的索引
表示实际使用的索引
索引字段的长度
扫描出的行数(估算的行数)
执行情况的描述和说明
id:
select_type:
查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询
SIMPLE:
简单的select查询,查询中不包含子查询或者UNIONPRIMARY:
查询中若包含任何复杂的子部分,最外层查询则被标记为SUBQUERY:
在SELECT或者WHERE列表中包含了子查询DERIVED:
在FROM列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。UNION:
若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVEDUNION RESULT:
从UNION表获取结果的SELECTtable:
显示这一行的数据是关于哪张表的
type:
访问类型排列。
system:
表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计。const:
表示通过索引一次就找到了,const用于比较primary key或者unique索引
。因为只匹配一行数据,所以很快。如将主键至于where列表中,MySQL就能将该查询转换为一个常量。(用在单表查询时)
eq_ref:
唯一性索引,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描。(用在多表查询时)
const和ref_eq区别ref:
非唯一索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体range:
只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为他只需要开始索引的某一点,而结束语另一点,不用扫描全部索引。index:
Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)all:
FullTable Scan,将遍历全表以找到匹配的行其他四个字段:
possible_keys:
key:
key_len:
ref:
rows:
Extra:
using join buffer:
使用了连接缓存impossible where:
where子句的值总是false,不能用来获取任何元组select tables optimized away:
在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。distinct:
优化distinct,在找到第一匹配的元组后即停止找同样值的工作回表查询与索引覆盖:
通过索引查询主键值,然后再去聚簇索引查询记录信息。
也就是无法在一课索引树上查询得到所有信息的查询就是回表查询。
explain的输出结果Extra字段为Using index时,能够触发索引覆盖。
只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。
实现索引覆盖最常见的方法就是:将被查询的字段,建立到组合索引
MySQL常见瓶颈:
子查询和连接查询对比:
连接查询
子查询
用哪种没有一定之规,要看读取的数据量、表设计结构、数据库规模、程序设计等多种因素综合考虑。
COUNT(*)与COUNT(1)与COUNT(字段)与COUNT(主键)对比:
全表扫表:
在数据库中,对无索引的表进行查询一般称为全表扫描。全表扫描是数据库服务器用来搜寻表的每一条记录的过程,直到所有符合给定条件的记录返回为止。注意:全表扫描是直接扫描主键索引上的行记录,而不是通过主键值。而索引扫描是通过对应索引上的字段扫描。
此外对于索引全扫描也是索引扫描,例如SELECT 字段 from 表,SELECT count(字段) FROM 表。
COUNT(主键)只判断是否存在主键,其他字段是否为NULL不在乎。COUNT(字段)则判断该字段是否为NULL。
COUNT(*)和COUNT(1)没区别且都是通过主键索引来统计行数,COUNT(主键)也一样。
COUNT(字段)由于所占空间小,减少IO,因此速度会快一些,但是要保证字段为非空。
主键字段
查询的则会使用上索引,若查询的字段只能使用主键索引且不是根据主键字段时
则会进行全表扫描。(where后面)
是啥还要看查询的内容(select后面)
是要啥为什么select * 比select 字段 效率低?
《阿里java开发手册(泰山版)》 中 MySQL 部分描述:4 - 1. 【强制】在表查询中,一律不要使用 *作为查询的字段列表,需要哪些字段必须明确写明:
①增加查询分析器解析成本。
②增减字段容易与 resultMap 配置不一致。
③无用字段增加网络 消耗,尤其是 text 类型的字段。
不需要的列会增加数据传输时间和网络开销
①用“SELECT * ”数据库需要解析更多的对象、字段、权限、属性等相关内容,在 SQL 语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担。
②增大网络开销;* 有时会误带上如log、IconMD5之类的无用且大文本字段,数据传输size会几何增涨。如果DB和应用程序不在同一台机器,这种开销非常明显
③即使 mysql 服务器和客户端是在同一台机器上,使用的协议还是 tcp,通信也是需要额外的时间。
对于无用的大字段,如 varchar、blob、text,会增加 io 操作
①准确来说,长度超过 728 字节的时候,会先把超出的数据序列化到另外一个地方,因此读取这条记录会增加一次 io 操作。(MySQL InnoDB)
失去MySQL优化器“覆盖索引”策略优化的可能性
①SELECT * 杜绝了覆盖索引的可能性,而基于MySQL优化器的“覆盖索引”策略又是速度极快,效率极高,业界极为推荐的查询优化方式。
②例如,有一个表为t(a,b,c,d,e,f),其中,a为主键,b列有索引。
那么,在磁盘上有两棵 B+ 树,即聚集索引和辅助索引(包括单列索引、联合索引),分别保存(a,b,c,d,e,f)和(a,b),如果查询条件中where条件可以通过b列的索引过滤掉一部分记录,查询就会先走辅助索引,如果用户只需要a列和b列的数据,直接通过辅助索引就可以知道用户查询的数据。
如果用户使用select *,获取了不需要的数据,则首先通过辅助索引过滤数据,然后再通过聚集索引获取所有的列,这就多了一次b+树查询,速度必然会慢很多。
索引什么时候失效:
索引是否失效判别标准是查询时只走索引,一丁点全表扫描都不行!!!!
查询条件包含or可能导致索引失效:
复合索引未用左列字段:
联合索引
的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。
like以%开头:
需要类型转换:
where中索引列有运算:
where中索引列使用了函数:
索引字段上使用(!= 或者 < >,not in)时可能会导致索引失效:
索引字段上使用is null, is not null可能会导致索引失效:
左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效:
存储引擎不能使用索引中范围条件右边的列:
B+树排放顺序:会先按照第一层索引排序,如果第一层数据一样,则会按照第二层排序,如果第二层还是一样,则会继续按照第三层排序。
mysql估计使用全表扫描要比使用索引快,则不使用索引:
如果MySQL表的某一列含有NULL值,那么包含该列的索引是否有效?
不建议列上允许为NULL。最好设置NOT NULL,并给一个默认值,比如0和 ''空字符串等,如果是datetime类型,也可以设置系统当前时间或某个固定的特殊值,例如'1970-01-01 00:00:00'。
mysql 中!=(不等于) 到底走不走索引?
如果返回的结果集过大(大于20%),那么可能也不会走索引,而是选择更有效率的表扫描了
。创建索引情况分析:
索引设计:
索引字段尽量使用数字型(简单的数据类型)
尽量不要让字段的默认值为NULL
前缀索引和索引选择性
使用唯一索引
使用组合索引代替多个列索引
注意重复/冗余的索引、不使用的索引
Mysql优化器是如何选择索引的?
当然扫描行数并不是唯一的判断标准,优化器还会结合是否使用临时表、是否排序等因素进行综合判断。
MySQL 在真正开始执行语句之前,并不能精确地知道满足这个条件的记录有多少条,而只能根据统计信息来估算记录数。
也就是说这个基数越大,索引的区分度越好。
使用采样统计
永远小表驱动大表类似嵌套循环Nested Loop:
---当B表的数据集必须小于A表的数据集时,用in优于exists。---
select * from A where id in (select id from B)
等价于:
for select id from B
for select * from A where A.id - B.id
--- 当A表的数据集系小于B表的数据集时,用exists优于in。---
select * from A where exists (select 1 from B where B.id = A.id )
等价于
for select * from A
for select * from B where B.id = A.id
注意:A表与B表的ID字段应建立索引。
order by关键字优化:
优化策略:
尝试提高sort_buffer_size:
不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。尝试提高max_length_for_sort_data:
提高这个参数,会增加用改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率.索引与排序:
MySQL查询支持filesort和index两种方式的排序。
filesort是先把结果查出,然后在缓存或磁盘进行排序操作,效率较低
,filesort有两种排序算法:双路排序和单路排序:使用index是指利用索引自动实现排序,不需另做排序操作,效率会比较高。
GROUP BY关键字优化:
group by实质是先排序后进行分组,遵照索引建的最佳左前缀。
索引总结:
与其说是“数据库查询只能用到一个索引”,倒不是说是 和全表扫描/只使用一个索引的速度比起来,去分析两个索引二叉树更加耗费时间,所以绝大多数情况下数据库都是是用一个索引。
索引实际应用分析:
MySQL 语句执行的神器-Optimizer Trace:
默认情况下,该功能是关闭的,
大家可以使用如下方式打开该功能,然后执行自己需要分析的 SQL 语句,然后再从 INFORMATION_SCHEMA 的 OPTIMIZER_TRACE中查找到该 SQL 语句执行优化的相关信息。# 1. 打开optimizer trace功能 (默认情况下它是关闭的):
SET optimizer_trace="enabled=on";
SELECT ...; # 这里输入你自己的查询语句
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
# 当你停止查看语句的优化过程时,把optimizer trace功能关闭
SET optimizer_trace="enabled=off";
TRACE:表示优化过程的JSON格式文本。(重点)
{
trace: {
steps:[
{ join_preparation: {}},<-->SQL的准备阶段,sql被格式化,对应函数J0IN: : prepare
{ join_optimization: {}},<--> SQL优化阶段,对应函数JOIN: :optimize
{ join_execution: {} }<-->SQL执行阶段,对应函数:J0IN: :exec
]
}
}
Show profiles概述: