记录项目中的锁等待超时Lock wait timeout exceeded——事务与索引

问题描述

项目中突然报错,主要是两个,一个是任务脚本执行时间过长,另一个是锁等待时间过长,如下

Lock wait timeout exceeded; try restarting transaction

该任务脚本用于做多个表之间的数据同步,同时,项目中还有多个脚本和其他代码有可能对表数据进行操作,产生锁。有两个表的数据量为百万级别,且数据项也比较多。

如果对死锁和锁等待不是很了解,可以快速跳到下面的链接,理解一下什么是锁等待,以及可能造成的原因。死锁和锁等待

问题分析

首先,先放出来这部分的伪代码吧(便于理解)

void method(){
	事务开始;
	while(true) {
		List<ObjectA> list1 = 从A表按照条件拿出100条数据;
		if(list1为空){ //如果为空,表示数据已经处理完毕,结束
			return;
		}
		
		//依次操作ObjectA数据
		for(ObjectA objectA: list1) {
			删除A表数据;
			与B表数据合并,并更新对应的数据;
			同步C表数据;
		}
	}
	事务结束;
}

原因主要是事务加的位置有问题,并且数据表没有对应索引,导致更新时间过长。

任务执行时间过长

这个主要是因为更新数据的时候缺少索引,数据量比较大时,查找对应数据的时间比较长。这个可以通过数据库sql语句执行时间分析出来,因为我们单位有大数据平台,通过大数据平台的分析,找到执行时间最长的几条sql分析一下就可以了。如果没有相关的平台,建议在一些数据量比较大的表的查询和更新操作前后加日志记录,方便对sql执行情况进行分析。

锁等待时间过长

这个是事务和索引共同导致的。

在事务执行的过程中,对事务中所有涉及更新/添加的表加锁,其他进程请求这部分表数据时就会锁等待。索引缺失导致代码中每次循环的时候都过长,又对整个循环加上了事务,所以锁等待的时间就会超时,出现文章开头的问题。除了数据库层面加上索引,还需要调整代码的内容,去掉事务或者优化事务位置。

问题解决

1、数据库:我们在b表,根据查询的条件建立了索引。之前平均执行时间为10min,添加后缩短到1min。

2、代码,因为我们业务特点,我们这段代码,即使更新出现异常,只要依然保留A表数据,就不会对整体业务产生影响。下次脚本执行时就可以补充回来这部分数据,所以我们去掉事务、调整代码结构并加上一段异常处理,调整后的代码如下。

void method(){
	while(true) {
		List<ObjectA> list1 = 从A表按照条件拿出100条数据;
		if(list1为空){ //如果为空,表示数据已经处理完毕,结束
			return;
		}
		
		//依次操作ObjectA数据
		for(ObjectA objectA: list1) {
			try{
				与B表数据合并,并更新对应的数据;
				同步C表数据;
				删除A表数据;
			} catch(Exception e) {
				日志记录异常;
			} finally {
				日志记录执行时间;
			}
		}
	}
}

当然,如果业务场景需要,必须使用事务,也可以把循环内的代码拿出来,单独进行事务操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章