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语句优化原则优化之后发现未达到预想的效果,问题如下:

  1. 在不优化的情况下自建数据库执行③slq语句比RDS数据库要快5倍以上;
  2. 优化之后的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执行计划结果比较差别如下:

  1. ECS ③优化前 sql 与 RDS ③优化前 执行计划结果无很大差异,但执行结果返回的时间分别是0.47 sec 和 5.77 sec(unbelievable);
  2. ECS ③优化后 sql 与 RDS ③优化后 执行计划结果相差非常大;
    1. ECS ③优化后 sql Extra : Using where(索引);Using index, rows 最大259行等
    2. 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性能优化分析 --- 下篇的主要内容,如果未能解决你的问题,请参考以下文章

(七) 下篇 Android 性能优化 Perfetto 详细介绍

MySQL大数据优化以及分解(下篇)

mysql 优化下

高频面试点:Android性能优化之内存优化(下篇)

4kw机柜无通道封闭CFD模拟分析及优化(下篇)

MySql查询性能优化必知必会