MySQL 5.6 5.7 如何自定义行锁超时等待(wait timeout)时间 innodb_lock_wait_timeout

1. 问题

最近由于业务的需要,写了个基于数据库锁机制的分布式调度简易框架,用于处理业务中的补偿任务和定时轮询任务。由于调度任务处理时间长短不一,有些是几秒处理完,而有些需要好几分钟。对于处理时间较长的任务,在事务中,行锁会一直占据直到事务处理结束。

那么问题就来了,对于时间处理较长的任务,再下一轮调度起来之前事务还没处理完,这个时候,下个事务会在行锁上一直等待下去,直到timeout。这样会占据多个线程、消耗多数数据库资源。

2. 解决思路、方案

思路也很简单,当检测到数据库行锁被占据时,当前线程立马结束,不进行锁释放等待,这样既节约线程资源也节省数据库资源。

我们知道,MySQL8是支持 'SELECT ... for update NOWAIT' 这种写法的,但是对于5.x版本这种写法并不支持(图 - 1),恰巧我们用的是5.6版本,我们需要另外想办法来解决。

图 - 1

思路1: 在MYSQL系统变量里面(global variable, session variable)中有特定的变量用来控制这个行锁超时等待时间,我们可以修改这变量值,来使得我们的等待超时时间缩短

思路2: 比较好的方案,我们只修改当前会话的,而不修改全局变量值(有可能多个程序在连你的数据库)

思路3: 到底哪个变量控制这行锁的超时等待时间呢?通过网上查询,我们可以知道有一个叫 'innodb_lock_wait_timeout' 的变量正是我们要寻找的东东。 MySQL对于超时相关的变量有很多,大家可以查询information_scheme.global/session_variables表 (5.7版本略微不同,需要在performance_schema去查询):

select * from information_schema.global_variables where variable_name like '%timeout%';

你会看到有很多相关的变量,每个变量的含义,大家可以网上查找一下,了解一下很有必要!!这里我们只关心 「innodb_lock_wait_timeout」这个变量,默认值是 50秒,表示: 行锁等待超时时间是50秒。

图 - 2

好了,问题根源我们也找到,现在的问题是怎么做?!

思路1:数据库的操作流程其实也挺直接明了的:

定义DataSource -> 从DS池中获取Connection -> 通过connection执行SQL

涉及到事务的,其实也就是在第二步获取connection后在执行第三部前,执行事务相关操作(在动态代理中),比如:执行 begin语句(start transaction语句效果也一样)

思路2:我们能否在Connection创建的时候,就把 innodb_lock_wait_timeout的值给修改了呢?答案是可以的。

Springboot中默认的数据库连接池用的是 Hikari,而它刚好有可配置属性:connectionInitSql,通过代码跟踪,可以知道这个SQL在创建connection的时候会被执行。这个正好是我们想要的!!!!好了,其他不多说了,上代码吧!!

通过 SET语句,我们可以设置SESSION级别的值(图 - 3)

图 - 3

3. 测试用例

图 - 4

4. 总结

这个问题,项目中存在有一段时间了,一直都没有时间沉下心来研究,这周趁着项目空闲期终于搞出了这个方案,下周上线,线上观察一段时间。

5. 附图

有同学问及到有关方法的代码,现黏贴一下:

lockAgain()方法

附图1

lockRowFor1000Seconds()方法

附图2

你可能感兴趣的:(MySQL 5.6 5.7 如何自定义行锁超时等待(wait timeout)时间 innodb_lock_wait_timeout)