博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MySQL管理与优化(14):SQL优化
阅读量:6119 次
发布时间:2019-06-21

本文共 9503 字,大约阅读时间需要 31 分钟。

hot3.png

SQL优化

优化SQL语句的一般步骤:

通过SHOW STATUS了解各SQL执行效率:

SHOW STATUS;SHOW STATUS LIKE 
;

如,

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_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             | 0     || Com_change_master         | 0     |...
具体各参数详解可见:

下面几个参数便于用户了解数据库的基本情况:

  • Connections: 试图链接MySQL服务器的次数。
  • Uptime: 服务器工作时间。
  • Slow_queries: 慢查询的次数。

定位执行效率较低的SQL语句:

  • 可通过慢查询日志定位那些执行效率较低的SQL语句,用--log-slow-queries[=file_name]选项启动。
  • 慢查询日志会在查询结束后才记录,可通过SHOW PROCESSLIST查看当前MySQL的线程,锁表等状态。

通过EXPLAIN分析低效SQL的执行计划:

  • 通过EXPLAIN关键字可分析sql语句执行过程,如:
mysql> EXPLAIN SELECT * FROM sales \G*************************** 1. row ***************************           id: 1  select_type: SIMPLE --查询类型, 有SIMPLE,PRIMARY,UNION,SUBQUERY等值        table: sales  --输出结果集的表         type: ALL    --表的连接类型:性能由高到低为:system, const, eq_ref, ref, ref_or_null, index_merge, unique_subquery,index_subquery, range, index, allpossible_keys: NULL   --查询时可能使用的索引          key: NULL   --实际使用的索引      key_len: NULL   --索引字段的长度          ref: NULL   --相对于key,若值为func,则表示从函数中获取的结果集         rows: 12     --扫描行的数量        Extra: NULL   --执行情况的说明和描述1 row in set (0.00 sec)

具体详情可参考:

确定问题并采取相应措施:

  • 当我们通过上面的一些措施发现性能问题所在后,就可以进行优化了,比如对一些查询字段添加索引等。

索引问题:

  • 索引是数据库优化最常用也是最重要的手段之一。

索引的存储分类:

  • MySQL中索引的存储类型目前只有BTREEHASH
  • MyISAMInnoDB存储引擎仅支持BTREE
  • MEMORY/HEAP存储引擎支持BTREE和HASH。

使用索引:

  • 多列索引中,仅查询第一列时才可能用到索引,如:
--创建多列索引mysql> CREATE INDEX idx_sales_year on sales(year, profit);Query OK, 0 rows affected (0.25 sec)Records: 0  Duplicates: 0  Warnings: 0--仅第一列有可能使用索引mysql> EXPLAIN SELECT * FROM sales WHERE year = 2005\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: refpossible_keys: idx_sales_year          key: idx_sales_year      key_len: 4          ref: const         rows: 6        Extra: NULL1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM sales WHERE profit = 2012\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 11        Extra: Using where1 row in set (0.05 sec)
  • 对于LIKE子句,仅当%不在第一个字符时,才有可能使用到索引:
mysql> EXPLAIN SELECT * FROM sales WHERE year LIKE '%2'\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 11        Extra: Using where1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM sales WHERE year LIKE '2%'\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: idx_sales_year          key: NULL      key_len: NULL          ref: NULL         rows: 11        Extra: Using where1 row in set (0.00 sec)
  • 对于大文本进行查询,使用全文索引而不使用LIKE %...%;
  • 对于NULL不会使用索引:
mysql> EXPLAIN SELECT * FROM sales WHERE year is null\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: NULL         type: NULLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: NULL        Extra: Impossible WHERE1 row in set (0.00 sec)

存在索引但不使用索引:

有些情况下,虽然存在索引,MySQL却不会使用索引:

  • 当key_part1均匀分布在1和100之间时,如:
SELECT * FROM table_name WHERE key_part1 > 1 AND key_part2 < 100;
  • 如果使用MEMORY/HEAP表并且WHERE条件中不使用"="进行索引列,那么不会用到索引,HEAP表只有在"="的条件下才会使用索引。
  • or分割开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及的索引不会被用到,如:
mysql> EXPLAIN SELECT * FROM sales WHERE country = 'ch%'\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: refpossible_keys: idx_sales_country          key: idx_sales_country      key_len: 22          ref: const         rows: 1        Extra: Using index condition1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM sales WHERE country = 'ch%' OR product = 'tn1'\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: idx_sales_country          key: NULL      key_len: NULL          ref: NULL         rows: 11        Extra: Using where1 row in set (0.00 sec)
  • 不是多列索引的第一列,也不会用到索引。
  • LIKE以%开始,也不会用到索引。
  • 若索引列类型是字符串,则一定要加上引号,否则也不会用到索引:
mysql> EXPLAIN SELECT * FROM sales WHERE country = 121\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: idx_sales_country          key: NULL      key_len: NULL          ref: NULL         rows: 11        Extra: Using where1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM sales WHERE country = '121'\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: refpossible_keys: idx_sales_country          key: idx_sales_country      key_len: 22          ref: const         rows: 1        Extra: Using index condition1 row in set (0.00 sec)

查看索引使用情况:

mysql> SHOW STATUS LIKE 'Handler_read%';+-----------------------+-------+| Variable_name         | Value |+-----------------------+-------+| Handler_read_first    | 2     || Handler_read_key      | 3     | 如果索引正在工作,值会增加| Handler_read_last     | 0     || Handler_read_next     | 0     || Handler_read_prev     | 0     || Handler_read_rnd      | 0     || Handler_read_rnd_next | 76    | 值越大,查询效率越慢+-----------------------+-------+

两个简单实用的优化方法:

定期分析表和检查表:

  • 分析表分析的结果可以得到准确的统计信息,分析期间会使用读取锁对表加锁,仅对MyISAM, DBD, InnoDB表有作用,其语法为:
ANALYZE [LOCA | NO_WRITE_TO_BINLOG] TABLE table1, table2, ...

如:

mysql> ANALYZE TABLE sales;+------------+---------+----------+----------+| Table      | Op      | Msg_type | Msg_text |+------------+---------+----------+----------+| test.sales | analyze | status   | OK       |+------------+---------+----------+----------+1 row in set (0.18 sec)
  • 检查表的作用是检查一个或多个表是否有错误,对MyISAM和InnoDB有作用,其语法为:
CHECK TABLE tb1_name [, tb1_name] ... [option] ... option = {QUICK|FASE|EXTENDED|CHANGED}
如:
mysql> CHECK TABLE sales;+------------+-------+----------+----------+| Table      | Op    | Msg_type | Msg_text |+------------+-------+----------+----------+| test.sales | check | status   | OK       |+------------+-------+----------+----------+1 row in set (0.00 sec)
  • 检查表对视图也试用,如视图引用的表不存在了,就能检查出错误。

定期优化表:

  • 优化表具有进行一些空间碎片整合,删除浪费的空间等作用,其语法:
OPTIMIZE [LOCAL | NO WRITE_TO_BINLOG] TABLE tb1_name, tb2_name,...
  • ANALIZE, CHECK, OPTIMIZE等操作都会对表进行锁定,所以应在数据库不繁忙时进行。

常用的SQL优化:

大批量插入数据:

  • 对于MyISAM的表,可通过关闭MyISAM表的非唯一索引的更新来提高插入速度,语法为:
ALTER TABLE tb1_name DISABLE KEYS;--loading dataALTER TABLE tb1_name ENABLE KEYS;
  • 对于InnoDB表,可以这样优化:

      1. 将导入的数据按主键的顺序排列,再导入;

      2. 导入数据前关闭唯一性校验::

SET UNIQUE_CHECKS=0;-- load dataSET UNIQUE_CHECKS=1;
      3. 导入数据前关闭自动提交:
SET AUTOCOMMIT=0;-- load dataSET AUTOCOMMIT=1;

优化INSERT语句:

  • 对于同一客户端连接,使用多值插入
  • 索引文件和数据文件分在不同的磁盘上存放
  • 如果进行批量插入,可以增加bulk_insert_buffer_size变量值的方法来提高速度,但这只能对MyISAM表使用。
  • 当从一个文本文件载入一个表示=时,使用LOAD DATA FILE会比INSERT快很多。

优化GROUP BY语句:

  • 默认GROUP BY会执行排序操作,可以通过ORDER BY NULL来禁止排序:
mysql> EXPLAIN SELECT * FROM sales GROUP BY product\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 12        Extra: Using temporary; Using filesort1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM sales GROUP BY product ORDER BY NULL\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: sales         type: ALLpossible_keys: NULL          key: NULL      key_len: NULL          ref: NULL         rows: 12        Extra: Using temporary1 row in set (0.04 sec)

优化ORDER BY语句:

  • MySQL可以使用一个索引来满足ORDER BY子句,而不需要额外的排序。当WHERE和ORDER BY使用相同的索引,且ORDER BY的顺序与索引顺序相同,并且ORDER BY的字段都是升序或都是降序。

优化嵌套查询:

  • 可用JOIN等子句来替换嵌套查询。

优化OR子句:

  • 应考虑为OR中的列添加索引

使用SQL提示:

  • 使用临时结果集,在遇到表锁定或花很长时间将结果传给客户端时有所帮助。

SELECT SQL_BUFFER_RESULTS * FROM ...

  • 针对INDEX来告诉MySQL执行相关的索引操作:

mysql> EXPLAIN SELECT * FROM users WHERE id = 3\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: users         type: constpossible_keys: PRIMARY,idx_users_id          key: PRIMARY      key_len: 4          ref: const         rows: 1        Extra: NULL1 row in set (0.00 sec)-- USE INDEX告诉MySQL使用indexmysql> EXPLAIN SELECT * FROM users USE INDEX (idx_users_id) WHERE id = 3\G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: users         type: refpossible_keys: idx_users_id          key: idx_users_id      key_len: 4          ref: const         rows: 1        Extra: NULL1 row in set (0.00 sec)其他:-- IGNORE INDEX告诉MySQL忽略索引-- FORCE INDEX告诉MySQL强制使用索引

不吝指正。

转载于:https://my.oschina.net/indestiny/blog/290080

你可能感兴趣的文章
MS SQLSERVER通用存储过程分页
查看>>
60.使用Azure AI 自定义视觉服务实现物品识别Demo
查看>>
Oracle 冷备份
查看>>
jq漂亮实用的select,select选中后,显示对应内容
查看>>
C 函数sscanf()的用法
查看>>
python模块之hashlib: md5和sha算法
查看>>
linux系统安装的引导镜像制作流程分享
查看>>
解决ros建***能登录不能访问内网远程桌面的问题
查看>>
pfsense锁住自己
查看>>
vsftpd 相关总结
查看>>
bash complete -C command
查看>>
解决zabbix 3.0中1151端口不能运行问题
查看>>
售前工程师的成长---一个老员工的经验之谈
查看>>
Get到的优秀博客网址
查看>>
dubbo
查看>>
【Git入门之四】操作项目
查看>>
老男孩教育每日一题-第107天-简述你对***的理解,常见的有哪几种?
查看>>
Python学习--time
查看>>
在OSCHINA上的第一篇博文,以后好好学习吧
查看>>
高利率时代的结局,任重道远,前途叵测
查看>>