Skip to content

分布式锁、ID 与任务调度

分布式系统里,很多问题不会直接以“大理论”的形式出现,而是会在工程里变成很具体的问号:

  1. 多个节点同时执行同一个任务怎么办
  2. 多个服务生成 ID 会不会冲突
  3. 定时任务在集群里会不会重复跑

这些问题看起来分散,但本质上都属于:多个节点同时存在时,如何控制执行权和全局唯一性。

这篇文章重点讲:

  1. 分布式锁在解决什么问题
  2. 全局唯一 ID 为什么是分布式问题
  3. 集群任务调度为什么会重复执行
  4. 常见方案各有什么优缺点
  5. 工程上最容易踩的坑是什么

1. 为什么单机里不是问题,到了分布式就变成问题

在单机应用里,很多事情默认就成立:

  1. 同一进程里加一把锁就够了
  2. 自增主键天然唯一
  3. 定时任务只会跑一份

但到了分布式系统,这些前提都没了:

  1. 有多个进程
  2. 有多个节点
  3. 没有天然共享内存

所以原本的本地锁、本地计数器、本地定时任务模型,都会失效。


2. 分布式锁到底在解决什么问题

分布式锁最核心解决的是:在多个节点同时运行时,某个关键动作同一时刻只能由一个节点执行。

例如:

  1. 只有一个节点执行结算任务
  2. 只有一个节点修改某个共享状态
  3. 只有一个请求能抢到某个资源

所以它不是“高级版 synchronized”,而是:跨进程、跨节点的执行权控制。


3. 一个最直观的分布式锁场景

假设有一个“每日对账”任务,系统部署了 4 个实例。

如果每个实例都跑这段逻辑,就会出现:

  1. 对账任务被重复执行
  2. 数据重复写入
  3. 下游接口被重复调用

这时就需要一层机制保证:同一时刻只有一个实例能拿到执行权。


4. 常见分布式锁实现思路有哪些

比较常见的思路包括:

  1. 基于数据库
  2. 基于 Redis
  3. 基于协调服务

4.1 基于数据库

例如利用唯一约束或状态字段控制抢占。

优点是:

  1. 简单
  2. 依赖少

缺点是:

  1. 性能一般
  2. 扩展性有限

4.2 基于 Redis

这是企业里非常常见的方案。

核心思路通常是:用原子命令抢锁,并设置过期时间。

例如:

java
RLock lock = redissonClient.getLock("settlement:task");
if (lock.tryLock()) {
    try {
        runSettlement();
    } finally {
        lock.unlock();
    }
}

4.3 基于协调服务

例如依赖专门的协调组件来控制节点执行权。

这类方案通常更偏:治理能力更强,但使用和运维也更重。


5. 分布式锁最容易被误解的地方是什么

最容易被误解的是:拿到锁就万事大吉。

实际上要注意的是:

  1. 锁会不会提前过期
  2. 解锁会不会误删别人的锁
  3. 业务执行时间超过锁有效期怎么办
  4. 锁失败时业务怎么降级

所以分布式锁真正难的,从来不是“加锁成功”,而是:边界条件和失败恢复。


6. 全局唯一 ID 为什么属于分布式问题

因为多个节点同时写数据时,本地自增 ID 通常不再适合作为全局唯一标识。

系统往往会需要:

  1. 全局唯一
  2. 尽量趋势递增
  3. 生成速度快
  4. 不强依赖单点

这时就会引出全局 ID 方案。


7. 常见全局 ID 方案有哪些

7.1 数据库自增

最简单,但更偏单点能力。

7.2 UUID

优点是生成简单、冲突概率低。

缺点是:

  1. 太长
  2. 不利于索引局部性
  3. 可读性差

7.3 雪花算法

这是企业里很常见的一类方案。

核心思路通常是:把时间、机器标识和序列号拼成一个全局唯一 ID。

它的优点是:

  1. 生成快
  2. 趋势递增
  3. 适合高并发生成

8. 任务调度为什么也会变成分布式问题

单机里的定时任务很简单:到时间就执行。

但在集群里,如果每个节点都按同一时间触发,就会出现:

  1. 同一任务被重复执行
  2. 重复发消息
  3. 重复写库

所以分布式任务调度真正要解决的是:谁来执行、什么时候执行、失败后怎么补偿。


9. 常见分布式任务调度思路有哪些

9.1 抢锁执行

多个节点同时竞争执行权,抢到锁的节点执行。

9.2 中心化调度平台

由一个专门调度平台统一分发任务。

9.3 分片调度

任务本身被拆成多个片段,由多个节点协同处理。

这几类方案的核心差别在于:到底是只需要“避免重复执行”,还是需要“统一调度和任务编排”。


10. 工程里常见的代码示意

例如一个简单的集群定时任务,可以这样表达执行权控制思路:

java
@Scheduled(cron = "0 0/5 * * * ?")
public void syncData() {
    if (!tryAcquireTaskLock()) {
        return;
    }
    try {
        doSync();
    } finally {
        releaseTaskLock();
    }
}

这个示意最重要的点不是具体 API,而是:集群任务不能只看定时表达式,还要看执行权控制。


11. 分布式锁、ID 和调度为什么适合放在一起讲

因为这三类能力最终都在处理类似的问题:

  1. 多节点并发存在
  2. 需要全局控制
  3. 需要避免冲突或重复

也就是说,它们共同回答的是:当系统不再是单机时,执行权、唯一性和调度秩序怎么建立。


12. 工程上最容易踩的几个坑

12.1 误区一:本地锁搬到分布式里就还是锁

不是。

本地锁只在同一进程内有效。

12.2 误区二:有过期时间的锁就一定安全

不一定。

业务时间过长、时钟问题、误删锁等都可能出问题。

12.3 误区三:任务只要定时触发就不会重复

集群里如果没有执行权控制,重复执行几乎是默认结果。

12.4 误区四:全局唯一 ID 只要不重复就行

实际还要考虑长度、趋势递增、索引友好性和高并发生成性能。


13. 一句话总结

分布式锁、全局 ID 和任务调度这条线,本质上是在为多节点系统建立执行权控制、全局唯一性和调度秩序;而工程里真正的难点,不是“找到一种实现”,而是把过期、重试、幂等、补偿和性能边界一起考虑进去。

基于 VitePress 构建的个人技术笔记。