我有一个Spring Scheduled Task,可以插入或更新json字符串,以提供查询缓存。最近,我使用多线程来提高效率,但是发现 CannotAcquireLockException 。我使用 Spring + MyBatis 框架。
我搜索了原因,有人说在另一个服务中使用服务会导致死亡锁并引发此类异常,另一些人说在表中的索引无法在插入或更新数据时超时,从而获得异常。我最信任的解释是我在MySQL中使用ON DUPLICATE KEY UPDATE
。事务开始时,查找重复项并获取最新数据,它将获得一个锁(我不知道它是哪种锁)。假设另一个事务也找到该行并获得了锁。现在,当更新行时,前者的事务需要后者的锁定释放,后者也需要前者的锁定释放。因此发生CannotAcquireLockException。
但是我使用 ConcurrentLinkedQueue 来存储目标数据的索引,并确保它不能同时获得同一行,为什么仍会发生异常?还是我犯错了?
任务:
@Scheduled(cron = "0 0/5 * * * ?")
public void executeJob() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
logger.info("@@@@@@@@@@start[" + sdf.format(new Date()) + "]@@@@@@@@@@");
Queue<CacheCondition> conditions = new ConcurrentLinkedQueue<>();
List<Integer> projectIds = this.getProjectListService().selectProjectIds();
for (String timeStr : TIME_LIST) {
for (Integer pid : projectIds) {
List<Integer> themeIds = this.getThemeService().selectThemeIds(pid);
themeIds.add(0, 0);
for (Integer tid : themeIds) {
conditions.offer(new CacheCondition(tid, timeStr, pid)); // (push)
}
}
}
for (int i = 0; i < 10; ++i) {
new Thread(new SubSyncIndexResultTask(conditions, projectNameDataService, indexCacheService)).start();
}
}
SubTask(Thread):
@Override
public void run() {
while (conditions.size() > 0) {
logger.info("^^^^^^^^^^subTask runs^^^^^^^^^^");
CacheCondition cc = conditions.poll(); // (pop)
Integer tid = cc.getTid();
String timeStr = cc.getTimeStr();
Integer pid = cc.getPid();
Map<String, List<Map<String, Object>>> statistics = this.getProjectNameDataService()
.getIndexStatistics(tid, timeStr, pid);
this.getIndexCacheService().insertOrUpdateJson(pid, tid, timeStr, JSON.toJSONString(statistics));
logger.info("pid = " + pid + ", tid = " + tid + ", timeStr = " + timeStr);
}
}
服务:
public int insertOrUpdateJson(Integer projectId, Integer themeId, String timeString, String resultJson) {
resultJson.replace("'", "\\'");
StringBuffer sqlBuffer = new StringBuffer("INSERT INTO index_cache(project_id, theme_id, time_string, " +
"result_json, update_time) VALUES(").append(projectId).append(", ").append(themeId).append(", ")
.append("'").append(timeString).append("', '").append(resultJson).append("', NOW())")
.append(" ON DUPLICATE KEY UPDATE result_json = '").append(resultJson).append("', update_time")
.append(" = NOW()");
return updateJson(sqlBuffer.toString());
}