优化SQL
第一步
通过show status 命令了解更重SQL语句的执行频率
第二步
定位执行效率较低的SQL语句
方法1:通过慢查询日志定位那些执行效率较低的SQL语句。将slow-query-log参数设置为ON后,MySQL会将所有执行时间超过阈值的SQL,写入slow_quert_log_file参数所指定的文件中。
方法2:方法1只有执行后才会写入日志文件。通过show processList命令可以查看当前正在执行的线程,可以实时查看sql的执行情况
第三步
分析SQL
方法1:通过Explain命令分析低效SQL的执行计划
方法2:用show profile for query Id (id 可以通过show profiles 获取)查看sql语句具体执行过程中,不同过程所需要的时间。
方法3:通过trace分析优化器如何选择执行计划
优化SQL的原则
1.小表驱动大表
2.order by 尽量使用索引顺序排序
3.
Select * from A where A.id in (select B.id from B)#B表驱动A表,相当于先从B表中查出id,然后再在A表中循环匹配(最好B是小表 (驱动表))
Select * from A where exists(select 1 from B where A.id = B.id)# A表驱动B表,相当于先从A表中查找数据,然后再利用B表过滤数据(最好A是小表 (驱动表))
慢查询日志
慢查询日志可以记录sql执行时间超过阈值的记录。默认情况下慢查询日志是关闭的需要手动开启。不过只有在需要的时候才开启,不需要的时候不要开启。因为开启后会带来一定的性能影响。
set global slow_query_log = on
通过这条语句可以开启慢查询日志。但是这只是开启当前数据库在此时的慢查询日志。服务器重启后失效。
如果想要永久生效,则需要去mysql的配置文件进行配置
查看超过多少秒会被认为是慢sql
show VARIABLES like '%long_query_time%' # 查看慢查询日志记录的阈值
即大于10s的sql是慢sql。
set global long_query_time = 1 #设置阈值为1秒
select sleep(2)
注意:需要重新连接mysql这个阈值才会更改。
mysqldumpslow
mysqldumpslow是mysql提供的一个工具,它可以帮助我们更好的分析慢查询日志文件。
索引
索引:排好序的快速查找数据结构
索引类型
InnoDB子啊MySQL 5.7中支持自适应的Hash索引。所谓自适应就是,Mysql根据数据的访问频率和模式为某些热点页自动创建Hash索引,索引由buffer pool中的B_tree来自动生成,效率很高。Hash索引适用于key-value查询。当使用范围查询时Hash索引不起作用。
MysQL如何使用索引
B-Tree索引时最常见的索引,构造类似二叉树,但却不是二叉树。B-Tree的B不是二叉树的意思而是平衡的意思。B-Tree索引可以很好的用于全关键字,关键字范围,关键字前缀的查询。
B-Tree的结构
根节点root下面有多个分支(Branch节点)。Branch节点下面得就是明细叶子(Leaf节点)
什么时候建索引
1.主键自动创建唯一索引
2.频繁作为查询条件的字段
3.查询中与其他表关联的字段,外键关系建立索引
4.查询中排序的字段
5.查询中统计或者分组的字段
什么时候不用索引
1.where条件里用不到的字段
2.频繁更新的字段
3.
防止索引失效
最左匹配原则
如果使用了复合索引,那么索引的顺序很重要。使用索引的顺序要和创建索引的顺子一致(如果写的乱序也可以,因为myslq的优化器会把他优化成正序),之间不能缺少索引。否则复合索引不能完全使用到。首先第一个索引不能丢。后面的索引在哪断了那么后面的索引就都不起作用了,但是前面的依然有效。(这是和索引的数据结构B-Tree有关)
不要对索引进行操作
不要对索引进行操作,因为这样就不能再利用索引了。如使用left(索引列,n)函数.就会变成不精确的查询,不精确的查询会使索引列失效。
范围右侧的索引全失效
这个顺序是指创建的顺序,因为sql语句中的顺序会被优化器优化成最优的顺序(mysql 8是这样的)例子
explain select * from test19 where id3 = 1 and id2 > 2 and id1 = 1
explain select * from test19 where id2 = 1 and id3 > 2 and id1 = 1
我创建的索引是一个(id1 id2 id3)顺序的复合索引。当我用id2进行范围查询时id3的索引失效了。
尽量使用覆盖索引
进项不使用select * 如果要检索不在索引内的字段时,会回表。
explain select * from test19 where id3 = 1 and id2 > 2 and id1 = 1
explain select id1,id2,id3 from test19 where id3 = 1 and id2 > 2 and id1 = 1
但是我有一个疑问这俩type一个是index一个是range。讲道理range是在索引中进行范围查询,而index是扫描全部的索引。不是应该range更好么。(我使用复合索引的时候出现了这种情况。如果我不适用复合索引,只是使用普通索引,那么type都是range)
使用!=会使索引失效(mysq8 会用到索引)
如果是mysql8版本的话,同样会用到索引
explain select id1 from test19 where id1 !=1
explain select * from test19 where id1 !=1
如果我使用普通索引,那么type都是range
is (not) null无法使用索引(mysql 8 可以使用索引)
以下例子都是mysql8的
explain select * from test18 where id1 is null
模糊查询不能以%开头
explain select * from user where username like "name%"
explain select * from user where username like "%name"
但是如果非要使用%开头的话,可以使用下列解决方法
通过使用覆盖索引的方式
explain select username from user where username like "%name"
explain select password from user where username like "%name" # 非selet 覆盖索引
查找主键
explain select id from user where username like "%name"
防止索引列的类型转换
explain select username from user where username = '11'
explain select username from user where username = 11;
少用or(mysql 8 or不会使索引失效)
mysql 8不会使索引失效
explain select password from user where username = '11' or username ='1';
explain select password from user where username = '11' or username ='1';
Explain
Explain +SQL语句。
查询后会显示这些字段。通过这些字段可以知道
1.表的读取顺序
2.数据读取操作的操作类型
3.显示这一行的数据是关于哪张表的
4.显示查询使用了何种类型
5.可能使用的索引
6.真正使用的索引
7.显示与索引列进行等值匹配的字段
8.估算出要找到所需记录需要读取的行数(越少越好)
id (可以获得表的读取顺序)
explain select * from test16 ,test15,test14
当id相同时时,执行顺序为从上至下。即先查询test16表然后是test15 最后是test14
EXPLAIN SELECT
*
FROM
test16 t16
WHERE
t16.id = (
SELECT
t15.id
FROM
test15 t15
WHERE
t15.id = ( SELECT t14.id FROM test14 t14 ))
如果是子查询那么id会递增,id越大越先执行。
EXPLAIN SELECT
*
FROM
test16 t16 , test17
WHERE
t16.id = (
SELECT
t15.id
FROM
test15 t15
WHERE
t15.id = ( SELECT t14.id FROM test14 t14 ))
当既有相同id又有不同id时。先执行最大的。然后相同的按顺序执行。
Select_type(数据读取操作的操作类型)
1.Simple:简单的select查询,不包含子查询和union
2.Primary:若查询中包含任何复杂的子部分,最外层查询则被标记为Primary
3.SubQuery:在select或where糙汉子查询
4.Derived:在from列表中包含的子查询被标记为Derived(衍生),Mysql会递归执行这些子查询,把结果放在临时表里。
5.Union:若第二个select出现在union之后,则被标记为union。若union包含在from字句的子查询中,外层select被标记未Derived
6.union Result:从Union表获取结果的select
例子
EXPLAIN SELECT
*
FROM
( SELECT * FROM test15 UNION SELECT * FROM test14 ) ss,
test16
WHERE
ss.id = test16.id
table(操作的表名)
显示这一行的数据是关于哪张表的
type(显示查询使用了何种类型)
1.System:表只有一行记录(等于系统表),这是const类型的特例
2.Const:表示通过索引一次就找到了。const常用于比较唯一索引和主键索引。因为只匹配一行数据,所以很快。
3.eq_ref:唯一性索引扫描,对于每个索引键。表中只有一条记录与之匹配。常见于主键或唯一索引扫描
4.ref:非唯一性索引扫描,返回匹配某个单独值得所有行
从最好到最差
5.range:只检索给定范围的行,使用一个索引来选择行。(> < between)
6:index:全索引扫描
7:all:全表扫描
System>Const>eq_ref>ref>range>index>all
possible_keys(可能使用的索引)
显示可能应用到的索引,可能没有,可能有一个可能有多个。查询涉及到的字段上若存在索引,则该索引将被列出。
key(真正使用的索引)
实际中使用的索引,如果为NULL则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中。
覆盖索引:索引中包含(覆盖)我们需要查询的值
key_len(索引中使用的字节数)
表示了使用到的索引的长度。如果索引类型为int 那么key_len = 5(因为允许null 所以4+1) 如果时int not null 那么此时的key_len = 4
ref(显示与索引列进行等值匹配的字段)
显示与索引进行等值查询的字段,可能是某一列也可能是某个常量(const)
rows(估算出要找到所需记录需要读取的行数)
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。
extra(包含不适合在其他列中显示但十分重要的额外信息)
1.Using filesort: mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。mysql中无法利用索引完成的排序操作被称为文件排序
2. Using tempoary:使用了临时表保存中间结果,mysql对查询结果排序时使用了临时表。常见于order by 和group by
3.Using index:表明select字段使用了覆盖索引
4.Using Where:使用了where进行过滤
5.Using join buffer:连接表使用了join缓存
6.impossible where:where子句肯定是false
直方图
mysql8中加入了直方图,为优化sql提供了新的思路。直方图一般用于那些没有创建索引同时数据的唯一值有限,并且数据量分布不均的表中。虽然通过创建索引的方式也可以提高访问速度,但是如果这个字段的索引使用频率不高,但是又要负担很大的维护成本。此时通过创建直方图的方式可以大大降低维护成本,直方图只需要创建一次,不需要进行实时维护。
语句。直方图之所以可以提高访问速度,是因为通过直方图mysql的优化器可以更好的选择sql的执行策略。
Analyze table table_name update histogram on table_clomun
MyISam的表级锁
MyIsam支持表级锁。表级锁分为两种一种为共享读锁一种为独占写锁。MyISam引擎认为写操作比读操作更重要,因此即使锁的等待队列中存在读锁,写锁也会排在他的前面。因此MyISam不适合有大量更新和读取操作的系统。但是在一些特殊情况下也允许在读的同时进行插入。(需要对concurrent_insert参数进行设置)当concurrent_insert参数为0时。不允许读的时候插入。当为1时,允许中间没有空洞时向表的尾部插入。当为3时,允许有空洞时向尾部插入。
MySQL的行锁
1.innoDB的行锁是基于索引实现的,如果不通过索引访问数据,innodb会对所有数据加锁
2.在不同隔离级别下InnoDB的锁机制和一致性读策略不同
3.MySQL的恢复和复制对InnoDB锁机制和一致性读策略也有较大影响。
4.锁冲突甚至死锁很难完避免。
//主从复制