代码从队列中获取数据,将其作为批处理添加到准备好的语句中,执行批处理然后提交:
int counter = 0;
while (!this.dataQueue.isEmpty()
&& counter <= config.MAX_FLUSH_COUNT) {
DataRecord record = this.dataQueue.poll();
if (record.ip.length() > 40) {
record.ip = record.ip.substring(0, 40);
}
try {
this.dataFlush.setInt(1, record.uid);
this.dataFlush.setInt(2, record.fid);
this.dataFlush
.setTimestamp(3, new Timestamp(record.time));
this.dataFlush.setString(4, record.ip);
this.dataFlush.addBatch();
} catch (Exception ex) {
Log.logError("Error building record: " + ex.getMessage());
}
counter++;
}
if (counter > 0) {
try {
this.dataFlush.executeBatch();
this.dataFlush.getConnection().commit();
} catch (SQLException ex) {
Log.logError("Error executing flush: " + ex.getMessage());
}
}
此刷新可能包含数千个条目(MAX_FLUSH_COUNT 限制为每批 2000 个),因此有时会发生死锁(刷新每 8 秒执行一次,死锁大约每 5 到 6 小时发生一次)。
编辑:这是我准备好的声明:
Connection conn = DBBase.getConnection();
conn.setAutoCommit(false);
this.snatchFlush = conn.prepareStatement("INSERT INTO slog (uid, fid, time, ip) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE fid=VALUES(fid), time=VALUES(time), ip=VALUES(ip)");
uid 是这个表的主键。数据库引擎是 InnoDB,没有外键,没有触发器。该表可能会同时被 Web 服务器和该应用程序读取或写入,这不太可能但可能发生。
我的第一个更改是将批次拆分为多个批次,并将它们的大小限制为 200-500 个数据集。
我现在的问题:发生死锁时,通常的解决方案是重新启动事务。
这是否意味着我必须将数据重新添加到 dataFlush Prepared Statement、executeBatch 并再次提交?如果是,那意味着我需要保存从队列中轮询的数据,否则它会丢失。
有没有更好的解决方案不需要我保存轮询数据?(我假设一个简单的 executeBatch 并在 catch-Block 中提交将不起作用,因为 executeBatch 删除了批处理数据)。