利用数据库锁来实现分布式环境下的定时任务只执行一次

我们有个task服务,这个服务是双节点,即两台服务器上都部署有这个服务,但是每天的定时任务我们只想执行一次,于是我们想到用数据库的行锁来实现这种功能。

核心代码是JobLocksMo lock = brJobLocksMongoDaoImpl.lock(jobLocksMo.getId(), jobLocksMo.getVersion());这块,当拿到任务Id后,判断当前任务是否被执行,如果没有被执行则给任务上锁,并把版本加1,这样下一个线程进来就没法更新数据库行记录

(在 MongoDB 中,findAndModify 方法用于查找文档并对其进行修改。如果查询条件找不到文档,findAndModify 方法将返回 null,如果更新成功则返回最新的对象)。

bash 复制代码
    @Async("taskExecutor")
    @Scheduled(cron = "0 0 5 9 12 ?")
    public void updateAgentReport() {
        log.info("处理顾问历史数据,开始....");
        JobLocksMo jobLocksMo = brJobLocksMongoDaoImpl.findById("br_data_job_lock_report_agent");
        if (null == jobLocksMo || jobLocksMo.getStatus() == 1) {
            log.info("任务锁未释放,不执行后续操作");
            return;
        }
        StopWatch watch = new StopWatch();
        watch.start();
        try {
            log.info("br_data_job_lock_report_agent status : " + jobLocksMo.getStatus());
            if (jobLocksMo.getStatus() == 0) {
                // 如果当前任务处于空闲状态则给当前任务加锁,核对版本正确才更新
                JobLocksMo lock = brJobLocksMongoDaoImpl.lock(jobLocksMo.getId(), jobLocksMo.getVersion());
                if (null != lock) {
                    LocalDateRangeBean localDateRangeBean = new LocalDateRangeBean();
                    localDateRangeBean.setStartDate(LocalDate.of(2019, 8, 1));
                    localDateRangeBean.setEndDate(LocalDate.now());
                    historyServiceImpl.updateAgentReport(localDateRangeBean);
                }
            }
        } catch (Exception e) {
            log.error("12.9 5点处理顾问历史数据异常:", e);
        } finally {
            watch.stop();         
            // 只要是给当前任务加上了锁最后必须释放锁
            if (jobLocksMo.getStatus() == 0) {
                brJobLocksMongoDaoImpl.unlock(jobLocksMo.getId());
            }
        }
    }
bash 复制代码
    public JobLocksMo lock(String id, Long version) {
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and("id").is(id);
        criteria.and("status").is(0);
        criteria.and("version").is(version);
        query.addCriteria(criteria);
        Update update = new Update();
        update.set("status", 1);
        update.inc("version", 1);
        update.set("updateId", "system_job_lock");
        update.set("updateTime", LocalDateTime.now());
        return brMongoTemplate.findAndModify(query, update, JobLocksMo.class);
    }
bash 复制代码
    public JobLocksMo unlock(String id) {
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and("id").is(id);
        criteria.and("status").is(1);
        query.addCriteria(criteria);
        Update update = new Update();
        update.set("status", 0);
        update.set("updateId", "system_job_unlock");
        update.set("updateTime", LocalDateTime.now());
        return brMongoTemplate.findAndModify(query, update, JobLocksMo.class);
相关推荐
数据组小组8 小时前
免费数据库管理工具深度横评:NineData 社区版、Bytebase 社区版、Archery,2026 年开发者该选哪个?
数据库·测试·数据库管理工具·数据复制·迁移工具·ninedata社区版·naivicat平替
悟空聊架构15 小时前
基于KaiwuDB在游乐场“刷卡+投币”双模消费系统中的落地实践
数据库·后端·架构
IvorySQL15 小时前
PostgreSQL 技术日报 (3月4日)|硬核干货 + 内核暗流一网打尽
数据库·postgresql·开源
进击的丸子18 小时前
虹软人脸服务器版SDK(Linux/ARM Pro)多线程调用及性能优化
linux·数据库·后端
NineData1 天前
NineData智能数据管理平台新功能发布|2026年1-2月
数据库·sql·数据分析
回家路上绕了弯1 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
IvorySQL1 天前
双星闪耀温哥华:IvorySQL 社区两项议题入选 PGConf.dev 2026
数据库·postgresql·开源
ma_king2 天前
入门 java 和 数据库
java·数据库·后端
jiayou642 天前
KingbaseES 实战:审计追踪配置与运维实践
数据库
NineData2 天前
NineData 迁移评估功能正式上线
数据库·dba