打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。
创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。
二、索引类型
Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。
1. FULLTEXT
即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。
全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。
2. HASH
由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。
HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。
3. BTREE
BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。
三、索引种类
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
ps.
索引合并,使用多个单列索引组合搜索
覆盖索引,select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖
四、操作索引
1. 创建索引
1
--创建普通索引CREATE INDEX index_name ON table_name(col_name);--创建唯一索引CREATE UNIQUE INDEX index_name ON table_name(col_name);--创建普通组合索引CREATE INDEX index_name ON table_name(col_name_1,col_name_2);--创建唯一组合索引CREATE UNIQUE INDEX index_name ON table_name(col_name_1,col_name_2);
2. 通过修改表结构创建索引
1
ALTER TABLE table_name ADD INDEX index_name(col_name);
3. 创建表时直接指定索引
1
2
3
CREATE TABLE table_name (
ID INT NOT NULL,col_name VARCHAR (16) NOT NULL,INDEX index_name (col_name)
);
4. 删除索引
1
--直接删除索引DROP INDEX index_name ON table_name;--修改表结构删除索引ALTER TABLE table_name DROP INDEX index_name;
5. 其它相关命令
1
2
3
4
5
6
7
8
9
10
- 查看表结构
desc table_name;
- 查看生成表的SQL
show create table table_name;
- 查看索引
show index from table_name;
- 查看执行时间
set profiling = 1;
SQL...
show profiles;
五、创建索引的时机
到这里我们已经学会了建立索引,那么我们需要在什么情况下建立索引呢?一般来说,在WHERE和JOIN中出现的列需要建立索引,但也不完全如此,因为MySQL只对<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE才会使用索引。例如:
1
SELECT t.Name FROM mytable_t LEFT JOIN mytable_m ON t.Name=m.username WHERE m.age=20 AND m.city='郑州' ;
此时就需要对city和age建立索引,由于mytable_m表的userame也出现在了JOIN子句中,也有对它建立索引的必要。
刚才提到只有某些时候的LIKE才需建立索引。因为在以通配符%和_开头作查询时,MySQL不会使用索引。
六、命中索引
数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。
即使建立索引,索引也不会生效:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- like '%xx' select * from tb1 where name like '%cn';- 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';- or select * from tb1 where nid = 1 or email = 'seven@live.com';
特别的:当or条件中有未建立索引的列才失效,以下会走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'- 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,不然...
select * from tb1 where name = 999;- != select * from tb1 where name != 'alex' 特别的:如果是主键,则还是会走索引
select * from tb1 where nid != 123- > select * from tb1 where name > 'alex' 特别的:如果是主键或索引是整数类型,则还是会走索引
select * from tb1 where nid > 123 select * from tb1 where num > 123- order by select email from tb1 order by name desc;
当根据索引排序时候,选择的映射如果不是索引,则不走索引
特别的:如果对主键排序,则还是走索引:
select * from tb1 order by nid desc;
- 组合索引最左前缀
如果组合索引为:(name,email)
name and email -- 使用索引 name -- 使用索引 email -- 不使用索引
七、其它注意事项
1
- 避免使用select *- count(1)或count(列) 代替 count(*)- 创建表时尽量时 char 代替 varchar- 表的字段顺序固定长度的字段优先- 组合索引代替多个单列索引(经常使用多个条件查询时)- 尽量使用短索引- 使用连接(JOIN)来代替子查询(Sub-Queries)- 连表时注意条件类型需一致- 索引散列值(重复多)不适合建索引,例:性别不适合
八、LIMIT分页
若需求是每页显示10条数据,如何建立分页?
我们可以先使用LIMIT尝试:
1
--第一页SELECT * FROM table_name LIMIT 0,10;--第二页SELECT * FROM table_name LIMIT 10,10;--第三页SELECT * FROM table_name LIMIT 20,10;
但是这样做有如下弊端:
每一条select语句都会从1遍历至当前位置,若跳转到第100页,则会遍历1000条记录
若记录的id不连续,则会出错
改善:
若已知每页的max_id和min_id,则可以通过主键索引来快速定位:
1
--下一页SELECT * FROM table_name WHERE id in (SELECT id FROM table_name WHERE id > max_id LIMIT 10);--上一页SELECT * FROM table_name WHERE id in (SELECT id FROM table_name WHERE id < min_id ORDER BY id DESC LIMIT 10);--当前页之后的某一页SELECT * FROM table_name WHERE id in (SELECT id FROM (SELECT id FROM (SELECT id FROM table_name WHERE id < min_id ORDER BY id desc LIMIT (页数差*10)) AS N ORDER BY N.id ASC LIMIT 10) AS P ORDER BY P.id ASC);--当前页之前的某一页SELECT * FROM table_name WHERE id in (SELECT id FROM (SELECT id FROM (SELECT id FROM table_name WHERE id > max_id LIMIT (页数差*10)) AS N ORDER BY N.id DESC LIMIT 10) AS P) ORDER BY id ASC;
九、执行计划
explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化
1
mysql> explain select * from tb2;+----+-------------+-------+------+---------------+------+---------+------+------+-------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------+| 1 | SIMPLE | tb2 | ALL | NULL | NULL | NULL | NULL | 2 | NULL |+----+-------------+-------+------+---------------+------+---------+------+------+-------+1 row in set (0.00 sec)
1
2
id查询顺序标识
如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B;+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 9 | NULL || 2 | DERIVED | tb1 | range | PRIMARY | PRIMARY | 8 | NULL | 9 | Using where |+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ 特别的:如果使用union连接其值可能为null
select_type
查询类型
SIMPLE 简单查询PRIMARY 最外层查询
SUBQUERY 映射为子查询
DERIVED 子查询UNION 联合UNION RESULT 使用联合的结果
...table正在访问的表名
type
查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/constALL 全表扫描,对于数据表从头到尾找一遍select * from tb1;
特别的:如果有limit限制,则找到之后就不再继续向下扫描 select * from tb1 where email = 'seven@live.com' select * from tb1 where email = 'seven@live.com' limit 1;
虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就不再继续扫描。INDEX 全索引扫描,对索引从头到尾找一遍select nid from tb1;
RANGE 对索引列进行范围查找select * from tb1 where name < 'alex';
PS:between andin> >= < <= 操作
注意:!= 和 > 符号
INDEX_MERGE 合并索引,使用多个单列索引搜索select * from tb1 where name = 'alex' or nid in (11,22,33);
REF 根据索引查找一个或多个值select * from tb1 where name = 'seven';
EQ_REF 连接时使用primary key 或 unique类型select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;
CONST 常量
表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。select nid from tb1 where nid = 2 ;
SYSTEM 系统
表仅有一行(=系统表)。这是const联接类型的一个特例。select * from (select nid from tb1 where nid = 1) as A;
possible_keys
可能使用的索引key真实使用的
key_len
MySQL中使用索引字节长度
rows
mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值extra
该列包含MySQL解决查询的详细信息
“Using index”
此值表示mysql将使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。
“Using where”
这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。
“Using temporary”
这意味着mysql在对查询结果排序时会使用一个临时表。
“Using filesort”
这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
“Range checked for each record(index map: N)”
这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。
上表详解
十、慢查询日志
MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10S以上的语句。默认情况下,MySQLl数据库并不启动慢查询日志,需要我们手动来设置这个参数,当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件,也支持将日志记录写入数据库表。
1. 查看慢日志参数:
1
--查询配置命令show variables like '%query%';--当前配置参数binlog_rows_query_log_events OFFft_query_expansion_limit 20have_query_cache YES--时间限制,超过此时间,则记录long_query_time 10.000000query_alloc_block_size 8192query_cache_limit 1048576query_cache_min_res_unit 4096query_cache_size 1048576query_cache_type OFFquery_cache_wlock_invalidate OFFquery_prealloc_size 8192--是否开启慢日志记录slow_query_log OFF--日志文件slow_query_log_file D:\Program Files (x86)\mysql-5.7.18-winx64\data\Jack-slow.log--
2. 修改当前配置
1
set global 变量名 = 值;--例如,修改时间限制为20slong_query_time = 20;
ps.也可以直接打开慢日志配置文件进行修改,但必须重启服务才能生效
3. 查看MySQL慢日志
1
mysqldumpslow -s at -a /usr/local/var/mysql/MacBook-Pro-3-slow.log
1
2
3
4
5
6
7
8
9
10
11
12
13
"""--verbose 版本--debug 调试--help 帮助 -v 版本-d 调试模式-s ORDER 排序方式
what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time
ar: average rows sent
at: average query time
c: count l: lock time
r: rows sent
t: query time-r 反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first)-t NUM 显示前N条just show the top n queries-a 不要将SQL中数字转换成N,字符串转换成S。don't abstract all numbers to N and strings to 'S'-n NUM abstract numbers with at least n digits within names
-g PATTERN 正则匹配;grep: only consider stmts that include this string
-h HOSTNAME mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard),
default is '*', i.e. match all
-i NAME name of server instance (if using mysql.server startup script)
-l 总时间中不减去锁定时间;don't subtract lock time from total time
1 初识索引
索引在MySQL中也叫是一种“键”,是存储引擎用于快速找到记录的一种数据结构 —— 排好序的快速查找数据结构。
数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。
索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。
2 索引的原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等
索引的原理:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
索引的本质:实际上是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上。
【优势】
(1)类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本
(2)通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗
【劣势】
(1)虽然索引大大提高了查询速度,同时却会降低更新表(增删改)的速度。因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
(2)索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句(需要不断调整优化)。
3 MySQL索引
3.1 索引分类
#### 常用索引 ####
#1、单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
#2、唯一索引:索引列的值必须唯一,但允许有空值
-主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
-唯一索引UNIQUE:加速查找+约束(不能重复)
#3、联合索引:即一个索引包含多个列
-PRIMARY KEY(id,name):联合主键索引
-UNIQUE(id,name):联合唯一索引
-INDEX(id,name):联合普通索引
#4、组合索引(最左前缀匹配)
#5、覆盖索引
3.2 基本语法
(1)建表时添加索引
建表同时建立单索引
CREATE TABLE t_user1(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
INDEX (userName) #关键字INDEX
);
建表同时建立唯一索引(可以是单或多)
CREATE TABLE t_user2(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
UNIQUE INDEX index_userName(userName) #关键字UNIQUE和INDEX
);
建表同时建立联合索引
CREATE TABLE t_user3(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
INDEX index_userName_password(userName,PASSWORD)
);
(2)给已存在表添加索引
-- 单列索引
CREATE INDEX index_userName ON t_user(userName);
-- 唯一索引
CREATE UNIQUE INDEX index_userName ON t_user(userName);
-- 联合索引
CREATE INDEX index_userName_password ON t_user(userName,PASSWORD);
(3)删除索引
DROP INDEX index_userName ON t_user;
DROP INDEX index_userName_password ON t_user;
3.3 MySQL常见瓶颈
-
- CPU:CPU在饱和的时候一般发生在数据装入在内存或从磁盘上读取数据时候
- IO:磁盘I/O瓶颈发生在装入数据远大于内存容量时
- 服务器硬件的性能瓶颈:top,free,iostat和vmstat来查看系统的性能状态
4 正确使用索引
4.1 哪些情况需要创建索引
- 主键自动建立唯一索引
- 频繁作为查询的条件的字段应该创建索引
- 查询中与其他表关联的字段,外键关系建立索引
- 单间/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序的速度
- 查询中统计或者分组字段
- 哪些字段可以建索引? 一般都where、order by 或者 group by 后面的字段。
4.2 哪些情况不要创建索引
- Where条件里用不到的字段不创建索引
- 表记录太少
- 当修改性能(频繁更新的字段)远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的,因为每次更新不单单是更新了记录还会更新索引,加重IO负担)
- 数据重复且分布平均的表字段,因此应该只为经常查询和经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
- 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
- 对于那些在查询中很少使用或者参考的列不应该创建索引
and/or工作原理
4.3 mysql索引失效case及优化:
1,全值匹配我最爱
-- 【optimizer会对查询顺序自动进行优化调整】
2,最佳左前缀法则 【带头大哥不能死,中间兄弟不能断】
-- 如果索引了多例,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
3,索引列少计算
-- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
4,范围之后全失效
-- 存储引擎不能使用索引中范围条件右边的列
5,覆盖索引代替select *
-- 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6,少用 is null或is not null
-- is null,is not null 也无法使用索引
7,少用!=或<>
-- mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
8,少用or
-- 少用or,用它连接时会索引失效【查看上边and/or 工作原理】
9,like %放右边
-- 【like以通配符开头('%abc...')mysql索引失效会变成全表扫描操作】
-- 【放左边是all,连带他本身全失效】
-- 【可用覆盖索引(varchar为380失效)或主键索引】
10,varchar类型要加''单引号,切记!!!
-- 【不加会计算-自动转型】
-- 【导致行锁升级为表锁】
11,使用连接(JOIN)来代替子查询(Sub-Queries)
12,使用count(1)
5 查询优化神器-explain
5.1 是什么 —— 查看执行计划
使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈
优化语句基本上都是在优化rows。具体用法和字段含义可以参考官网 explain-output
5.2 能干嘛
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
- 索引长度
5.3 执行计划包含的信息
Id | Select_type | Table | Type | Possible_keys | Key | Key_len | Ref | Rows | Extra |
【详解】
【1】id :select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
-- id相同,执行顺序由上至下
-- id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
-- id相同不同,同时存在
【2】select_type:查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询
-- SIMPLE:简单的select查询,查询中不包含子查询或者UNION
-- PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为
-- SUBQUERY:在SELECT或者WHERE列表中包含了子查询
-- DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)
MySQL会递归执行这些子查询,把结果放在临时表里。
-- UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;
若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
-- UNION RESULT:从UNION表获取结果的SELECT
【3】table:显示这一行的数据是关于哪张表的
【4】type:显示查询使用了何种类型,从最好到最差依次是:-- system>const>eq_ref>ref>range>index>ALL
-- system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
-- const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键至于where列表中,MySQL就能将该查询转换为一个常量
-- eq_ref:唯一性索引,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描
-- ref:非唯一索引扫描,返回匹配某个单独值的所有行。
本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
-- range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引
一般就是在你的where语句中出现了between、<、>、in等的查询这种范围扫描索引扫描比全表扫描要好,因为他只需要开始索引的某一点,而结束语另一点,不用扫描全部索引。
-- index:Full Index Scan,index与ALL区别为index类型只遍历索引树。
这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
-- all:FullTable Scan,将遍历全表以找到匹配的行
【5】possible_keys:显示可能应用在这张表中的索引,一个或多个。
-- 查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
【6】key:实际使用的索引。如果为null则没有使用索引
-- 查询中若使用了覆盖索引,则索引和查询的select字段重叠(USING index)
【7】key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好
-- key_len显示的值为索引最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
【8】ref:显示索引那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值
【9】rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
【10】Extra:包含不适合在其他列中显示但十分重要的额外信息
-- Using filesort(不好):说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成排序操作成为“文件排序”
-- Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by
-- Using where:表明使用了where过滤
-- USING index:表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!
-- 如果同时出现using where,表明索引被用来执行索引键值的查找;
-- 如果没有同时出现using where,表面索引用来读取数据而非执行查找动作。
【type类型说明】:
从最好到最差依次是:system>const>eq_ref>ref>range>index>ALL
一般来说,得保证查询只是达到range级别,最好达到ref
6 慢查询优化的基本步骤
# 0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
# 1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
# 2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
# 3.order by limit 形式的sql语句让排序的表优先查
# 4.了解业务方使用场景
# 5.加索引时参照建索引的几大原则
# 6.观察结果,不符合预期继续从0分析
7 MySQL锁机制
7.1 锁的分类
(1)从数据操作的类型(读、写)分
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
- 悲观锁:
- 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
- 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
- 乐观锁:
- 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。
- 乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
(2)从对数据操作的颗粒度
- 表锁
- 行锁
7.2 表锁(MyISAM)—— 偏读
特点:偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低
7.3 行锁(InnoDB)—— 偏写
特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
-- InnoDB与MyISAM的最大不同有两点:
一是支持事务(TRANSACTION);
二是采用了行级锁
7.4 页锁(了解即可)
开销和加锁时间界于表锁和行锁之间:会出现死锁;
锁定粒度界于表锁和行锁之间,并发度一般。
7.5 优化建议
- 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能较少检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
8 慢查询日志
查看是否开启及如何开启:
-- 默认:
SHOW VARIABLES LIKE '%slow_query_log%'
-- 开启:
set global slow_query_log = 1
# 慢日志
- 执行时间 > 10
- 未命中索引
- 日志文件路径
配置:
- 内存
show variables like '%query%';
show variables like '%queries%';
set global 变量名 = 值
- 配置文件
mysqld --defaults-file='E:\wupeiqi\mysql-5.7.16-winx64\mysql-5.7.16-winx64\my-default.ini'
my.conf内容:
slow_query_log = ON
slow_query_log_file = D:/....
# 注意:修改配置文件之后,需要重启服务
MySQL日志管理
9 日志分析工具
9.1 mysqldumpshow
查看mysqldumpshow的帮助信息
s:是表示按何种方式排序
c:访问次数
l:锁定时间
r:返回记录
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
t:即为返回前面多少条的数据
g:后边搭配一个正则匹配模式,大小写不敏感的
9.2 Show profiles
(1)是什么
是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优测量。
官网:http://dev.mysql.com/doc/refman/5.5/en/show-profile.html
默认情况下,参数处于关闭状态,并保存最近15次的运行结果
(2)分析步骤
1.是否支持,看看当前的SQL版本是否支持
2.开启功能,默认是关闭,使用前需要开启
3.运行SQL
-- select * from emp group by id%10 limit 150000
-- select * from emp group by id%20 order by 5
4.查看结果,show profiles;
5.诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL 数字号码;
6.日常开发需要注意的结论
-- converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
-- Creating tmp table 创建临时表
-- 拷贝数据到临时表
-- 用完再删除
-- Copying to tmp table on disk 把内存中临时表复制到磁盘,危险!!!
-- locked