- mysql优化
1 尽力避免使结果返回额外的行和列,这样传输过程需要浪费时间
2 尽量避免在select中使用*
3 反复多次修改一个查询,获取了更多不需要的数据并不是个好主意,更好的建议是写新查询来代替已有查询来满足业务需要。
- mysql执行路径
首先是Query Cache,采用严格的查询匹配,Query Cache中存放查询语句以及结果集。
然后是解析器,主要是将查询语句分解为多个单词,操作符和内建树。
然后是预处理器,这里主要对权限和语句语义进行检查。
最后是查询优化器,将从解析器,预处理器得到的解析树转换成执行计划。优化器会从多个方案中选择一个合适的执行计划。
最后是查询执行引擎,负责执行执行计划。
了解查询状态:
1 show full processlist 如果登陆的用户具有super权限,那么只能显示本链接的线程状态。每个进程可能有多个状态。
Sleep Query Locked Analyzing, Statistics Copying to tmp table Sorting result Sending data
http://dev.mysql.com/doc/refman/5.7/en/general-thread-states.html
在进行调优时,尝试执行查询以检查其不同状态,了解执行所处的大致情况。
问题: 无法显示全局,即使使用root登陆。
2 explain select语句 使用explain查看select语句是如何执行。
3 使用show warnings查看经过重构之后的查询语句是怎么样的?貌似无法使用了?
- 语句查询优化。
查询电影长度小于50的,如果不建立索引,那么将是全表查询如1
修改表使其增加索引,注意add index后面不需要加上on。如2
再次执行select,查询长度小于50的,发现possible_keys是len,真正使用的也是len。
但是再次执行select,查询长度小于100的,发现possible_keys是len,真正却没有去使用len索引,采用的仍然是全表扫描。
上面查询优化的例子告诉我们建立索引的时候查询优化器不一定使用他们。需要根据实际使用情况进行分析。
1 mysql> explain extended select * from film where length<50; +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | film | ALL | len | NULL | NULL | NULL | 1142 | 33.01 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
2 mysql> alter table film add index len(length); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 3 mysql> explain extended select * from film where length<50; +----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | film | range | len | len | 3 | NULL | 27 | 100.00 | Using where | +----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 4 mysql> explain extended select * from film where length<100; +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | film | ALL | len | NULL | NULL | NULL | 1142 | 33.01 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql>
- 单个复杂查询以及多个简单查询比较
简单查询以及复杂查询不可以简单地认为单个由于复杂或者复杂优于单个。需要结合具体的业务需求来进行。
但是如果查询数据上有索引,那么通常几个数据集小的查询的union会比简单地查询会更快,因为如果数据集合过大,那么就会考虑不去使用索引,而采用直接扫描的方式。
如果简单地使用length<80,那么在length的possible_keys,但是实际却没有使用len作为索引。 mysql> explain select * from film where length<80; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | film | ALL | len | NULL | NULL | NULL | 1142 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | Last_query_cost | 240.399000 | +-----------------+------------+ 1 row in set (0.00 sec) 可以知道上一次的查询cost为240多。 如果采用多个集合的union来进行,那么就会得到比较好的效果。 mysql> explain select * from film where length<60; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | film | range | len | len | 3 | NULL | 95 | Using where | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | Last_query_cost | 134.009000 | +-----------------+------------+ 1 row in set (0.00 sec) mysql> explain select * from film where length between 60 and 80; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | film | range | len | len | 3 | NULL | 157 | Using where | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | Last_query_cost | 220.809000 | +-----------------+------------+ 1 row in set (0.00 sec) mysql> explain select * from film where length<60 union select * from film where length between 60 and 80; +----+--------------+------------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+-------+---------------+------+---------+------+------+-------------+ | 1 | PRIMARY | film | range | len | len | 3 | NULL | 95 | Using where | | 2 | UNION | film | range | len | len | 3 | NULL | 157 | Using where | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+-------+---------------+------+---------+------+------+-------------+ 3 rows in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | Last_query_cost | 0.000000 | +-----------------+----------+ 1 row in set (0.00 sec) 如此拆分成两个查询,那么可以看到数据集变小了,但是两者加起来134+220大于240,这主要是数据在传输的过程中花费了大量时间,如果吧这两个集合结合到一块,那么可以发现消耗时间 变得很小为0。
- mysq join连接顺序对性能的影响。
inner join对连接的顺序没有影响,但是对outter join的连接顺序有着影响,查询优化器生成的执行计划可能不太一样。
mysql> explain extended select * from film f left join film_actor fa on fa.film_id=f.film_id left join film_category fc on fc.film_id=f.film_id; +----+-------------+-------+------+----------------+----------------+---------+------------------+------+----------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------+----------------+----------------+---------+------------------+------+----------+-------+ | 1 | SIMPLE | f | ALL | NULL | NULL | NULL | NULL | 1142 | 100.00 | | | 1 | SIMPLE | fa | ref | idx_fk_film_id | idx_fk_film_id | 2 | sakila.f.film_id | 1 | 100.00 | | | 1 | SIMPLE | fc | ref | PRIMARY | PRIMARY | 2 | sakila.f.film_id | 1 | 100.00 | | +----+-------------+-------+------+----------------+----------------+---------+------------------+------+----------+-------+ 3 rows in set, 1 warning (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+-------------+ | Variable_name | Value | +-----------------+-------------+ | Last_query_cost | 2524.399000 | +-----------------+-------------+ 1 row in set (0.00 sec) mysql> explain extended select * from film_actor fa left join film_category fc on fc.film_id=fa.film_id left join film f on fa.film_id=f.film_id; +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+----------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+----------+-------+ | 1 | SIMPLE | fa | ALL | NULL | NULL | NULL | NULL | 5482 | 100.00 | | | 1 | SIMPLE | fc | ref | PRIMARY | PRIMARY | 2 | sakila.fa.film_id | 1 | 100.00 | | | 1 | SIMPLE | f | eq_ref | PRIMARY | PRIMARY | 2 | sakila.fa.film_id | 1 | 100.00 | | +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+----------+-------+ 3 rows in set, 1 warning (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+--------------+ | Variable_name | Value | +-----------------+--------------+ | Last_query_cost | 12071.399000 | +-----------------+--------------+ 1 row in set (0.00 sec)
表的链接顺序对最终的执行花费有影响,可以通过show warnings;来查看执行计划
- 聚集函数的优化
聚集函数的优化理由是,使用聚集函数的时候可能没有使用索引,使用order by时候使用了索引,这样使用了索引会导致查询更快一些。(视频)
在mysql 5.6下测试,发现两者均使用了索引。
mysql> explain select min(film_id) from film where length=100; +----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+ | 1 | SIMPLE | film | ref | len | len | 3 | const | 12 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+ 1 row in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | Last_query_cost | 3.405711 | +-----------------+----------+ 1 row in set (0.00 sec) mysql> explain select * from film where length=100 order by film_id limit 1; +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | 1 | SIMPLE | film | ref | len | len | 3 | const | 12 | Using where | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ 1 row in set (0.00 sec) mysql> show status like 'last_query_cost'; +-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | Last_query_cost | 14.399000 | +-----------------+-----------+ 1 row in set (0.00 sec)
- group by查询语句
根据不同字段进行group by,如果结果集相同,但是查询的效果可呢过不相同。group by title,length == group by film_id
结果fim_id用到了索引,意味着更快一些。
- limit返回结果集
select * from table limit by offset,count
是从offset+1到offset+count行
- union以及union all结果集区分
union会对合并的结果集进行remove,去除重复的元素。
union all则不会去去除重复元素。
- 不等号以及不等号
不等号会影响索引的使用,应该尽量使用等号来进行查询。
- 最佳实践经验
• 使用EXPLAIN语法检查查询执行计划
– 查看索引的使用情况
– 查看行扫描情况
• 当需要获取唯一一行时,请使用LIMIT 1语法
– 对如MIN或MAX等聚合计算操作有帮助
• 避免使用SELECT *
– 这会导致表的全扫描
– 网络带宽会被浪费
将DELECT, UPDATE或INSERT中的查询语句解构成多个更小查询
• 多表列选用适当的数据类型
– 更小的列有助于更佳的性能
• MySQL Query Cache是大小写和空格敏感的
– 请使用严格相同的语句来进行重复查询
• WHERE语法中的列加索引(之前的例子)
• 对JOIN的表列加索引(之前的例子)
• 避免使用<>,因为这会导致索引不被使用
• 如果不存在重复行数据,请使用UNION ALL替代UNION
现实中有许多方法可以查询出相同的结果集。往往一种调优方法对某个查询
有效时,而对于某个其他查询,则需要其他方法来处理。
• 在部署到生产环境前,请尽量多地在开发环境中模拟现实数据情景,测试你
的查询,以得出最佳方案。