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

MySQL concurrent insert, why does it happen and what is the best approach to deal with it?

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

我可以在 error.log 中看到 "Lock wait timeout exceeded; try restarting transaction" 并且在发生这种情况的那一刻,我总是在 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) 

两个查询总是 运行 的秒数相同(因此它们同时开始)。一个由我正在维护的项目执行,另一个由执行类似工作的遗留项目执行(但我想这没有任何区别,因为即使查询来自同一项目的不同线程)。

我尝试 google 并且似乎当两个插入到同一个 table 同时发生时,mysql 可能会死锁。我不明白为什么会这样?为什么 mysql 不将两个查询放入队列中并以随机顺序一一执行?

我们使用的隔离级别是可重复读。我在 Whosebug 上看到了一些将其更改为 READ COMMITTED 的建议,但我不确定它是否会解决我们的问题,而且据我所知,它也可能会破坏复制。

table 的结构如下:

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

table 正在使用 innodb。 我们在这个 table 中也有 gps_position_index 和 vehicle_index。我想知道这个问题是否与我们在 table?

上有索引有关

我们用来保存空闲事件的方法是GenericHibernateDaoImpl里面的save():

@Override
public T save(T item) {
    factory.getCurrentSession().saveOrUpdate(item);
    return item;
}

class GenericHibernateDaoImpl 具有 @Transactional 指令,因此,据我所知,事务在调用 save() 方法之前打开,并在 save() 完成时提交并保存。

遗留项目不使用 Hibernate,它只使用 java.sql.Statement:

    setStatement.execute(FleetManagerSqlHelper.insertIdlingEvent(deviceBinding.getVehicleId(), Long.toString(gpsPositionId)));

处理这种情况的最佳方法是什么?我可以减少 innodb_lock_wait_timeout(目前它是 50)并在每次发生 TimeoutException 时捕获它,重试它直到它完成工作(我什至看到有人在网上推荐它),但这是正确的方法吗?这种情况也可能发生在其他 table 上,我猜,这是否意味着我需要用重试包围项目内的所有查询?

我很感激任何建议,因为我对 MySQL 没有那么多经验,而且我对这种行为感到非常困惑。

非常感谢!

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