分布式锁实现:Redis、Zookeeper、数据库三种方案对比

分布式锁实现:Redis、Zookeeper、数据库三种方案对比

摘要:分布式锁是分布式并发控制的重要手段,理解不同方案的优缺点对选择合适的锁实现至关重要。本文将深入剖析数据库、Redis、Zookeeper三种主流实现方案的原生原理与代码实战,助你在面试与实战中游刃有余。

一、引言

在单体架构时代,我们可以利用Java原生的synchronized关键字或ReentrantLock类轻松解决并发问题,因为它们共享同一个JVM内存模型。然而,随着微服务架构的普及,业务场景从单机多线程转变为多机多进程,跨JVM的并发控制成为了新的挑战。

想象一个典型的电商秒杀场景:用户下单扣减库存。如果两台服务器同时读取到库存剩余1,并进行扣减,就会出现“超卖”现象。此时,就需要一把跨越进程边界的“锁”——分布式锁来协调多个服务实例的行为。

本文将从原理、实战、优缺点对比三个维度,深度解析三种主流的分布式锁实现方案。

二、核心概念与理论基础

在深入实现细节之前,我们需要明确一个合格的分布式锁必须具备的核心特性:

  1. 互斥性:这是锁的最基本属性,任意时刻,只有一个客户端能持有锁。
  2. 防死锁:锁必须有超时机制,防止客户端持有锁后宕机导致锁无法释放,造成系统死锁。
  3. 安全性:锁只能被持有它的客户端释放,不能被其他客户端误删。
  4. 高可用与高性能:加锁和解锁的开销要小,且锁服务本身要具备高可用性(如集群部署)。

三、方案一:基于数据库的实现

3.1 技术原理

利用数据库的ACID特性,最简单的方式是通过唯一索引来实现互斥。

原理:创建一张锁表,包含lock_key(锁资源名)字段并设置唯一约束。当需要获取锁时,插入一条记录;释放锁时,删除该记录。数据库会自动保证插入操作的原子性和唯一性,插入成功即代表获取锁成功。

3.2 实战代码

首先,定义数据库表结构:

CREATE TABLE `distributed_lock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lock_key` varchar(64) NOT NULL COMMENT '锁资源标识',
  `holder_info` varchar(255) NOT NULL COMMENT '持有者信息(如UUID+线程ID)',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_lock_key` (`lock_key`) -- 唯一索引是互斥性的核心
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Java实现代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;

@Component
public class DatabaseDistributedLock {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 当前线程的唯一标识,用于防止误删
    private static final ThreadLocal<String> THREAD_HOLDER = ThreadLocal.withInitial(() -> 
        UUID.randomUUID().toString() + "-" + Thread.currentThread().getId()
    );

    /**
     * 尝试获取锁
     * @param lockKey 锁的key
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey) {
        try {
            // 利用唯一索引冲突机制,插入成功即获取锁
            String sql = "INSERT INTO distributed_lock (lock_key, holder_info) VALUES (?, ?)";
            int affectedRows = jdbcTemplate.update(sql, lockKey, THREAD_HOLDER.get());
            return affectedRows > 0;
        } catch (Exception e) {
            // 捕获唯一索引冲突异常,表示锁已被占用
            return false;
        }
    }

    /**
     * 释放锁
     * @param lockKey 锁的key
     */
    public void unlock(String lockKey) {
        // 必须同时匹配 lockKey 和 holder_info 才能删除,防止误删其他线程的锁
        String sql = "DELETE FROM distributed_lock WHERE lock_key = ? AND holder_info = ?";
        jdbcTemplate.update(sql, lockKey, THREAD_HOLDER.get());
        THREAD_HOLDER.remove();
    }
}

3.3 优缺点分析

  • 优点:实现简单,利用现有数据库设施,无需引入第三方组件,易于理解。
  • 缺点
    • 性能瓶颈:数据库通过磁盘IO存储数据,并发能力远不如内存数据库。
    • 无阻塞重试机制:插入失败直接返回,需要业务层自行实现轮询重试。
    • 死锁风险:如果服务宕机,数据库记录可能残留。虽然可以通过定时任务清理过期记录,但这增加了系统复杂度。

四、方案二:基于Redis的实现

4.1 技术原理

Redis基于内存操作,性能极高,是目前最流行的分布式锁实现方案。其核心在于SET命令的扩展

posted @ 2026-02-27 06:01  寒人病酒  阅读(0)  评论(0)    收藏  举报