代码从队列中获取数据,将其作为批处理添加到预准备语句中,执行批处理然后提交:
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删除了批处理数据)。