慢Sql不一定是数据库或者Sql本身的问题,有可能是网络链路的问题
目录
本文主要分享一次由网络链路引起的慢Sql问题,慢Sql除了索引和Sql语句本身的问题,也有可能是由网络链路引起的。
1. 问题描述
- 近期生产服务器系统频繁报Sql超时和死锁的错误
Lock wait timeout exceeded; try restarting transaction。 - 根据错误日志定位,定位到一个关于用户金额更新的方法。
- 这个方法主要做了两件事情,开启事务,更新用户金额然后往记录表插入一条数据。
- 查询慢Sql日志,确认了是更新用户金额的update语句超时了。然后懵逼的就是这个Sql过滤条件是主键,explain这个update语句type是range级别的,没道理会超时。
- 再次查看日志,是多个事务并发,单次事务执行时间要好几秒,前面的行锁持有时间过长,导致后续的事务没获得锁超时了。
那么问题来了,一张数据量不算太大的表(20w以内),update语句在过滤条件为表主键的情况下,为什么还会超时?
2. 一次远程Sql执行的流程
2.1. 网络链路可能导致慢 SQL 的各个环节
|
|
2.2. 远程调用SQL的完整流程
sequenceDiagram
participant Client as 客户端/浏览器
participant App as 应用服务器
participant Network as 网络链路
participant DB as 数据库服务器
title UPDATE语句的网络链路耗时分析
%% 第一部分:请求进入
Note over Client,DB: 阶段一:请求接收与事务开始
Client->>App: HTTP POST/PUT请求
App->>App: 业务验证与处理(15ms)
App->>Network: 开始事务 BEGIN
%% 第二部分:查询数据(网络往返1)
Note over Client,DB: 阶段二:查询要更新的数据
App->>Network: SELECT查询(查数据)
Network->>DB: 查询请求(30ms延迟)
DB->>DB: 执行查询(5ms)
DB->>Network: 返回结果数据
Network->>App: 数据返回(30ms延迟)
%% 第三部分:业务逻辑处理
App->>App: 业务计算与数据组装(10ms)
%% 第四部分:执行UPDATE(网络往返2)
Note over Client,DB: 阶段三:执行UPDATE操作
App->>Network: UPDATE语句
Network->>DB: UPDATE请求(30ms延迟)
DB->>DB: 获取行锁(5ms)
DB->>DB: 执行更新(8ms)
DB->>Network: 返回影响行数
Network->>App: 结果返回(30ms延迟)
%% 第五部分:可能的额外操作
Note over Client,DB: 阶段四:更新相关数据
App->>Network: 更新关联表/计数器
Network->>DB: 第二个UPDATE(30ms延迟)
DB->>DB: 执行更新(3ms)
DB->>Network: 返回结果
Network->>App: 结果返回(30ms延迟)
%% 第六部分:提交事务
Note over Client,DB: 阶段五:事务提交
App->>Network: COMMIT提交
Network->>DB: 提交请求(30ms延迟)
DB->>DB: 写入redo log(3ms)
DB->>DB: 释放锁(2ms)
DB->>Network: 提交完成
Network->>App: 提交确认(30ms延迟)
%% 第七部分:响应返回
App->>App: 组装响应(5ms)
App->>Client: 返回HTTP响应(200 OK)
%% 第八部分:分析对比
Note over App,DB: 耗时分析
App->>DB: 理想情况(本地执行)
DB-->>App: 总耗时:BEGIN(1ms)+SELECT(5ms)+UPDATE(13ms)+COMMIT(5ms)=24ms
App->>DB: 实际情况(远程+网络延迟)
DB-->>App: 总耗时:24ms + 网络(30ms×6往返×2)=24ms+360ms=384ms
sequenceDiagram
participant Client as 客户端/浏览器
participant App as 应用服务器
participant Network as 网络链路
participant DB as 数据库服务器
title UPDATE语句的网络链路耗时分析
%% 第一部分:请求进入
Note over Client,DB: 阶段一:请求接收与事务开始
Client->>App: HTTP POST/PUT请求
App->>App: 业务验证与处理(15ms)
App->>Network: 开始事务 BEGIN
%% 第二部分:查询数据(网络往返1)
Note over Client,DB: 阶段二:查询要更新的数据
App->>Network: SELECT查询(查数据)
Network->>DB: 查询请求(30ms延迟)
DB->>DB: 执行查询(5ms)
DB->>Network: 返回结果数据
Network->>App: 数据返回(30ms延迟)
%% 第三部分:业务逻辑处理
App->>App: 业务计算与数据组装(10ms)
%% 第四部分:执行UPDATE(网络往返2)
Note over Client,DB: 阶段三:执行UPDATE操作
App->>Network: UPDATE语句
Network->>DB: UPDATE请求(30ms延迟)
DB->>DB: 获取行锁(5ms)
DB->>DB: 执行更新(8ms)
DB->>Network: 返回影响行数
Network->>App: 结果返回(30ms延迟)
%% 第五部分:可能的额外操作
Note over Client,DB: 阶段四:更新相关数据
App->>Network: 更新关联表/计数器
Network->>DB: 第二个UPDATE(30ms延迟)
DB->>DB: 执行更新(3ms)
DB->>Network: 返回结果
Network->>App: 结果返回(30ms延迟)
%% 第六部分:提交事务
Note over Client,DB: 阶段五:事务提交
App->>Network: COMMIT提交
Network->>DB: 提交请求(30ms延迟)
DB->>DB: 写入redo log(3ms)
DB->>DB: 释放锁(2ms)
DB->>Network: 提交完成
Network->>App: 提交确认(30ms延迟)
%% 第七部分:响应返回
App->>App: 组装响应(5ms)
App->>Client: 返回HTTP响应(200 OK)
%% 第八部分:分析对比
Note over App,DB: 耗时分析
App->>DB: 理想情况(本地执行)
DB-->>App: 总耗时:BEGIN(1ms)+SELECT(5ms)+UPDATE(13ms)+COMMIT(5ms)=24ms
App->>DB: 实际情况(远程+网络延迟)
DB-->>App: 总耗时:24ms + 网络(30ms×6往返×2)=24ms+360ms=384ms
sequenceDiagram
participant Client as 客户端/浏览器
participant App as 应用服务器
participant Network as 网络链路
participant DB as 数据库服务器
title UPDATE语句的网络链路耗时分析
%% 第一部分:请求进入
Note over Client,DB: 阶段一:请求接收与事务开始
Client->>App: HTTP POST/PUT请求
App->>App: 业务验证与处理(15ms)
App->>Network: 开始事务 BEGIN
%% 第二部分:查询数据(网络往返1)
Note over Client,DB: 阶段二:查询要更新的数据
App->>Network: SELECT查询(查数据)
Network->>DB: 查询请求(30ms延迟)
DB->>DB: 执行查询(5ms)
DB->>Network: 返回结果数据
Network->>App: 数据返回(30ms延迟)
%% 第三部分:业务逻辑处理
App->>App: 业务计算与数据组装(10ms)
%% 第四部分:执行UPDATE(网络往返2)
Note over Client,DB: 阶段三:执行UPDATE操作
App->>Network: UPDATE语句
Network->>DB: UPDATE请求(30ms延迟)
DB->>DB: 获取行锁(5ms)
DB->>DB: 执行更新(8ms)
DB->>Network: 返回影响行数
Network->>App: 结果返回(30ms延迟)
%% 第五部分:可能的额外操作
Note over Client,DB: 阶段四:更新相关数据
App->>Network: 更新关联表/计数器
Network->>DB: 第二个UPDATE(30ms延迟)
DB->>DB: 执行更新(3ms)
DB->>Network: 返回结果
Network->>App: 结果返回(30ms延迟)
%% 第六部分:提交事务
Note over Client,DB: 阶段五:事务提交
App->>Network: COMMIT提交
Network->>DB: 提交请求(30ms延迟)
DB->>DB: 写入redo log(3ms)
DB->>DB: 释放锁(2ms)
DB->>Network: 提交完成
Network->>App: 提交确认(30ms延迟)
%% 第七部分:响应返回
App->>App: 组装响应(5ms)
App->>Client: 返回HTTP响应(200 OK)
%% 第八部分:分析对比
Note over App,DB: 耗时分析
App->>DB: 理想情况(本地执行)
DB-->>App: 总耗时:BEGIN(1ms)+SELECT(5ms)+UPDATE(13ms)+COMMIT(5ms)=24ms
App->>DB: 实际情况(远程+网络延迟)
DB-->>App: 总耗时:24ms + 网络(30ms×6往返×2)=24ms+360ms=384mssequenceDiagram
participant Client as 客户端/浏览器
participant App as 应用服务器
participant Network as 网络链路
participant DB as 数据库服务器
title UPDATE语句的网络链路耗时分析
%% 第一部分:请求进入
Note over Client,DB: 阶段一:请求接收与事务开始
Client->>App: HTTP POST/PUT请求
App->>App: 业务验证与处理(15ms)
App->>Network: 开始事务 BEGIN
%% 第二部分:查询数据(网络往返1)
Note over Client,DB: 阶段二:查询要更新的数据
App->>Network: SELECT查询(查数据)
Network->>DB: 查询请求(30ms延迟)
DB->>DB: 执行查询(5ms)
DB->>Network: 返回结果数据
Network->>App: 数据返回(30ms延迟)
%% 第三部分:业务逻辑处理
App->>App: 业务计算与数据组装(10ms)
%% 第四部分:执行UPDATE(网络往返2)
Note over Client,DB: 阶段三:执行UPDATE操作
App->>Network: UPDATE语句
Network->>DB: UPDATE请求(30ms延迟)
DB->>DB: 获取行锁(5ms)
DB->>DB: 执行更新(8ms)
DB->>Network: 返回影响行数
Network->>App: 结果返回(30ms延迟)
%% 第五部分:可能的额外操作
Note over Client,DB: 阶段四:更新相关数据
App->>Network: 更新关联表/计数器
Network->>DB: 第二个UPDATE(30ms延迟)
DB->>DB: 执行更新(3ms)
DB->>Network: 返回结果
Network->>App: 结果返回(30ms延迟)
%% 第六部分:提交事务
Note over Client,DB: 阶段五:事务提交
App->>Network: COMMIT提交
Network->>DB: 提交请求(30ms延迟)
DB->>DB: 写入redo log(3ms)
DB->>DB: 释放锁(2ms)
DB->>Network: 提交完成
Network->>App: 提交确认(30ms延迟)
%% 第七部分:响应返回
App->>App: 组装响应(5ms)
App->>Client: 返回HTTP响应(200 OK)
%% 第八部分:分析对比
Note over App,DB: 耗时分析
App->>DB: 理想情况(本地执行)
DB-->>App: 总耗时:BEGIN(1ms)+SELECT(5ms)+UPDATE(13ms)+COMMIT(5ms)=24ms
App->>DB: 实际情况(远程+网络延迟)
DB-->>App: 总耗时:24ms + 网络(30ms×6往返×2)=24ms+360ms=384ms
由上图可知,远程的一次update语句或者说一次事务,不是单纯的把Sql甩给服务器就行了,期间有多次交互:
- 服务端的数据库建立远程连接
- 服务端开启事务请求
- 服务端发起update请求
- 数据库执行update语句,获得行锁,执行更新,返回影响行数
- 服务端接收结果进行确认,提交事务
- 数据库写入redo log,释放行锁,返回结果
若服务器与数据库存在巨大网络延迟,这多次交互累加起来的时间可能要数秒之多,会导致我遇到的这个问题。
3. 关键结论
3.1. UPDATE比SELECT更容易受网络影响
-
事务开销:需要BEGIN、COMMIT的额外网络往返
-
锁竞争:网络延迟会延长锁持有时间,加剧锁竞争
-
重试机制:乐观锁、超时重试会倍增网络请求
-
数据一致性:可能需要更新多个表,增加网络交互
3.2. 快速判断公式
如果:UPDATE本地执行快 + 应用端执行慢 + 网络延迟高 那么:很可能是网络链路问题
解决方法优先级:
- 减少网络往返次数(批量、存储过程)
- 降低网络延迟(同机房、专线)
- 异步化处理(消息队列)
- 优化事务范围(减少锁时间)