深入浅出Mysql - 优化篇(Sql语句)

定期优化表

如果已经删除了表的一大部分,或者如果已经对含有可变长度行的表(含有VARCHAR、BLOB或TEXT列的表)进行了很多更改,则应使用OPTIMIZE TABLE命令来进行表优化。?这个命令可以将表中的空间碎片进行合并,并且可以消除由于删除或者更新造成的空间浪费,但OPTIMIZE TABLE命令只对MylSAM、BDB和InnoDB表起作用。

mysql> analyze table fa_order;
+------------------------+---------+----------+----------+
| Table                  | Op      | Msg_type | Msg_text |
+------------------------+---------+----------+----------+
| zs_personcash.fa_order | analyze | status   | OK       |
+------------------------+---------+----------+----------+
1 row in set (0.03 sec)

对于InnoDB引擎的表来说,通过设置innodb_file_per_table参数,设置InnoDB为独立表空间模式,这样每个数据库的每个表都会生成一个宓立的云1文件,用于存储表的数据和索引,这样可以一定程度上减轻InnoDB表的空间回收问题。另外,在删除大量数据后,InnoDB表可以通过alter table但是不修改引擎的方式来回收不用的空间。

常用的SQL优化

优化Insert语句

  • 如果同时从同一客户插入很多行,应尽*使用多个值表的INSERT诺句,这种方式将大大编减客户端与数据库之间的连接、关闭等消耗,使得效率比分开执行的单个INSERT语句?快(在大部分情况下,使用多个值表的INSERT语句能比单个INSERT语句快上好几倍)。
	insert into table ( name , age , sex ) values ( 'stark', 30 , 1), ( 'yy', 30 , 2 ) ...
  • 如果从不同客户指入很多行,可以通过使用INSERT DELAYED#句得到更高的速度.?DELAYED的舍义是让INSERT语句马上执行,其实数据都敬放在内存的队列中,并没冇真正写入磁盘,这比毎条语句分别括入要快得多;LOW_PRIORITY R1好相反,在所有算他用户时表的读写完成后才进行插入。
  • 将索引文件和数据文件分在不同的磁盘上存放(利用建表中的选项)。
  • 如果进行批量插入,可以通过增加bulk_insert_buflfer_size变量值的方法来提高速度,但是,这只能对MylSAM表使用。
  • 当从一个文本丈件装载一个表时,使用LOAD DATA INFILE.这通常比使用很多INSERT语句快20倍。

优化Order By 语句

优化ORDER BY语句之前,首先来了解一下MySQL中的排序方式。

mysql> show index from fa_order;
+----------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name     | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| fa_order |          0 | PRIMARY      |            1 | id           | A         |     5080802 | NULL     | NULL   |      | BTREE      |         |               |
| fa_order |          1 | pay_status   |            1 | pay_status   | A         |          18 | NULL     | NULL   | YES  | BTREE      |         |               |
| fa_order |          1 | goodtype     |            1 | goodtype     | A         |          18 | NULL     | NULL   | YES  | BTREE      |         |               |
| fa_order |          1 | user_id      |            1 | user_id      | A         |     5080802 | NULL     | NULL   |      | BTREE      |         |               |
| fa_order |          1 | flag         |            1 | flag         | A         |          18 | NULL     | NULL   | YES  | BTREE      |         |               |
| fa_order |          1 | ordersn      |            1 | order_no     | A         |     5080802 | NULL     | NULL   | YES  | BTREE      |         |               |
| fa_order |          1 | consume_type |            1 | consume_type | A         |          18 | NULL     | NULL   | YES  | BTREE      |         |               |
| fa_order |          1 | f_addtime    |            1 | addtime      | A         |     1016160 | NULL     | NULL   | YES  | BTREE      |         | 查询统计钱    |
| fa_order |          1 | f_addtime    |            2 | flag         | A         |     1016160 | NULL     | NULL   | YES  | BTREE      |         | 查询统计钱    |
+----------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
9 rows in set (0.06 sec)

第一种通过有序索引顺序扫描直接返回有序数据,这种方式在使用explain分析查询的时候显示为Using Index,不需要额外的排序,操作效率较高。

mysql> explain select id from fa_order order by id;
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | fa_order | index | NULL          | PRIMARY | 8       | NULL | 5080802 | Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
1 row in set (0.06 sec)

第二种是通过对返回数据进行排序,也就是通常说的Filesort排序,所有不是通过索引直接返回排序结果的排序都叫Filesort排序。Filesort并不代表通过磁盘文件进行排序,而只是说明进行了Filesort排序操作,至于排序操作是否使用了磁盘文件或临时表等,则取决于MySQL服务器对排序参数的设置和需要排序数据的大小。

mysql> explain select * from fa_order order by goodtype;
+----+-------------+----------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows    | Extra          |
+----+-------------+----------+------+---------------+------+---------+------+---------+----------------+
|  1 | SIMPLE      | fa_order | ALL  | NULL          | NULL | NULL    | NULL | 5080802 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+---------+----------------+
1 row in set (0.05 sec)

Filesort是通过相应的排序算法,将取得的数据在sort_buffer_size系统变量设置的内存排序区中进行排序,如果内存装载不下,它就会将磁盘上的魏进行分块,再对各个数据块进行排序,然后将各个块合并成有序的结果集。sort_bufifer_size设置的排序区是每个线程独占的,所以同一个时刻,MySQL中存在多个sort buff排序区。

了解了MySQL排序的方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。WHERE条件和ORDER BY使用相同的索引,并且ORDER BY的顺序和索引顺序相同,并且ORDER BY的字段都是升序或者都是降序。否则肯定需要额外的排序操作,这样就会出现Filesort。

Filesort 的优化

通过创建合适的索引能够减少Filesort出现,但是在某些情况下,条件限制不能让Filesort消失,那就需要想办法加快Filesort的操作。对于Filesort, MySQL有两种排序算法。

两次扫描算法(Two Passes):首先根据条件取出排序字段和行指针信息,之后在排序?区sort buffer中排序如果排序区sort buffer不够,则在临时表Temporary Table中存储排序结果.完成排序后根据行指针回表读取记录.该算法是MySQL4.1之前采用的算法,需要两次访问数据,第一次获取排序字段和行指针信息,第二次根据行指针获取记录,尤其是第二次读取操作可能导致大量随机I/O操作;优点是排序的时候内存开销较少。

一次扫描算法(Single Pass): 一次性取出満足条件的行的所有字段,然后在排序区sort buffer中排序后直接输出结果集.排序的时候内存开销比较大,但是排序效率比两次扫描算法美高。

MySQL通过比较系统变量max_lengfli_for_sort_data的大小和Query语句取出的字段总大小来判断使用哪种排序算法。如果max_length_for_sort_data更大,那么使用第二种优化之后的算法;否则使用第一种算法。

适当加大系统变量max_length_fbr_sort_data的值,能够让MySQL选择更优化的Filesort排序算法。当然,假如max_length_fdr_sort_data设置过大,会造成CPU利用率过低和磁盘I/O?过高,CPU和I/O利用平衡就足够了。

适当加大sort_buffer_size排序区,尽量让排序在内存中完成,而不是通过创建临时表放在文件中进行;当然屈不能无限制加大sort_buflfer_size排序区,因为sort_buffbr_size参数是每个线程独占的,设置过大,会导致服务器SWAP要考虑数据库活动连接成和服务器内存的?大小来适当设置排序区。

尽量只使用必要的字段,SELECT具体的字段名称,而不是SELECT*选择所有字段,这样可以减少排序区的使用,提高SQL性能。

优化Group By 语句

默认情况下,MySQL对所有GROUP BY coll,col2,…的字段进行排序。这与在查询中指定 ORDER BY coll,co!2,…类似。因此,如果显式包括包含相同列的ORDER BY子句,则对MySQL的实际执行性能没有什么影响。

如果查询包括GROUP BY但用户想要避免排序结果的消耗测可以指定ORDER BY NULL 禁止排序。

优化嵌套查询

MySQL 4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的?查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性地完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)替代。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章