背景
前段时间收到运维反馈, 线上 MySQL 数据库凌晨时候出现慢查询的报警, 并把原始 sql 发了过来:
-- 去除了业务含义的 sql
update test_user set a=1 where id=1;
表数据量 200W 左右, 不是很大, 而且是根据主键更新.
问题排查
排查 MySQL 数据库
我看到 sql 后第一反应就是是不是数据库出问题了, 每个小时都有业务, 偏偏白天业务高峰时间段正常, 凌晨业务量很少时候出问题, 让运维先检查了数据库的状态, 反馈是数据库正常.
排查业务代码(第一次)
这块业务代码比较复杂, 而且是别人写的, 第一次看都没看完, 直接在代码里打印了各个模块执行的时间, 然后上线.
排查业务代码(第二次)
第二天又出现慢查询了, 我赶紧下载了线上日志, 发现整个方法执行时间很长, 然后发现执行时间长的代码有几行调用其他服务的代码, 使用的是 HttpClient, 猜到了原因, 应该是调用其他超时导致的.
说下系统整体流程, 微信 (A) 回调我们的收银台服务(B), 收银台更新订单信息并调用业务服务(C).
出问题原因是:
第一次 A 调用 B,B 锁住记录行并调用 C, 这个时候 C 没有响应, 导致 A 又发送了第二次请求.
第二次 A 调用 B,B 更新记录时候发生死锁, 出现慢查询.
解决方案
收银台系统 B 接收回调的方法添加分布式锁, 保证同一时刻同一订单只能更新一次.
收银台调用业务服务使用的是 HttpClient4.4, 默认超时时间 60 秒, 这么长时间如果对方没有响应就完了, 改成了 5 秒, 超时立马返回, 不影响其他业务.
HttpClient4.4 版本设置超时时间代码如下:
- HttpPost httpPost = new HttpPost(url);
- RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).setConnectionRequestTimeout(5000).build();
- httpPost.setConfig(requestConfig);
上面设置了 3 个超时时间, 含义如下:
setConnectTimeout: 设置连接超时时间, 单位毫秒.
setConnectionRequestTimeout: 设置从 connect Manager 获取 Connection 超时时间, 单位毫秒. 这个属性是新加的属性, 因为目前版本是可以共享连接池的.
setSocketTimeout: 请求获取数据的超时时间, 单位毫秒. 如果访问一个接口, 多少时间内无法返回数据, 就直接放弃此次调用.
最后, 按照这两个方案改造上线后, 系统运行正常, 问题解决.
来源: https://www.cnblogs.com/haha12/p/12580673.html