sql语句的优化

优化 sql 语句的一般步骤:

通过 show [sesson|global] status命令来查看各种 sql 语句的执行情况.

mysql 客户端连接成功后, 可以使用命令 show [session|global] status 来查看服务器的状态信息 (session 表示当前连接的统计结果, global 表示至上次启动数据库的统计结果, 如果不写系统默认是 sesson).
下面命令是显示了当前 session 中所有统计参数的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> show status like 'Com%';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Com_admin_commands | 0 |
| Com_assign_to_keycache | 0 |
| Com_alter_db | 0 |
| Com_alter_db_upgrade | 0 |
| Com_alter_event | 0 |
| Com_alter_function | 0 |
| Com_alter_instance | 0 |
| Com_alter_procedure | 0 |
| Com_alter_server | 0 |
| Com_alter_table | 0 |
| Com_alter_tablespace | 0 |
| Com_alter_user | 0 |
| Com_analyze | 0 |
| Com_begin | 0 |
| Com_binlog | 0 |
| Com_call_procedure | 0 |
| Com_change_db | 1 |
...

Com_xxx 表示每个 xxx 语句的执行次数,我们通常关心的是一下几个参数:

Com_select: 执行 select 的次数, 每次执行增加一次.
Com_insert: 执行 insert 的指数, 每次执行增加一次, 如果是批量执行也是一次.
Com_update: 执行 update 的次数, 每次执行增加一次.
Com_delete: 执行delete 的次数, 妹纸执行增加一次.

ps: 上面的这些记录对于所有的存储引擎都会记录,下面的参数是针对 innodb 的引擎的.

innodb_rows_read: select 查询返回的行数.
innodb_rows_insert: 执行 insert 插入操作的行数.
innodb_rows_update: 执行 update 更新操作的行数.
innodb_rows_delete: 执行 delete 删除操作的行数.

通过以上参数很容易的了解到当前数据库是以插入更新为主还是查询操作为主.以及各类 sql 大致执行的比列,

对于事务性的应用, 通过 Com_commitCom_rollback 可以连接事物提交和回滚的情况, 对于回滚操作非常频繁的数据库, 可能 应用编写的有问题.

此外,以下几个参数便于用户了解数据库的基本情况.

Connections: 试图连接服务器的次数.
Uptime: 服务器工作的时间.
Slow_queries: 慢查询的次数.

定位执行效率较低的 sql:

可以通过两种方法定位执行效率较低的 sql: 关键词 show processlist

通过慢查询日志定位那些执行效率较低的 sql.
慢查询日志在在查询完事儿后才记录, 所以并不能很实时的获取到问题的所在.可以使用 show processlist 命令查看当前执行的 sql 的线程,包括线程的状态, 是否锁表,可以实时查看 sql 的执行状态, 同时对一些锁边操作进行优化.

通过 explain 分析较低的 sql 执行计划:

通过以上步骤获取到了执行效率较低的 sql 语句, 可以使用 explain 或者 desc 命令获取mysql 如何执行 select 语句的信息,包括 select 语句执行过程中如何连接和连接的顺序等.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> explain select * from vc \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: vc
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)

ERROR:
No query specified

字段说明:

select_type : 表示 select 的类型.
table: 输出结果集的表
type: 表示 mysql 在表中找到所需行的方式(访问类型)
类型 级别 描述
all 1 全表扫描
index 2 索引全扫描
range 3 索引范围扫描(<,> !=, between)
ref 4 使用唯一索引扫描, 或者是唯一索引的前缀扫描
eq_ref 5 使用了唯一索引
const/system 6 表中最多有一个匹配行, 查询速度非常的好(primary key 或者 index )
null 7 mysql 不用表或者索引, 直接可以获取的结果,

type 类型如上图所示, 级别越高表示性能越好.

通过 show profile 分析 sql

通过命令 have_profiling 参数查看是否支持profile;

1
2
3
4
5
6
7
mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set, 1 warning (0.00 sec)

profiling 默认是关闭的, 可以使用set 语句在 session 级别打开 profiling.

1
2
3
4
5
6
7
8
9
10
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 0 |
+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> set profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)

通过 profiling 我们能更加清楚的了解 sql 语句的执行过程.
首先我们在一个 innodb 的表上执行查询操作:
select count(*) from ai;

1
2
3
4
5
6
7
mysql> select count(*) from ai;
+----------+
| count(*) |
+----------+
| 3 |
+----------+
1 row in set (0.00 sec)

执行完毕后使用 show profiles; 命令查看当前 sql 语句的 query_id 为13:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> show profiles;
+----------+------------+-----------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------+
| 1 | 0.00163600 | show tables |
| 2 | 0.00005400 | show table status 'ai' |
| 3 | 0.00032400 | show table status like 'ai' |
| 4 | 0.00027200 | show table status like 'ai' |
| 5 | 0.00019300 | select * from ai |
| 6 | 0.00011700 | show create table ai |
| 7 | 0.00009600 | show create table ai |
| 8 | 0.00004500 | insert into ai |
| 9 | 0.00144900 | insert into ai values (1) |
| 10 | 0.00113800 | insert into ai values (2) |
| 11 | 0.00120700 | insert into ai values (3) |
| 12 | 0.00016300 | select * from ai |
| 13 | 0.00017200 | select count(*) from ai |
| 14 | 0.00004700 | show profiling |
+----------+------------+-----------------------------+
14 rows in set, 1 warning (0.00 sec)

使用 show profile for query query_id; 查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> show profile for query 13;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000057 |
| checking permissions | 0.000005 |
| Opening tables | 0.000012 |
| init | 0.000011 |
| System lock | 0.000005 |
| optimizing | 0.000023 |
| executing | 0.000012 |
| end | 0.000002 |
| query end | 0.000005 |
| closing tables | 0.000006 |
| freeing items | 0.000011 |
| cleaning up | 0.000023 |
+----------------------+----------+
12 rows in set, 1 warning (0.00 sec)

一般执行慢只要集中在 sending_data 的语句上面. sending_data 一般是线程开始处理数据行到吧执行结果返回给客户端的过程.在 sending_data 的过程中需要做大量的磁盘读写操作, 所以经常是时间最耗时的状态.

通过 trace 分析优化器如何选择执行计划

...

索引

索引的分类:

B-Tree: 最常见的索引, 大部分的引擎都支持
HASH: 只有memory引擎使用, 使用场景简单
R_Tree: 空间索引
Full-text: 全文索引,Innodb 从5.6 开始支持 Full-text 索引
坚持原创技术分享,您的支持将鼓励我继续创作!
//