官网:https://www.mysql.com/
MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司
MySQL 是一种关联数据库管理系统(RDBMS), 将数据保存在不同的表中,而不是将所有数据放在一个大的仓库内,这样就增加了速度并提高了灵活性。
MySQL 特征
MySQL 是开源的,所以你不需要支付额外的费用。
MySQL 支持大型的数据库。可以处理拥有处理上千万条记录的大型数据库。
MySQL 可以允许于多个系统上,并且支持多种语言,这些编程语言包含 C、C++、Java、Perl、PHP 、 Eiffel 、 Ruby 和 Tcl 等。
show variables like '%storage_engines%'; #查看当前默认的存储引擎
复制代码
1.4.2 MyISAM和InnoDB
第2章 索引优化分析
2.1 概述
2.1.1 性能下降原因
性能下降SQL慢,执行时间长,等待时间长的原因:
查询语句写的烂
索引失效:单值和复合
关联查询太多join(设计缺陷或不得已的需求)
服务器调优及各个参数设置不合理(缓冲、线程数等)
2.1.2 常用的join查询
2.1.2.1 SQL执行顺序
①手写
通常我们按照需求写的SQL查询语句是这样的:
SELECT DISTINCT查询列表
FROM 左表 INNER|LEFT|RIGHT
JOIN 右表 ON 连接条件
WHERE 分组前的筛选条件
GROUP BY 分组列表
HAVING 分组后的筛选条件
ORDER BY 排序列表
LIMIT 分页参数
复制代码
我们手写的SQL中SELECT在最前面的位置。 ②机读
随着 Mysql 版本的更新换代,其优化器也在不断的升级,优化器会分析不同执行顺序产生的性能消耗不同而动态调整执行顺序。下面是经常出现的查询顺序:
FROM 左表 INNER|LEFT|RIGHT
JOIN 右表 ON 连接条件
WHERE 分组前的筛选条件
GROUP BY 分组列表
HAVING 分组后的筛选条件
SELECT DISTINCT查询列表
ORDER BY 排序列表
LIMIT 分页参数
复制代码
而在机读中FROM是最先执行的。 ③总结
2.1.2.2 join图
左外连接
A表独有部分+AB两表的公有部分。
-- SQL语句如下:
SELECT 查询列表 FROM A LEFT JOIN B ON A.key=B.key# 其中A.key=B.key指的是连接条件
-- 注:
-- 左外连接中左边的是主表,右边的是从表
复制代码
右外连接
-- SQL语句如下:
SELECT 查询列表 FROM A RIGHT JOIN B ON A.key=B.key# 其中A.key=B.key指的是连接条件
-- 注:
-- 右外连接中右边的是主表,左边的是从表
复制代码
内连接
获取的是两张表的公有部分。
-- SQL语句如下:
SELECT 查询列表 FROM A INNER JOIN B ON A.key=B.key# 其中A.key=B.key指的是连接条件
-- 注:
-- 内连接求的是多张表的交集部分
复制代码
左外连接去除交集
SELECT 查询列表 FROM A LEFT JOIN B ON A.key=B.key WHERE B.key IS NULL;# 将从表B的连接条件作为NULL值判断
复制代码
右外连接去除交集
SELECT 查询列表 FROM A RIGHT JOIN B ON A.key=B.key WHERE A.key IS NULL;# 将从表A的连接条件作为NULL值判断
复制代码
全外连接
注意:MySQL不支持full join。
SELECT 查询列表 FROM A FULL JOIN B ON A.key=B.key;# 全外连接就是求并集
# 可以间接完成效果
SELECT 查询列表 FROM A LEFT JOIN B ON A.key=B.key
UNION
SELECT 查询列表 FROM A RIGHT JOIN B ON A.key=B.key
复制代码
全外连接去除交集
SELECT 查询列表 FROM A FULL JOIN B ON A.key=B.key WHERE A.key IS NULL OR B.key IS NULL;
复制代码
实例如下:
-- 左外连接
select * from beauty left join boys on beauty.boyfriend_id=boys.id;
-- 右外连接
select * from beauty right join boys on beauty.boyfriend_id=boys.id;
-- 内连接
select * from beauty inner join boys on beauty.boyfriend_id=boys.id;
-- 左外连接去除交集
select * from beauty left join boys on beauty.boyfriend_id=boys.id where boys.id IS NULL;
-- 右外连接去除交集
select * from beauty right join boys on beauty.boyfriend_id=boys.id where beauty.boyfriend_id IS NULL;
-- 全外连接(MySQL不能运行)
select * from beauty full join boys on beauty.boyfriend_id=boys.id;
-- 全外连接去除交集(MySQL不能运行)
select * from beauty full join boys on beauty.boyfriend_id=boys.id where boys.id IS NULL OR beauty.boyfriend_id IS NULL;
复制代码
2.2 索引简介
2.2.1 索引是什么
MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质:索引是数据结构。可以简单理解为排好序的快速查找数据结构。
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度(即将排序的字段添加到索引)。 ②Using temporary
使了用临时表保存中间结果,MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。 注:出现了这个值就应该对查询语句进行优化了。
优化前:
③Using index
Using index 代表表示相应的 select 操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!如果同时出现 using where,表明索引被用来执行索引键值的查找;如果没有同时出现 using where,表明索引只是用来读取数据而非利用索引执行查找。
利用索引进行了排序或分组。 ④Using where
表明使用了 where 过滤。 ⑤Using join buffer
使用了连接缓存。
⑥impossible where
where 子句的值总是 false,不能用来获取任何元组。比如查找一个人名字既为"张三"又为"李四",不可能筛选成功。
`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES(NULL,23,'test',NOW());
ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`); # 创建索引
复制代码
2.4.2.2 索引失效的各种情况
(1)全值匹配最好
查询的字段按照顺序在索引中都可以匹配,此时速度最快。
比如上面的测试用例中创建了复合索引(name, age, pos),那么条件的参数(where name=xxx and age=xxx and pos=xxx)和索引中的字段个数相同并且顺序一致(name对应name,age对应age,pos对应age),那么情况最好,如下:
ORDER BY子句满足两种情况,会使用index排序,一是ORDER BY子句采用遵照最佳左前缀法则,二是where条件字段和ORDER BY子句组合起来,满足最佳左前缀法则排序分组优化
3.1.2.2 MySQL的排序算法
当发生 Using filesort 时,MySQL会根据自己的算法对查询结果进行排序。有两种排序算法:双路排序和单路排序。
3.1.2.2.1 双路排序
MySQL 4.1 之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和 order by 列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出.
从磁盘取排序字段,在 buffer 进行排序,再从磁盘取其他字段。
简单来说,取一批数据,要对磁盘进行了两次扫描,众所周知,I\O 是很耗时的,所以在 mysql4.1 之后,出现了第二种改进的算法,就是单路排序。
3.1.2.2.2 单路排序
从磁盘读取查询需要的所有列,按照 order by 列在 buffer 对它们进行排序,然后扫描排序后的列表进行输出, 它的效率更快一些,避免了第二次读取数据。并且把随机 IO 变成了顺序 IO,但是它会使用更多的空间, 因为它把每一行都保存在内存中了。
3.1.2.2.3 存在的问题
EXPLAIN SELECT * FROM tblA ORDER BY age ASC,birth DESC; #排序不一致,要么都是升序或者都是降序
复制代码
3.1.3 group by关键字优化
group by优化和order by优化大致相似。
group by实质是先排序后进行分组,遵照索引建的最佳左前缀。
当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置。
where高于having,能写在where限定的条件就不要去having限定了。
要想在排序时使用索引,避免 Using filesort,可以采用索引覆盖。
ORDER BY /GROUP BY后面字段的顺序要和复合索引的顺序完全一致。
ORDER BY /GROUP BY后面的索引必须按照顺序出现,排在后面的可以不出现。
要进行升序或者降序时,字段的排序顺序必须一致。不能一部分升序,一部分降序,可以都升序或者都降序。
如果复合索引前面的字段作为常量出现在过滤条件中,排序字段可以为紧跟其后的字段。
3.2 慢查询日志
默认情况下,MySQL 数据库没有开启慢查询日志,需要我们手动来设置这个参数。 如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会将SQL语句写入日志,因此或多或少带来一定的性能影响。
SQL语句说明SHOW VARIABLES LIKE ‘%slow_query_log%’;查询慢查询日志是否开启,默认为OFF表示未开启SET GLOBAL slow_query_log=1;开启慢查询日志SET GLOBAL slow_query_log=0;关闭慢查询日志SHOW VARIABLES LIKE ‘long_query_time%’;查询慢查询设定阈值,默认为10秒SET long_query_time=5;设定慢查询阈值为5秒,可以设置为几秒,单位为秒
注意:
et global slow_query_log=1开启慢查询日志,仅对当前数据库生效,MySQL重启后失效。