MySQL并发插入,它为什么会发生以及处理它的最佳方法是什么?

时间:2016-11-21 09:06:00

标签: mysql hibernate concurrency insert deadlock

我目前正在维护一些由以前的开发人员编写的处理车辆跟踪的项目。 我发现有时系统卡住了大约一分钟而不是继续工作,我们一直试图找到原因很长一段时间。

我可以在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经验,我对此行为感到非常困惑。

非常感谢!

1 个答案:

答案 0 :(得分:0)

我们已将事务隔离级别更改为READ-COMMITTED,它似乎已解决了问题