《知识点扫盲 · Redis 分布式锁》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

技术简介

Redis 分布式锁是一种利用 Redis 的特性来实现的分布式锁机制,主要用于解决在分布式系统中多个实例对共享资源的并发访问问题。通过使用 Redis 作为锁的存储介质,可以确保在多个服务实例之间的互斥访问。

Redis 分布式锁的基本原理:

  • 加锁:通过 SETNX 命令,尝试设置一个锁的键。如果设置成功,表示获得锁;如果失败,表示锁已经被其他实例持有。
  • 设置过期时间:为了防止死锁,通常在加锁时会设置一个过期时间,确保即使持锁的实例崩溃,锁也会在一定时间后自动释放。
  • 解锁:在完成对共享资源的操作后,释放锁。解锁时需要确保只有持有锁的实例才能解锁,通常通过比较锁的值来实现。

Redis 分布式锁的注意事项:

  1. 锁过期时间: 设置合理的锁过期时间,避免死锁。
  2. 释放: 确保在使用完锁后及时释放锁,避免资源浪费。
  3. 重入锁: 如果需要同一个线程多次获取锁,可以使用可重入锁。
  4. 公平锁: 如果需要按照请求顺序分配锁,可以使用公平锁
  5. 锁竞争: 在高并发场景下,可能会出现锁竞争,需要考虑如何处理锁竞争。

示例说明

编写工具类 RedisLockUtil,代码如下:

java 复制代码
public class RedisLockUtil {

    public static final String LOCK_KEY_PREFIX = "lock:";
    private static final String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private static final RedisScript<Long> UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLOCK_LUA_SCRIPT, Long.class);

    /**
     * 尝试Redis锁
     *
     * @param lockKey  锁名称
     * @param lockId   锁持有者唯一ID
     * @param duration 过期时间(过期自动释放锁)
     * @param timeUnit 时间单位
     * @return true: 获取锁成功 , false: 获取锁失败
     */
    public static boolean tryLock(@NonNull String lockKey, @NonNull String lockId, long duration, @NonNull TimeUnit timeUnit) {
        return RedisUtil.setIfAbsent(LOCK_KEY_PREFIX.concat(lockKey), lockId, duration, timeUnit);
    }

    /**
     * 释放Redis锁
     *
     * @param lockKey 锁名称
     * @param lockId  锁持有者唯一ID
     */
    public static boolean unlock(@NonNull String lockKey, @NonNull String lockId) {
        Object result = RedisUtil.getRedisTemplate().execute(UNLOCK_REDIS_SCRIPT, Collections.singletonList(LOCK_KEY_PREFIX.concat(lockKey)), lockId);
        return Long.valueOf(1).equals(result);
    }
}

核心工具类逻辑:

java 复制代码
/**
 * 仅在Redis中键不存在时设置键的值并设置过期时间。
 *
 * @param redisKey Redis中的键。
 * @param value    要设置的值。
 * @param expire   键的过期时间。
 * @param timeUnit 过期时间的时间单位。
 * @return 如果成功设置了键值对,则返回true,否则返回false。
 */
public static boolean setIfAbsent(@NonNull String redisKey, Object value, long expire, TimeUnit timeUnit) {
    return checkBool(getRedisTemplate().opsForValue()
            .setIfAbsent(redisKey, value, expire, timeUnit));
}

使用示例:

java 复制代码
    public void doSomething() {
        // 分布式锁标识,根据该标识进行加锁和解锁
        String lockKey = "myLockKey";
        // 随机ID,解锁时用于校验
        String lockId = UUID.randomUUID().toString();
        try {
            // 创建一个60秒后自动过期的锁. 返回true: 获取锁成功,返回false:获取锁失败(开发者自行决定获取锁失败时处理方案)
            boolean status = RedisLockUtil.tryLock(lockKey, lockId, 60, TimeUnit.SECONDS);
            // 获取锁失败
            if (!status) {
                // 执行获取锁失败的业务处理逻辑
                throw ApiException.createEx(ExceptionCodeEnum.REDIS_TRY_GET_LOCK_FAILURE, lockKey);
            }
            // 执行获取锁成功后的业务逻辑
            // ...
        } finally {
            // 解锁状态,true:解锁成功,false:解锁失败
            boolean unlockStatus = RedisLockUtil.unlock(lockKey, lockId);
            if (!unlockStatus) {
                log.error("[Redis分布式锁] 解锁失败, lockKey={}, lockId={}", lockKey, lockId);
            }
        }
    }

总结陈词

此篇文章介绍了 Redis 分布式的基础应用,仅供学习参考。

Redis 分布式锁是一种简单高效的实现分布式锁的方案,可以有效解决分布式系统中数据冲突问题。选择合适的实现方法,注意相关注意事项,可以确保锁的可靠性和性能。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关推荐
正在走向自律1 分钟前
深度剖析 KES 行标识体系:OID 与 ROWID 核心原理、实战案例及性能优化
数据库·oid·kes·rowid
一直都在57215 分钟前
MySQL索引优化
android·数据库·mysql
wjp@00130 分钟前
SQL server导出导入数据
运维·服务器·数据库
脑子加油站30 分钟前
MySQL8数据库高级特性
数据库·mysql
REDcker1 小时前
OpenSSL:C 语言 TLS 客户端完整示例
c语言·网络·数据库
zly35001 小时前
centos7 mysql 无法被远程连接
数据库·mysql
廿一夏1 小时前
MySql的增删改查
数据库·mysql·dba
瀚高PG实验室1 小时前
HGDB 4.5.8.8开启oracle兼容执行带聚合函数的SQL导致数据库进程被信号11杀死
数据库·sql·oracle·瀚高数据库
炘爚2 小时前
日志系统整体设计步骤以及功能函数梳理
运维·服务器·数据库
_下雨天.2 小时前
PostgreSQL日常维护
数据库·postgresql