同时使用ON DUPLICATE KEY UPDATE时,MySQL中会发生CannotAcquireLockException

时间:2018-09-12 03:45:48

标签: mysql spring transactions

我有一个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());
}

0 个答案:

没有答案