前言
本文主要给大家分享了关于sql语句优化的一般步骤,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
一、通过 show status 命令了解各种 sql 的执行频率
mysql 客户端连接成功后,通过 show [session|global] status
命令可以提供服务器状态信息,也可以在操作系统上使用 mysqladmin extend-status
命令获取这些消息。
show status
命令中间可以加入选项 session(默认) 或 global:
session (当前连接)
global (自数据上次启动至今)
# Com_xxx 表示每个 xxx 语句执行的次数。 mysql> show status like 'Com_%';
我们通常比较关心的是以下几个统计参数:
Com_select : 执行 select 操作的次数,一次查询只累加 1。
Com_insert : 执行 insert 操作的次数,对于批量插入的 insert 操作,只累加一次。
Com_update : 执行 update 操作的次数。
Com_delete : 执行 delete 操作的次数。
上面这些参数对于所有存储引擎的表操作都会进行累计。下面这几个参数只是针对 innodb 的,累加的算法也略有不同:
Innodb_rows_read : select 查询返回的行数。
Innodb_rows_inserted : 执行 insert 操作插入的行数。
Innodb_rows_updated : 执行 update 操作更新的行数。
Innodb_rows_deleted : 执行 delete 操作删除的行数。
通过以上几个参数,可以很容易地了解当前数据库的应用是以插入更新为主还是以查询操作为主,以及各种类型的 sql 大致的执行比例是多少。对于更新操作的计数,是对执行次数的计数,不论提交还是回滚都会进行累加。
对于事务型的应用,通过 Com_commit
和 Com_rollback
可以了解事务提交和回滚的情况,对于回滚操作非常频繁的数据库,可能意味着应用编写存在问题。
此外,以下几个参数便于用户了解数据库的基本情况:
Connections : 试图连接 mysql 服务器的次数。
Uptime : 服务器工作时间。
Slow_queries : 慢查询次数。
二、定义执行效率较低的 sql 语句
1. 通过慢查询日志定位那些执行效率较低的 sql 语句,用 --log-slow-queries[=file_name]
选项启动时,mysqld 写一个包含所有执行时间超过 long_query_time 秒的 sql 语句的日志文件。
2. 慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候慢查询日志并不能定位问题,可以使用 show processlist 命令查看当前 mysql 在进行的线程,包括线程的状态、是否锁表等,可以实时的查看 sql 的执行情况,同时对一些锁表操作进行优化。
三、通过 explain 分析低效 sql 的执行计划
测试数据库地址:https://downloads.mysql.com/docs/sakila-db.zip(本地下载)
统计某个 email 为租赁电影拷贝所支付的总金额,需要关联客户表 customer 和 付款表 payment , 并且对付款金额 amount 字段做求和(sum) 操作,相应的执行计划如下:
mysql> explain select sum(amount) from customer a , payment b where a.customer_id= b.customer_id and a.email='JANE.BENNETT@sakilacustomer.org'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: a partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 599 filtered: 10.00 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table: b partitions: NULL type: ref possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: sakila.a.customer_id rows: 26 filtered: 100.00 Extra: NULL 2 rows in set, 1 warning (0.00 sec)
select_type: 表示 select 类型,常见的取值有:
simple:简单表,及不使用表连接或者子查询
primary:主查询,即外层的查询
union:union 中的第二个或后面的查询语句
subquery: 子查询中的第一个 selecttable : 输出结果集的表
type : 表示 mysql 在表中找到所需行的方式,或者叫访问类型,常见类型性能由差到最好依次是:all、index、range、ref、eq_ref、const,system、null:
1.type=ALL
,全表扫描,mysql 遍历全表来找到匹配的行:
mysql> explain select * from film where rating > 9 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1000 filtered: 33.33 Extra: Using where 1 row in set, 1 warning (0.01 sec)
2.type=index
, 索引全扫描,mysql 遍历整个索引来查询匹配的行
mysql> explain select title form film\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film partitions: NULL type: index possible_keys: NULL key: idx_title key_len: 767 ref: NULL rows: 1000 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
3.type=range
,索引范围扫描,常见于<、<=、>、>=、between等操作:
mysql> explain select * from payment where customer_id >= 300 and customer_id <= 350 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: range possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: NULL rows: 1350 filtered: 100.00 Extra: Using index condition 1 row in set, 1 warning (0.07 sec)
4.type=ref
, 使用非唯一索引扫描或唯一索引的前缀扫描,返回匹配某个单独值的记录行,例如:
mysql> explain select * from payment where customer_id = 350 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: ref possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: const rows: 23 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.01 sec)
索引 idx_fk_customer_id
是非唯一索引,查询条件为等值查询条件 customer_id = 350
, 所以扫描索引的类型为 ref。ref 还经常出现在 join 操作中:
mysql> explain select b.*, a.* from payment a,customer b where a.customer_id = b.customer_id \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: b partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 599 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: SIMPLE table: a partitions: NULL type: ref possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: sakila.b.customer_id rows: 26 filtered: 100.00 Extra: NULL 2 rows in set, 1 warning (0.00 sec)
5.type=eq_ref
,类似 ref,区别就在使用的索引时唯一索引,对于每个索引的键值,表中只要一条记录匹配;简单的说,就是多表连接中使用 primary key
或者 unique index
作为关联条件。
mysql> explain select * from film a , film_text b where a.film_id = b.film_id \G *************************** 1. row ********