mysql性能优化分析 --- 下篇
Posted q兽兽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql性能优化分析 --- 下篇相关的知识,希望对你有一定的参考价值。
概要回顾
之前看过《高性能mysql》对mysql数据库有了系统化的理解,虽然没能达到精通,但有了概念,遇到问题时会有逻辑条理的分析;
这回继上次sql分析结果的一个继续延伸分析,我拿了;
备注:分析(除sql基本优化过程外)这个过程并定位到具体问题,给出针对性的解决方案,是一个非常漫长的过程,需要一个个的去排除和论证的过程;当对来说十分枯燥和窘境,但成功了就是一非常大的收获和经验;
-- ③ 优化前 select p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.oss_volume as userOSSCapacity,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string FROM poehistoryinfo p LEFT JOIN recordregistryinfo r ON p.record_registry_info_id=r.id LEFT JOIN identityinfo i ON i.account_id=r.mgmt_account_id where r.contract_address=? GROUP BY r.contract_address,p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string,p.oss_volume ORDER BY p.block_time DESC limit ?,? -- ③ 优化后 SELECT p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.oss_volume as userOSSCapacity,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string FROM poehistoryinfo p INNER JOIN (SELECT p1.id FROM poehistoryinfo p1 LEFT JOIN recordregistryinfo r ON p1.record_registry_info_id=r.id LEFT JOIN identityinfo i ON i.account_id=r.mgmt_account_id where r.contract_address=? ORDER BY p1.block_time DESC limit ?,?) a ON a.id = p.id ;
--- 环境RDS 1C 1G mysql数据库,导入数据库,这时没有其他多余的IO读写操作 这回我申请了一个RDS 1C 1G mysql数据库,导入数据库,这时没有其他多余的IO读写操作; 效果:2条sql语句, ①优化前sql, 5.77ms ②优化后sql, 0.01ms 执行计划正常 --- 环境ECS 1C 1G自建数据库这是没有多余操作,这时没有其他多余的IO读写操作 ECS 自建数据库 1C 1G 这是没有多余操作, 效果:2条sql语句, ①优化前sql, 1.77ms ②优化后sql, 0.01ms 执行计划正常 --- 环境RDS 1C 1G mysql数据库,线上库,这时IOPS平均操作为300~400之间 我司生产上RDS 1C 1G 这时平均读写操作为300-400; 效果:2条sql语句, ①优化前sql, 5.89ms ②优化后sql, 6.35ms,不正常
问题
按mysql语句优化原则优化之后发现未达到预想的效果,问题如下:
- 在不优化的情况下自建数据库执行③slq语句比RDS数据库要快5倍以上;
- 优化之后的sql语句放到RDS上,比之前未优化的sql语句还慢;
分析思路
为了发现问题所以,我获取RDS 上的sql执行计划:
mysql> explain select p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.oss_volume as userOSSCapacity,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string FROM poehistoryinfo p LEFT JOIN recordregistryinfo r ON p.record_registry_info_id=r.id LEFT JOIN identityinfo i ON i.account_id=r.mgmt_account_id where r.contract_address=‘85b692c81d16bfa49ce0b8a166458c71f522fd5b‘ GROUP BY r.contract_address,p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string,p.oss_volume ORDER BY p.block_time DESC limit 1,25; id FROM poehistoryinfo p1 LEFT JOIN recordregistryinfo r ON p1.record_registry_info_id=r.id LEFT JOIN identityinfo i ON i.account_id=r.mgmt_account_id where r.contract_address=‘85b692c81d16bfa49ce0b8a166458c71f522fd5b‘ ORDER BY p1.block_time DESC limit 1,25; +----+-------------+-------+------------+------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ | 1 | SIMPLE | r | NULL | ALL | PRIMARY | NULL | NULL | NULL | 39 | 10.00 | Using where; Using temporary; Using filesort | | 1 | SIMPLE | i | NULL | ref | identityinfo_account_id_ind | identityinfo_account_id_ind | 153 | sync.r.mgmt_account_id | 1 | 100.00 | Using where; Using index | | 1 | SIMPLE | p | NULL | ALL | NULL | NULL | NULL | NULL | 172999 | 10.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ 3 rows in set, 1 warning (0.01 sec)
mysql> explain SELECT p.record_data,p.cooperation_name,p.cooperation_id,p.block_time,p.oss_volume as userOSSCapacity,p.txhash,p.seq_id,p.pre_seq_id,p.custom_string FROM poehistoryinfo p INNER JOIN (SELECT p1.id FROM poehistoryinfo p1 LEFT JOIN recordregistryinfo r ON p1.record_registry_info_id=r.id LEFT JOIN identityinfo i ON i.account_id=r.mgmt_account_id where r.contract_address=‘85b692c81d16bfa49ce0b8a166458c71f522fd5b‘ ORDER BY p1.block_time DESC limit 1,25) a ON a.id = p.id ; +----+-------------+------------+------------+--------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+--------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 26 | 100.00 | NULL | | 1 | PRIMARY | p | NULL | eq_ref | PRIMARY | PRIMARY | 4 | a.id | 1 | 100.00 | NULL | | 2 | DERIVED | r | NULL | ALL | PRIMARY | NULL | NULL | NULL | 39 | 10.00 | Using where; Using temporary; Using filesort | | 2 | DERIVED | i | NULL | ref | identityinfo_account_id_ind | identityinfo_account_id_ind | 153 | sync.r.mgmt_account_id | 1 | 100.00 | Using where; Using index | | 2 | DERIVED | p1 | NULL | ALL | NULL | NULL | NULL | NULL | 172999 | 10.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+------------+------------+--------+-----------------------------+-----------------------------+---------+------------------------+--------+----------+----------------------------------------------------+ 5 rows in set, 1 warning (0.00 sec)
根据上面的分析和上篇ECS sql执行计划结果比较差别如下:
- ECS ③优化前 sql 与 RDS ③优化前 执行计划结果无很大差异,但执行结果返回的时间分别是0.47 sec 和 5.77 sec(unbelievable);
- ECS ③优化后 sql 与 RDS ③优化后 执行计划结果相差非常大;
- ECS ③优化后 sql Extra : Using where(索引);Using index, rows 最大259行等
- RDS ③优化后 sql Extra : 比较多Using where;Using temporary;Using filesort;Using where;Using index;Using buffer(Block Nested Loop); rows 中有172999(相当于全表查询)
分析术语名词说明
Using index:表示MySQL将使用覆盖索引
Using where: 表示使用存储引擎检索索引在进行过滤
Using temporary: 表示MySQL对查询结果排序时会使用一个临时表
Using filesort: 表示Mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取
Using buffer(Block Nested Loop): 关联嵌套查询
分析以上结果,很明显RDS 优化后的效果非常不佳,甚至RDS优化前还差,使用时间还长; 而优化语句是按照Mysql 优化器和索引的基本原则来的,除非是唯一能想到的是两个mysql环境与配置存在差异导致的。 按照这个思路,结合RDS工单处理工程师, 去查询语句如下
- show profiles;
- show global variables like ‘%query_cache%‘;
- show variables like ‘%optimizer_switch%‘ G;
- show create table 表明 G;
- show variables;
step 1:分析show profiles,发现主要耗时时间在sending data上,但无法定位,猜测可能是缓存或者是乐观锁之类的问题;
step 2:分析%query_cache%发现query_cache是没有开启的;
step 3:分析table表是否存在差异,貌似没有...排除
step 4:分析show variables;猜测rds的"tmpdir","/home/mysql/data3001/tmp",ecs自建的是| tmpdir | /tmp 会对Using temporary; Using filesort 操作有影响;怀疑/tmp 是属于内存盘,所以ECS会比RDS性能更优,但当我用df -h 看一下,发现并没有这种情况,再由RDS工程师查看ubuntu文档对其验证,发现假设不成立;
step 5: 之前是试过使用强制索引来改变优化后的sql语句的执行计划,但并没有多大的用处。RDS工程师分析认为问题出在IOPS(Input/Output Operations Per Second)上,因为RSD平均IOPS有300-400,而ECS则是0;为了排除这个原因RDS工程师很大方的建了一个1C1G RDS MySQL给我测试使用。优化后的问题不在出现,而优化前的SQL语句还是有问题:ECS的执行时间比RDS的块10多倍;
不过有一点可以确定,但重复执行多次sql语句时,执行返回结果时间会缩短,一直到一个基本固定不变的值,这说明查询存在缓存, 目前给出的解决方案是修改RDS配置如下:
set tmp_table_size=50*1024*1024*2; set max_heap_table_size=50*1024*1024*2; set join_buffer_size=50*1024*1024; set sort_buffer_size=50*1024*1024;
其实,我认为这个语句无论是否有效,对RDS的用户而言都是非常差的一个表现,还需要认为的去深度考虑mysql的配置参数变量,从某种意义上说就是DB owner,那我们又何必要使用RDS呢。
以上是关于mysql性能优化分析 --- 下篇的主要内容,如果未能解决你的问题,请参考以下文章