Spring Batch测试场景下多语句timestamp重复问题求助
这问题我之前碰到过类似的,核心就是快速执行场景下时间戳精度跟不上代码运行速度导致的,我来帮你拆解原因和可行的解决办法:
问题根源分析
从你贴的日志能明显看到,重复的timestamp=20200330085053599253全集中在08:50:53.599这同一毫秒内生成。而提升日志级别后,日志打印带来的IO延迟把每次生成时间戳的间隔拉开了,超过了当前生成逻辑的精度阈值,所以就不再重复了。
大概率你当前的时间戳生成逻辑是基于毫秒级精度的(比如直接调用System.currentTimeMillis()),当Spring Batch的任务在单线程里快速执行时,同一毫秒内会多次触发生成逻辑,自然会得到一模一样的时间戳。
可行解决方案
1. 实现带原子递增的高精度时间戳生成器
这是最贴合业务需求的方案——既保留时间戳的可读性,又能绝对保证唯一性。核心思路是:在同一毫秒内,给时间戳附加一个自增的序列号,确保同一毫秒内的不同调用生成唯一值。
示例Java代码:
import java.util.concurrent.atomic.AtomicLong; public class UniqueTimestampGenerator { private static final AtomicLong COUNTER = new AtomicLong(0); private static long LAST_TIMESTAMP = 0; public static String generateUniqueTimestamp() { long currentTime = System.currentTimeMillis(); long counter; synchronized (UniqueTimestampGenerator.class) { if (currentTime == LAST_TIMESTAMP) { counter = COUNTER.incrementAndGet(); } else { COUNTER.set(0); counter = 0; LAST_TIMESTAMP = currentTime; } } // 格式化为「yyyyMMddHHmmssSSS + 3位计数器」,确保同一毫秒内最多999个唯一值 return String.format("%tY%<tm%<td%<tH%<tM%<tS%<tL%03d", currentTime, counter); } }
这个生成器会在同一毫秒内自动递增计数器,保证时间戳唯一;当进入新的毫秒时,计数器重置为0,不会影响时间戳的可读性。
2. 改用微秒级时间戳(降低重复概率)
如果业务允许,可以结合System.currentTimeMillis()和System.nanoTime()把精度提升到微秒级,大幅降低重复概率:
public static String generateMicroTimestamp() { long millis = System.currentTimeMillis(); long micro = System.nanoTime() % 1000000; // 取纳秒的后6位,对应微秒级 return String.format("%tY%<tm%<td%<tH%<tM%<tS%<tL%06d", millis, micro); }
不过极端情况下(同一微秒内多次调用)还是可能重复,所以要绝对唯一的话,建议结合方案1的计数器逻辑。
3. 让数据库生成唯一时间戳
如果业务场景允许,可以把时间戳生成交给数据库:比如MySQL用NOW(6)生成微秒级时间戳,PostgreSQL用CURRENT_TIMESTAMP(6)。应用层只需要插入数据时让数据库自动填充时间字段,完全避免应用层的精度问题。
4. 调整Spring Batch Chunk大小(临时应急)
如果只是测试环境临时验证,可以尝试调小Chunk的处理记录数,间接拉长每次处理的间隔。但这只是治标不治本的办法,不建议在生产环境使用。
验证建议
你可以先在测试环境替换时间戳生成逻辑为方案1的实现,然后关闭额外日志,观察是否还会出现重复的timestamp。如果问题解决,再考虑推广到生产环境。
内容的提问来源于stack exchange,提问作者sixstring

