关于MySQL数据库连接超时问题及解决办法

发布于:2025-09-04 ⋅ 阅读:(11) ⋅ 点赞:(0)

前言

1 为什么要使用锁?和事务有什么关系

使用锁,是为了保证事务的正常进行。举一个例子 事务A 和事务B 都要对 student 表中的某一个字段进行操作。事务A 进行查询操作,而事务B,则是对数据进行修改,如果 我不加锁 约束的话,那么事务A 前后查询的数据 不一致。不符合事务的一致性【事务前后的状态保持一致】

2 锁等待超时是什么意思?

两个或多个事务同时操作数据库中表的某一个字段,但已经持有锁的事务 长时间没有提交,释放锁。导致被等待的事务,等待锁释放时间超过规定超时时间,而导致 数据库主动和事务失去联系

举一个例子: 事务A 和事务B 都要对 student 表中的某一个字段进行操作。事务A 进行查询操作,而事务B,则是对数据进行修改 我现在给事务A添加排他锁 【功能:只要有一个事务进来了,不允许其他事务再操作】 。正常情况下,只有等 事务A 查询结束,事务提交成功,事务B才可以修改。但问题是 事务A查询 时间或者说查询语句的执行时间,比较久,锁一直没有释放,导致 事务B 因为长时间等待锁释放,超时。


“锁等待超时的本质是:多个事务竞争同一把锁时,持有锁的事务未及时释放,等待锁的事务在超过数据库设定的「锁等待超时阈值」(如 InnoDB 的innodb_lock_wait_timeout)后,数据库为避免死锁扩散,主动终止等待事务并抛出错误(如 MySQL 错误码 1205)。”


问题:锁等待,超时


解决办法:

1 延长超时 时间 ,在配置数据源时

注意:延长超时时间,只是一个治标不治本的方法。只是通过延长事务等待锁的时间。究其根本,还是找到  事务为什么没有提交,锁为什么没有释放的原因。

如下图所示:


2  查看待办表的索引,同时为达成目的 比如说根据 AD_BI_NO 条件修改 ,可以添加索引

注意:使用索引,是为了提高查询效率,降低全表查询,锁的颗粒度。从之前的表级锁 降低为行级锁。

-------查看待办表的索引

show index from wf_approve;

----- 为条件【查询/修改】新增索引

CREATE INDEX idx_wf_approve_ad_bi_no ON wf_approve(AD_BI_NO);

场景1 :当新增索引后,“若执行UPDATE wf_approve SET status=1 WHERE AD_BI_NO='xxx',索引:idx_wf_approve_ad_bi_no 后,SQL 会精准锁对应行,减少锁等待。”

  • 新增索引后,用explain 关键字 查看 SQL 是否走索引(避免索引失效);
  •  比如说:执行 explain UPDATE wf_approve SET status=1 WHERE AD_BI_NO='xxx' 


3 debug 定位 + 锁查询 SQL
  •  明确 自己要做什么 ?

举一个例子:我需要先添加一条甩项数据,当甩项 工作流走完,触发反射方法,对其工作流节点和状态 进行调整,从01责任人 修改为 甩项。这是我的目的 这一过程中,通过debug 方式 ,检查每一个环节,观察具体是哪里, 导致数据库连接超时,同时
通过执行 ,下面的sql语句 ,观察 是哪个事务 和目标表 相关联 并且 正在持有锁,未释放。

-- 查看 mis_saas 数据库中持有锁和等待锁的详细信息

SELECT
    -- 数据库和表信息
    dl.OBJECT_SCHEMA AS 数据库名,
    dl.OBJECT_NAME AS 表名,
    -- 锁基本信息
    dl.LOCK_TYPE AS 锁类型,
    dl.LOCK_MODE AS 锁模式,
    dl.LOCK_STATUS AS 锁状态,
    dl.LOCK_DATA AS 锁定数据,
    -- 事务信息
    itr.trx_id AS 事务ID,
    itr.trx_mysql_thread_id AS 线程ID,
    itr.trx_started AS 事务开始时间,
    itr.trx_state AS 事务状态,
    itr.trx_query AS 执行的SQL语句,
    -- 锁等待相关信息
    dlw.REQUESTING_ENGINE_LOCK_ID AS 请求锁ID,
    dlw.BLOCKING_ENGINE_LOCK_ID AS 阻塞锁ID
FROM
    performance_schema.data_locks dl
        -- 关联事务表
        JOIN information_schema.INNODB_TRX itr
             ON CAST(itr.trx_id AS UNSIGNED) = dl.ENGINE_TRANSACTION_ID
        -- 关联锁等待关系表
        LEFT JOIN performance_schema.data_lock_waits dlw
                  ON dl.ENGINE_LOCK_ID = dlw.REQUESTING_ENGINE_LOCK_ID
                      OR dl.ENGINE_LOCK_ID = dlw.BLOCKING_ENGINE_LOCK_ID
WHERE
    dl.OBJECT_SCHEMA = 'mis_saas'  -- 只查询 mis_saas 数据库
ORDER BY
    itr.trx_started;  -- 按事务开始时间排序
  • 代码中,提到的数据表

  • 执行结果

比如 “锁状态为GRANTED的是持有锁的事务,WAITING的是等待锁的事务”;“锁定数据为1表示锁的是 id=1 的行”;
- 给出 “后续操作”:查到阻塞事务后,用KILL 线程ID(线程 ID 来自trx_mysql_thread_id)终止未释放锁的事务。


4 优化代码

比如;对一张表,查了很多次,改了很多次,都可以整合为一次去查询或修改,

5 如果一个功能中,涉及到很多表的操作,可以考虑,对一些操作,重新开一个新的事务

6 对于导致数据库连接超时的操作,可以考虑等主事务,事务提交结束,再执行

主事务提交后执行回调逻辑(如后续的数据库操作、消息发送等),可以通过 TransactionSynchronizationManager 注册事务同步器

注意:该方法基于 Spring 的事务同步机制,需在 Spring 项目中使用;

示例代码

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    @Override
    public void afterCommit() {
        // 更新和删除 wf_approve 表的操作
    }
});

优点:即使afterCommit方法中的sql语句 出现问题,也不会影响 主事务的结果
缺点:回调逻辑(更新 / 删除 wf_approve)不在原事务中,而是在事务提交后异步执行。
如果回调执行时,其他事务恰好需要操作 wf_approve 表的同一条记录,可能因锁竞争导致超时。


网站公告

今日签到

点亮在社区的每一天
去签到