MySQL优化

索引失效

CREATE TABLE `tradelog`
(
    `id`         int(11) NOT NULL,
    `tradeid`    varchar(32) DEFAULT NULL,
    `operator`   int(11) DEFAULT NULL,
    `t_modified` datetime    DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY          `tradeid` (`tradeid`),
    KEY          `t_modified` (`t_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1、条件字段函数操作

    select count(*)
    from tradelog
    where month (t_modified)=7;
-- month(t_modified) : 对索引字段做函数操作,可能会破坏索引值的有序性,会导致全表扫描 

select *
from tradelog
where id + 1 = 10000;
-- 优化器偷懒,MySQL 优化器不能用 id 索引快速定位到 9999 这一行。
-- 所以,需要你在写 SQL 语句的时候,手动改写成 where id = 10000 -1 才可以。

2、隐式类型转换

    select *
    from tradelog
    where tradeid = 110717;
-- MySQL 中,字符串和数字做比较的话,是将字符串转换成数字
-- 对于优化器来说,这个语句相当于:select * from tradelog where CAST(tradid AS signed int) = 110717;

3、隐式字符编码转换

两个表的字符集不同、字符集相同关联字段类型不同;
连接过程中关联字段会按照转换规则转化, 如果转化驱动表的字段,被驱动表还是会走索引的, 如果转化的是被驱动表的字段,则驱动表不会走, 是直接导致对被驱动表做全表扫描的原因。

只查一行的语句,执行慢

  • 1、查询长时间不返回

先执行一下 show processlist 命令,看看当前语句处于什么状态


线程的状态是 Waiting for table metadata lock 表示有一个线程正在表上请求或者持有 MDL 写锁,把 select 语句堵住了。
找到谁持有 MDL 写锁,然后把它 kill 掉

select blocking_pid from sys.schema_table_lock_waits
找出造成阻塞的 process id,把这个连接用 kill 命令断开即可

线程的状态是 Waiting for table flush 表示的是,现在有一个线程正要对表做 flush 操作

可能情况是:有一个 flush tables 命令被别的语句堵住了,然后它又堵住了我们的 select 语句

-- MySQL 里面对表做 flush 操作的用法
-- 如果指定表 t 的话,代表的是只关闭表 t;
-- 如果没有指定具体的表名,则表示关闭 MySQL 里所有打开的表。
-- 这两个语句执行起来都很快,除非它们也被别的线程堵住了
flush tables t with read lock;
flush tables with read lock;

select * from t where id=1 lock in share mode;
线程的状态是 statistics 表示的是,有一个事务在这行记录上持有一个写锁,select 语句就会被堵住

--找到阻塞的线程 blocking_pid
-- KILL  pid   断开连接,让事务回滚,释放锁
-- 占有行锁的是 update 语句,这个语句已经是之前执行完成了的,现在执行 KILL QUERY,无法让这个事务去掉行锁
select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`
  • 2、查询慢

两个事务,一个事务更新一条数据100万次,
另一个事务使用“快照读”,需要把当前数据执行100万次 undo log 会比较慢
使用当前读,虽然加锁但时间会更快一点

慢查询性能

索引没有设计好
SQL 语句没写好
MySQL 选错了索引

-- SQL 语句没写好/选错了索引
-- 不重发服务,使用 MySQL 提供了 query_rewrite 功能
select *
from t
where id + 1 = 10000;

-- 改写规则
insert into query_rewrite.rewrite_rules(pattern, replacement, pattern_database)
values ("select * from t where id + 1 = ?", "select * from t where id = ? - 1", "db1");
-- 插入的新规则生效
call query_rewrite.flush_rewrite_rules();

order by

尽量使用索引,避免排序

  • 使用了索引,无法避免排序(使用的索引和排序的字段不一致)
    1、排序字段存在于多个索引中
    2、排序字段,升降序不一致
    3、组合索引,k1查询,k2排序

group by

1、如果对 group by 语句的结果没有排序要求,要在语句后面加 order by null;
2、尽量让 group by 过程用上表的索引,确认方法是 explain 结果里没有 Using temporary 和 Using filesort;
3、如果 group by 需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大 tmp_table_size 参数,来避免用到磁盘临时表;
4、如果数据量实在太大,使用 SQL_BIG_RESULT 这个提示,来告诉优化器直接使用排序算法得到 group by 的结果。

join

  • 使用 left join 时,左边的表不一定是驱动表。
  • 如果需要 left join 的语义,就不能把被驱动表的字段放在 where 条件里面做等值判断或不等值判断,必须都写在 on 里面。
  • join 将判断条件是否全部放在 on 部分没有区别

优化分页查询

通常使用 <LIMIT M,N> + 合适的 order by 来实现分页查询,这种实现方式在没有任何索引条件支持的情况下,需要做大量的文件排序操作(file sort),性能将会非常得糟糕。如果有对应的索引,通常刚开始的分页查询效率会比较理想,但越往后,分页查询的性能就越差。

在使用 LIMIT 的时候,偏移量 M 在分页越靠后的时候,值就越大,数据库检索的数据也就越多。例如 LIMIT 10000,10 这样的查询,数据库需要查询 10010 条记录,最后返回 10 条记录。也就是说将会有 10000 条记录被查询出来没有被使用到。

  • 禁止传入过大的页
  • 避免扫描无用的行,覆盖查询+子查询(先查主键,根据主键查数据)

Explain

explain 后使用 show warnings 可以看到优化器优化之后的 SQL

id

SELECT的查询序列号, id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

select_type

每个select子句的类型

取值含义
SIMPLE简单SELECT,不使用UNION或子查询等
PRIMARY子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY
UNIONUNION中的第二个或后面的SELECT语句
DEPENDENT UNIONUNION中的第二个或后面的SELECT语句,取决于外面的查询
UNION RESULTUNION的结果,union语句中第二个select开始后面所有select
SUBQUERY子查询中的第一个SELECT,结果不依赖于外部查询
DEPENDENT SUBQUERY子查询中的第一个SELECT,依赖于外部查询
DERIVED派生表的SELECT, FROM子句的子查询
UNCACHEABLE SUBQUERY一个子查询的结果不能被缓存,必须重新评估外链接的第一行

table

显示这一步所访问数据库中表名称

partitions

访问的分区表信息

type

MySQL在表中找到所需行的方式

取值含义
ALLMySQL将遍历全表以找到匹配的行
indexindex与ALL区别为index类型只遍历索引树
range只检索给定范围的行,使用一个索引来选择行
ref表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref多表连接中使用primary key或者 unique key作为关联条件
const、system当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
NULLMySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

possible_keys

MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)

Key

显示MySQL实际决定使用的索引,必然包含在possible_keys中

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

ref

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

Extra

取值含义
Using filesort表示的就是需要排序
Using index表示的就是使用了覆盖索引
Using temporary表示的是需要使用临时表
Block Nested Loop表示 join 语句使用 Block Nested-Loop Join 算法,在大表上的 join 操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种 join 尽量不要用。
Using MRR表示的是用上了 MRR 优化,由于在 read_rnd_buffer 中按照 id 做了排序,所以最后结果集也是按照主键 id 递增顺序的
Using join buffer (Block Nested Loop)表示的是用 BNL 算法
Using where需要在服务层过滤数据(mysql分为服务层和存储引擎层)
using index condition需要回表查询数据,但是有部分数据是在二级索引过滤后,再回表查询数据,减少了回表查询的数据行数

Show Profile 分析 SQL 执行性能

SHOW PROFILE [type [, type] ... ]
[FOR QUERY n]
[LIMIT row_count [OFFSET offset]]
 
type 参数:
| ALL:显示所有开销信息
| BLOCK IO:阻塞的输入输出次数
| CONTEXT SWITCHES:上下文切换相关开销信息
| CPU:显示 CPU 的相关开销信息 
| IPC:接收和发送消息的相关开销信息
| MEMORY :显示内存相关的开销,目前无用
| PAGE FAULTS :显示页面错误相关开销信息
| SOURCE :列出相应操作对应的函数名及其在源码中的调用位置 (行数) 
| SWAPS:显示 swap 交换次数的相关开销信息

可以通过 set 语句在 Session 级别开启该功能:

select @@profiling;
set @@profiling=1;

Show Profiles 只显示最近发给服务器的 SQL 语句,默认情况下是记录最近已执行的 15 条记录,可以重新设置 profiling_history_size 增大该存储记录,最大值为 100。

获取到 Query_ID 之后,再通过 Show Profile for Query ID 语句,就能够查看到对应 Query_ID 的 SQL 语句在执行过程中线程的每个状态所消耗的时间了:

慢日志

#慢查询日志配置
,slow_query_log的值为OFF,表示慢查询日志是禁用的
show variables  like '%slow_query_log%';
#开启慢查询日志
,1表示开启,0表示关闭
set global slow_query_log =1;
#
将慢查询日志的阈值设置为 0,表示这个线程接下来的语句都会被记录入慢查询日志中  
set long_query_time=0;

optimizer_trace

/* 打开 optimizer_trace,只对本线程有效 */
SET
optimizer_trace='enabled=on'; 
 
/* @a 保存 Innodb_rows_read 的初始值 */
select VARIABLE_VALUE
into @a
from performance_schema.session_status
where variable_name = 'Innodb_rows_read';

/* 执行语句 */
select city, name, age
from t
where city = '杭州'
order by name limit 1000;

/* 查看 OPTIMIZER_TRACE 输出 */
SELECT *
FROM `information_schema`.`OPTIMIZER_TRACE`;

/* @b 保存 Innodb_rows_read 的当前值 */
select VARIABLE_VALUE
into @b
from performance_schema.session_status
where variable_name = 'Innodb_rows_read';

/* 计算 Innodb_rows_read 差值
   表示整个执行过程扫描的行数
*/
select @b - @a;
R_TRACE 输出 */
SELECT *
FROM `information_schema`.`OPTIMIZER_TRACE`;

/* @b 保存 Innodb_rows_read 的当前值 */
select VARIABLE_VALUE
into @b
from performance_schema.session_status
where variable_name = 'Innodb_rows_read';

/* 计算 Innodb_rows_read 差值
   表示整个执行过程扫描的行数
*/
select @b - @a;
  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL优化是通过调整MySQL的参数和配置,以及优化SQL语句的编写和执行,来提高MySQL系统的性能和可用性的过程。其主要目标是尽可能快地响应用户请求,提高系统的处理能力和并发性能,同时减少资源消耗和硬件成本。MySQL优化涉及多个方面,包括以下几个方面: 1. 参数和配置优化:通过调整MySQL的参数和配置,如缓冲区大小、连接数、线程数等,来提高系统的性能和可用性。 2. SQL语句优化:合理的SQL语句设计和索引优化可以显著提高系统的查询效率和响应速度。常见的SQL语句优化方法包括使用合适的索引、避免全表扫描、优化查询语句的写法等。 3. 数据库设计优化:良好的数据库设计可以提高系统的性能和可维护性。包括合理的表结构设计、适当的数据类型选择、范式设计等。 4. 查询缓存优化MySQL提供了查询缓存功能,可以缓存查询结果,减少数据库的访问次数,提高查询性能。但是在某些情况下,查询缓存可能会降低性能,需要根据具体情况进行配置和优化。 5. 硬件和操作系统优化:合理配置硬件和操作系统参数,如磁盘IO优化、内存管理、网络配置等,可以提高MySQL系统的性能和可用性。 6. 定期维护和监控:定期进行数据库的维护工作,如备份、优化表、碎片整理等,同时监控数据库的性能指标,及时发现和解决性能问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值