我目前正在维护一些由以前的开发人员编写的处理车辆跟踪的项目。 我发现有时系统会卡住大约一分钟,然后就继续工作,我们已经尝试寻找原因很长时间了。
我可以在 error.log 中看到“超出锁定等待超时;尝试重新启动事务”,并且在发生这种情况时,我总是看到两个类似的查询在 MySQL 中运行:
Insert into idling_events (gps_position_id, vehicle_id) values (23083665777 ,'46881')
和
insert into idling_events (gps_position_id, vehicle_id, created_at, updated_at) values (23083665761, null, null, null)
两个查询始终运行相同的秒数(因此它们同时开始)。一个是由我正在维护的项目执行的,另一个是由正在执行类似工作的遗留项目执行的(但我想这没有任何区别,因为即使查询是从同一项目的不同线程)。
我尝试谷歌,似乎当两个插入到同一个表的同时发生时,mysql可能会死锁。我不明白为什么会变成这样?为什么mysql不将两个查询放入队列中并以随机顺序一一执行?
我们使用的隔离级别是REPEATABLE READ。我在 Stackoverflow 上看到了一些将其更改为 READ COMMITTED 的建议,但我不确定这是否能解决我们的问题,而且据我了解,它也可能会破坏复制。
表的结构如下:
id bigint(14) unsigned, not null, primary key, auto_increment
gps_position_id bigint(14) unsigned, not null, key: mul
vehicle_id int(11), can be null, key: mul
created_at datetime, can be null
updated_at datetime, can be null
表正在使用 innodb。 该表中还有 gps_position_index 和vehicle_index。我想知道这个问题是否与我们在表上有索引有关?
我们用来保存空闲事件的方法是GenericHibernateDaoImpl中的save():
@Override
public T save(T item) {
factory.getCurrentSession().saveOrUpdate(item);
return item;
}
类 GenericHibernateDaoImpl 有 @Transactional 指令,因此,据我了解,事务在调用 save() 方法之前打开,并在 save() 完成时提交和保存。
遗留项目没有使用Hibernate,它只是使用java.sql.Statement:
setStatement.execute(FleetManagerSqlHelper.insertIdlingEvent(deviceBinding.getVehicleId(), Long.toString(gpsPositionId)));
处理这种情况的最佳方法是什么?我可以减少 innodb_lock_wait_timeout (目前是 50)并在每次发生时捕获 TimeoutException,重试直到完成工作(我什至看到有人在网上推荐它),但这是正确的方法吗?这种情况也可能发生在其他表上,我猜,这是否意味着我需要重试项目内的所有查询?
我很感激任何建议,因为我没有太多使用 MySQL 的经验,而且我对这种行为感到非常困惑。
非常感谢!
最佳答案
我们已将事务隔离级别更改为 READ-COMMITTED,似乎已经解决了问题
关于MySQL并发插入,为什么会发生以及处理它的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40716303/