转载:https://blog.csdn.net/enmotech/article/details/88809822html
本文主要总结了慢查询优化的过程当中经常使用的以及不合理的操做,适合有 MySQL 基础的开发人员。python
索引相关mysql
索引基数算法
基数是数据列所包含的不一样值的数量,例如,某个数据列包含值 一、三、七、四、七、3,那么它的基数就是 4。spring
索引的基数相对于数据表行数较高(也就是说,列中包含不少不一样的值,重复的值不多)的时候,它的工做效果最好。sql
若是某数据列含有不少不一样的年龄,索引会很快地分辨数据行;若是某个数据列用于记录性别(只有“M”和“F”两种值),那么索引的用处就不大;若是值出现的概率几乎相等,那么不管搜索哪一个值均可能获得一半的数据行。数据库
在这些状况下,最好根本不要使用索引,由于查询优化器发现某个值出如今表的数据行中的百分比很高的时候,它通常会忽略索引,进行全表扫描。惯用的百分比界线是“30%”。缓存
索引失效缘由安全
索引失效的缘由有以下几点:性能优化
对索引列运算,运算包括(+、-、*、/、!、<>、%、like'%_'(% 放在前面)。
类型错误,如字段类型为 varchar,where 条件用 number。
对索引应用内部函数,这种状况下应该要创建基于函数的索引。例如 select * from template t where ROUND (t.logicdb_id) = 1,此时应该建 ROUND (t.logicdb_id) 为索引。
MySQL 8.0 开始支持函数索引,5.7 能够经过虚拟列的方式来支持,以前只能新建一个 ROUND (t.logicdb_id) 列而后去维护。
若是条件有 or,即便其中有条件带索引也不会使用(这也是为何建议少使用 or 的缘由),若是想使用 or,又想索引有效,只能将 or 条件中的每一个列加上索引。
若是列类型是字符串,那必定要在条件中数据使用引号,不然不使用索引。
B-tree 索引 is null 不会走,is not null 会走,位图索引 is null,is not null 都会走。
组合索引遵循最左原则。
索引的创建
索引的创建须要注意如下几点:
最重要的确定是根据业务常常查询的语句。
尽可能选择区分度高的列做为索引,区分度的公式是 COUNT(DISTINCT col) / COUNT(*),表示字段不重复的比率,比率越大咱们扫描的记录数就越少。
若是业务中惟一特性最好创建惟一键,一方面能够保证数据的正确性,另外一方面索引的效率能大大提升。
EXPLIAN中有用的信息
基本用法
EXPLIAN 基本用法以下:
desc 或者 explain 加上你的 SQL。
extended explain 加上你的 SQL,而后经过 show warnings 能够查看实际执行的语句,这一点也是很是有用的,不少时候不一样的写法经 SQL 分析后,实际执行的代码是同样的。
提升性能的特性
EXPLIAN 提升性能的特性以下:
索引覆盖(covering index):须要查询的数据在索引上均可以查到不须要回表 EXTRA 列显示 using index。
ICP特性(Index Condition Pushdown):原本 index 仅仅是 data access 的一种访问模式,存数引擎经过索引回表获取的数据会传递到 MySQL Server 层进行 where 条件过滤。
5.6 版本开始当 ICP 打开时,若是部分 where 条件能使用索引的字段,MySQL Server 会把这部分下推到引擎层,能够利用 index 过滤的 where 条件在存储引擎层进行数据过滤。
EXTRA 显示 using index condition。须要了解 MySQL 的架构图分为 Server 和存储引擎层。
索引合并(index merge):对多个索引分别进行条件扫描,而后将它们各自的结果进行合并(intersect/union)。
通常用 or 会用到,若是是 AND 条件,考虑创建复合索引。EXPLAIN 显示的索引类型会显示 index_merge,EXTRA 会显示具体的合并算法和用到的索引。
Extra 字段
Extra 字段使用:
using filesort:说明 MySQL 会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
MySQL 中没法利用索引完成的排序操做称为“文件排序”,其实不必定是文件排序,内部使用的是快排。
using temporary:使用了临时表保存中间结果,MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
using index:表示相应的 SELECT 操做中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。
impossible where:where 子句的值老是 false,不能用来获取任何元组。
select tables optimized away:在没有 group by 子句的状况下基于索引优化 MIN/MAX 操做或者对于 MyISAM 存储引擎优化 COUNT(*) 操做,没必要等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
distinct:优化 distinct 操做,在找到第一匹配的元组后即中止找一样值的操做。
using filesort、using temporary 这两项出现时须要注意下,这两项是十分耗费性能的。
在使用 group by 的时候,虽然没有使用 order by,若是没有索引,是可能同时出现 using filesort,using temporary 的。
由于 group by 就是先排序在分组,若是没有排序的须要,能够加上一个 order by NULL 来避免排序,这样 using filesort 就会去除,能提高一点性能。
type 字段
type 字段使用:
system:表只有一行记录(等于系统表),这是 const 类型的特例,平时不会出现。
const:若是经过索引依次就找到了,const 用于比较主键索引或者 unique 索引。由于只能匹配一行数据,因此很快。若是将主键置于 where 列表中,MySQL 就能将该查询转换为一个常量。
eq_ref:惟一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配。常见于主键或惟一索引扫描。
ref:非惟一性索引扫描,返回匹配某个单独值的全部行。本质上也是一种索引访问,它返回全部匹配某个单独值的行,然而它可能会找到多个符合条件的行,因此它应该属于查找和扫描的混合体。
range:只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪一个索引,通常就是在你的 where 语句中出现 between、<、>、in 等的查询。
这种范围扫描索引比全表扫描要好,由于只须要开始于缩印的某一点,而结束于另外一点,不用扫描所有索引。
index:Full Index Scan ,index 与 ALL 的区别为 index 类型只遍历索引树,这一般比 ALL 快,由于索引文件一般比数据文件小。
也就是说虽然 ALL 和 index 都是读全表,但 index 是从索引中读取的,而 ALL 是从硬盘读取的。
all:Full Table Scan,遍历全表得到匹配的行。
字段类型和编码
MySQL 返回字符串长度
CHARACTER_LENGTH(同CHAR_LENGTH)方法返回的是字符数,LENGTH 函数返回的是字节数,一个汉字三个字节。
varchar 等字段创建索引长度计算语句
select count(distinct left(test,5))/count(*) from table;越趋近 1 越好。
MySQL 的 utf8
MySQL 的 utf8 最大是 3 个字节不支持 emoji 表情符号,必须只用 utf8mb4。须要在 MySQL 配置文件中配置客户端字符集为 utf8mb4。
JDBC 的链接串不支持配置 characterEncoding=utf8mb4,最好的办法是在链接池中指定初始化 SQL。
例如:hikari 链接池,其余链接池相似 spring . datasource . hikari . connection - init - sql =set names utf8mb4。不然须要每次执行 SQL 前都先执行 set names utf8mb4。
MySQL 排序规则
通常使用 _bin 和 _genera_ci:
utf8_genera_ci 不区分大小写,ci 为 case insensitive 的缩写,即大小写不敏感。
utf8_general_cs 区分大小写,cs 为 case sensitive 的缩写,即大小写敏感,可是目前 MySQL 版本中已经不支持相似于 ***_genera_cs 的排序规则,直接使用 utf8_bin 替代。
utf8_bin 将字符串中的每个字符用二进制数据存储,区分大小写。
那么,一样是区分大小写,utf8_general_cs 和 utf8_bin 有什么区别?
cs 为 case sensitive 的缩写,即大小写敏感;bin 的意思是二进制,也就是二进制编码比较。
utf8_general_cs 排序规则下,即使是区分了大小写,可是某些西欧的字符和拉丁字符是不区分的,好比 ä=a,可是有时并不须要 ä=a,因此才有 utf8_bin。
utf8_bin 的特色在于使用字符的二进制的编码进行运算,任何不一样的二进制编码都是不一样的,所以在 utf8_bin 排序规则下:ä<>a。
初始化命令
SQLyog 中初始链接指定编码类型使用链接配置的初始化命令,以下图:
SQL语句总结
经常使用但容易忘的
SQL 语句经常使用但容易忘的总结以下:
若是有主键或者惟一键冲突则不插入:insert ignore into。
若是有主键或者惟一键冲突则更新,注意这个会影响自增的增量:INSERT INTO room_remarks(room_id,room_remarks)VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks = "234"。
若是有就用新的替代,values 若是不包含自增列,自增列的值会变化:REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")。
备份表:CREATE TABLE user_info SELECT * FROM user_info。
复制表结构:CREATE TABLE user_v2 LIKE user。
从查询语句中导入:INSERT INTO user_v2 SELECT * FROM user 或者 INSERT INTO user_v2(id,num) SELECT id,num FROM user。
连表更新:UPDATE user a, room b SET a.num=a.num+1 WHERE a.room_id=b.id。
连表删除:DELETE user FROM user,black WHERE user.id=black.id。
锁相关
锁相关(做为了解,不多用):
共享锁:select id from tb_test where id = 1 lock in share mode。
排它锁:select id from tb_test where id = 1 for update。
优化时用到
优化时用到:
强制使用某个索引:select * from table force index(idx_user) limit 2。
禁止使用某个索引:select * from table ignore index(idx_user) limit 2。
禁用缓存(在测试时去除缓存的影响):select SQL_NO_CACHE from table limit 2。
查看状态
查看状态:
查看字符集:SHOW VARIABLES LIKE 'character_set%'。
查看排序规则:SHOW VARIABLES LIKE 'collation%'。
SQL 编写注意
SQL 编写请注意:
where 语句的解析顺序是从右到左,条件尽可能放 where 不要放 having。
采用延迟关联(deferred join)技术优化超多分页场景,好比 limit 10000,10,延迟关联能够避免回表。
distinct 语句很是损耗性能,能够经过 group by 来优化。
连表尽可能不要超过三个表。
踩坑
踩坑总结以下:
若是有自增列,truncate 语句会把自增列的基数重置为 0,有些场景用自增列做为业务上的 ID 须要十分重视。
聚合函数会自动滤空,好比 a 列的类型是 int 且所有是 NULL,则 SUM(a) 返回的是 NULL 而不是 0。
MySQL 判断 null 相等不能用 “a=null”,这个结果永远为 UnKnown,where 和 having 中,UnKnown 永远被视为 false,check 约束中,UnKnown 就会视为 true 来处理。因此要用“a is null”处理。
千万大表在线修改
MySQL 在表数据量很大的时候,若是修改表结构会致使锁表,业务请求被阻塞。
MySQL 在 5.6 以后引入了在线更新,可是在某些状况下仍是会锁表,因此通常都采用 PT 工具( Percona Toolkit)。
如对表添加索引:
pt-online-schema-change --user='root' --host='localhost' --ask-pass --alter "add index idx_user_id(room_id,create_time)"
D=fission_show_room_v2,t=room_favorite_info --execute
慢查询日志
有时候若是线上请求超时,应该去关注下慢查询日志,慢查询的分析很简单,先找到慢查询日志文件的位置,而后利用 mysqldumpslow 去分析。
查询慢查询日志信息能够直接经过执行 SQL 命令查看相关变量,经常使用的 SQL 以下:
-- 查看慢查询配置
-- slow_query_log 慢查询日志是否开启
-- slow_query_time 指定了慢查询的阈值
-- long_query_time 指定了慢查询的阈值
-- log_queries_not_using_indexes 是否记录全部没有利用索引的查询
SHOW VARIABLES LIKE'%quer%';
--查看慢查询是日志仍是表的形式
SHOW VARIABLES LIKE'log_output'
-- 查看慢查询的数量
SHOW GLOBAL STATUS LIKE'solw_queries';
mysqldumpslow 的工具十分简单,我主要用到的参数以下:
-t:限制输出的行数,我通常取前十条就够了。
-s:根据什么来排序默认是平均查询时间 at,我还常常用到 c 查询次数,由于查询次数很频繁可是时间不高也是有必要优化的,还有 t 查询时间,查看那个语句特别卡。
-v:输出详细信息。
例子:mysqldumpslow -v -s t -t 10 mysql_slow.log.2018-11-20-0500。
查看SQL进程和杀死进程
若是你执行了一个 SQL 的操做,可是迟迟没有返回,你能够经过查询进程列表看看它的实际执行情况。
若是该 SQL 十分耗时,为了不影响线上能够用 kill 命令杀死进程,经过查看进程列表也能直观的看下当前 SQL 的执行状态;若是当前数据库负载很高,在进程列表可能会出现,大量的进程夯住,执行时间很长。
命令以下:
--查看进程列表
SHOW PROCESSLIST;
--杀死某个进程
kill 183665
若是你使用的 SQLyog,那么也有图形化的页面,在菜单栏→工具→显示→进程列表。
在进程列表页面能够右键杀死进程,以下所示:
一些数据库性能的思考
在对公司慢查询日志作优化的时候,不少时候多是忘了建索引,像这种问题很容易解决,加个索引就好了。可是有几种状况就不是简单加索引能解决了:
业务代码循环读数据库
考虑这样一个场景,获取用户粉丝列表信息,加入分页是十个,其实像这样的 SQL 是十分简单的,经过连表查询性能也很高。
可是有时候,不少开发采用了取出一串 ID,而后循环读每一个 ID 的信息,这样若是 ID 不少对数据库的压力是很大的,并且性能也很低。
统计 SQL
不少时候,业务上都会有排行榜这种,发现公司有不少地方直接采用数据库作计算,在对一些大表作聚合运算的时候,常常超过五秒,这些 SQL 通常很长并且很难优化。
像这种场景,若是业务容许(好比一致性要求不高或者是隔一段时间才统计的),能够专门在从库里面作统计。另外我建议仍是采用 Redis 缓存来处理这种业务。
超大分页
在慢查询日志中发现了一些超大分页的慢查询如 Limit 40000,1000,由于 MySQL 的分页是在 Server 层作的,能够采用延迟关联在减小回表。
可是看了相关的业务代码正常的业务逻辑是不会出现这样的请求的,因此颇有多是有恶意用户在刷接口,最好在开发的时候也对接口加上校验拦截这些恶意请求。
来源:https://www.cnblogs.com/chenfangzhi
资源下载
关注公众号:数据和云(OraNews)回复关键字获取
2018DTCC , 数据库大会PPT
2018DTC,2018 DTC 大会 PPT
ENMOBK,《Oracle性能优化与诊断案例》
DBALIFE ,“DBA 的一天”海报
DBA04 ,DBA 手记4 电子书
122ARCH ,Oracle 12.2体系结构图
2018OOW ,Oracle OpenWorld 资料
产品推荐云和恩墨Bethune Pro企业版,集监控,巡检,安全于一身,你的专属数据库实时监控和智能巡检平台,漂亮的不像实力派,你值得拥有!
云和恩墨zData一体机现已发布超融合版本和精简版,支持各类简化场景部署,零数据丢失备份一体机ZDBM也已发布,欢迎关注。