跟着AI学习Java,第四天

7 阅读23分钟

第 4 天学习任务

Redis

一、Redis核心概述

Redis(Remote Dictionary Server)是一款基于内存的高性能键值对(Key-Value)NoSQL数据库,支持多种数据结构,具备高并发、低延迟、可持久化、支持分布式等特性,广泛应用于缓存、会话存储、分布式锁、限流、消息队列等场景。

核心优势:

  • 纯内存操作,读写速度极快(单机QPS可达10万+);
  • 单线程模型+IO多路复用,避免线程上下文切换和锁竞争;
  • 支持多种数据结构,适配不同业务场景;
  • 支持持久化,可将内存数据同步到磁盘,避免宕机数据丢失;
  • 支持主从复制、哨兵、集群,具备高可用性;
  • 支持Lua脚本,实现原子性操作。

二、Redis 5种基础数据结构(底层实现+命令+应用场景·必考)

Redis的核心竞争力之一是丰富的数据结构,每种结构都有对应的底层实现,适配不同的存储和查询场景,面试中几乎必考底层原理和应用。

1. String(字符串)—— 最基础、最常用

底层实现

基于简单动态字符串(SDS,Simple Dynamic String),是C语言char数组的增强版,结构如下:

  • len:记录字符串实际长度(字节数),获取长度O(1);
  • alloc:记录数组总容量(字节数),避免频繁扩容;
  • buf[]:存储字符串内容,末尾自动追加'\0'(兼容C语言函数);
  • flags:标记字符串类型(如是否压缩、编码格式)。

SDS vs C语言字符串的优势(面试必答):

  • 获取长度O(1)(C语言需遍历,O(n));
  • 杜绝缓冲区溢出(扩容时先检查空间,不足则扩容);
  • 空间预分配:扩容时额外分配空间,减少内存重分配次数;
  • 惰性释放:删除字符串时不立即释放内存,后续复用,提升性能;
  • 二进制安全:可存储图片、视频等二进制数据,不依赖'\0'判断结束。

常用命令(实操+面试必背)

  • SET key value [EX seconds] [PX milliseconds] [NX|XX]:设置键值对,EX/PX设置过期时间,NX(仅不存在时设置),XX(仅存在时设置);
  • GET key:获取键对应的值,不存在返回nil;
  • INCR key:自增1(仅适用于数字类型),返回自增后的值;
  • DECR key:自减1,返回自减后的值;
  • INCRBY key increment:自增指定数值;
  • DECRBY key decrement:自减指定数值;
  • MSET key1 value1 key2 value2 ...:批量设置键值对;
  • MGET key1 key2 ...:批量获取多个键的值;
  • EXPIRE key seconds:设置键的过期时间(秒);
  • TTL key:查看键的剩余过期时间(-1:永不过期,-2:已过期/不存在);
  • PERSIST key:取消键的过期时间,变为永不过期。

应用场景

  • 缓存对象:将JSON格式的对象(如用户信息、商品信息)存储为字符串;
  • 分布式ID:通过INCR/INCRBY生成全局唯一ID(如订单ID、用户ID);
  • 计数器:文章阅读量、点赞数、接口访问次数等;
  • 限流:基于INCR统计单位时间内的请求数,超过阈值则拒绝;
  • 会话存储:存储用户登录会话信息,设置过期时间自动失效。

2. Hash(哈希)—— 结构化数据存储

底层实现

两种编码格式,根据数据量自动切换:

  1. ziplist(压缩列表):当哈希中field-value数量少、值较小时使用(默认阈值:field数≤512,单个value≤64字节);
  2. hashtable(哈希表):当数据量超过阈值时,自动转为哈希表,查询效率O(1)。

ziplist优势:节省内存,连续存储,减少内存碎片;缺点:查询、修改效率低于哈希表。

常用命令

  • HSET key field value:给哈希表设置field-value对;
  • HGET key field:获取哈希表中指定field的值;
  • HMSET key field1 value1 field2 value2 ...:批量设置哈希表的field-value;
  • HMGET key field1 field2 ...:批量获取哈希表中多个field的值;
  • HGETALL key:获取哈希表中所有field-value对(数据量大时慎用,会阻塞主线程);
  • HKEYS key:获取哈希表中所有field;
  • HVALS key:获取哈希表中所有value;
  • HDEL key field1 field2 ...:删除哈希表中指定field;
  • HLEN key:获取哈希表中field的数量;
  • HEXISTS key field:判断哈希表中是否存在指定field。

应用场景

  • 存储结构化数据:用户信息(id、name、age、phone)、商品信息(id、name、price、stock);
  • 局部更新:无需修改整个对象,仅更新某个field(如修改用户手机号);
  • 节省内存:相比String存储JSON,Hash可避免重复存储key,减少内存占用。

3. List(列表)—— 有序可重复集合

底层实现

Redis 3.2版本前:ziplist(元素少、值小)和linkedlist(双向链表);

Redis 3.2版本后:统一使用quicklist(快速列表),是ziplist和linkedlist的结合体,结构如下:

quicklist = 多个ziplist + 双向链表,每个ziplist存储多个元素,链表节点指向各个ziplist,兼顾内存效率和操作效率。

核心特点:有序、可重复、两端操作(LPUSH/LPOP、RPUSH/RPOP)效率高(O(1)),中间插入/删除效率低(O(n))。

常用命令

  • LPUSH key value1 value2 ...:从列表左侧插入一个/多个元素;
  • RPUSH key value1 value2 ...:从列表右侧插入一个/多个元素;
  • LPOP key:从列表左侧弹出一个元素;
  • RPOP key:从列表右侧弹出一个元素;
  • LRANGE key start stop:获取列表中从start到stop的元素(start=0,stop=-1表示所有元素);
  • LLEN key:获取列表的长度;
  • LINSERT key BEFORE|AFTER pivot value:在指定元素pivot的前面/后面插入value;
  • LREM key count value:删除列表中count个值为value的元素(count>0:从左到右删;count<0:从右到左删;count=0:删除所有);
  • LTRIM key start stop:保留列表中start到stop的元素,删除其他元素;
  • BLPOP key1 key2 ... timeout:阻塞式从左侧弹出元素,无元素则等待timeout秒(timeout=0表示永久等待);
  • BRPOP key1 key2 ... timeout:阻塞式从右侧弹出元素。

应用场景

  • 消息队列:基于BLPOP/BRPOP实现简单的阻塞式消息队列;
  • 栈/队列:LPUSH+LPOP实现栈(先进后出),LPUSH+RPOP实现队列(先进先出);
  • 最新列表:如新闻列表、朋友圈动态,LPUSH插入新内容,LRANGE获取最新N条;
  • 任务队列:存储待执行的任务,消费者通过RPOP/BLPOP获取任务执行。

4. Set(集合)—— 无序唯一集合

底层实现

两种编码格式,自动切换:

  1. intset(整数集合):当集合中所有元素都是整数,且元素个数≤512时使用,节省内存;
  2. hashtable(哈希表):当元素包含非整数,或个数超过阈值时使用,key为集合元素,value为null,利用哈希表的唯一性特性。

核心特点:无序、元素唯一、支持交、并、差等集合运算,查询元素是否存在效率高(O(1))。

常用命令

  • SADD key member1 member2 ...:向集合中添加一个/多个元素(重复元素会自动去重);
  • SREM key member1 member2 ...:删除集合中指定元素;
  • SMEMBERS key:获取集合中所有元素(数据量大时慎用);
  • SISMEMBER key member:判断元素是否在集合中(O(1));
  • SCARD key:获取集合中元素的个数;
  • SUNION key1 key2 ...:求多个集合的并集,返回所有元素;
  • SINTER key1 key2 ...:求多个集合的交集,返回共同元素;
  • SDIFF key1 key2 ...:求集合1相对于集合2的差集(只在集合1中存在的元素);
  • SRANDMEMBER key [count]:随机获取集合中count个元素(count不写默认1,count为负数时可重复获取);
  • SPOP key [count]:随机弹出集合中count个元素。

应用场景

  • 去重:如用户点赞、收藏,避免重复操作;
  • 共同好友/兴趣标签:通过SINTER求两个用户的共同好友;
  • 抽奖:通过SRANDMEMBER/SPOP随机抽取中奖用户;
  • 标签管理:给用户/商品打标签,通过集合运算筛选目标用户/商品。

5. ZSet(有序集合)—— 有序唯一集合(面试必考)

底层实现(核心考点)

两种编码格式,自动切换:

  1. ziplist(压缩列表):当元素个数≤128,单个元素值≤64字节时使用,元素按score排序存储;
  2. skiplist(跳表)+ hashtable(哈希表):当数据量超过阈值时使用,是ZSet的核心实现。

核心结构解析:

  • skiplist(跳表):按score从小到大排序,支持快速范围查询(O(log n)),解决链表范围查询效率低的问题;
  • hashtable:存储元素与score的映射,支持快速获取元素的score(O(1)),保证元素唯一性。

面试必问:ZSet为什么用跳表,不用红黑树?

  • 跳表实现更简单,红黑树的插入、删除需要维护平衡,逻辑复杂;
  • 跳表支持范围查询更高效,红黑树范围查询需要遍历,跳表可直接通过层级指针定位;
  • 跳表的插入、删除效率与红黑树相当(均为O(log n)),但实现成本更低。

常用命令

  • ZADD key score1 member1 score2 member2 ...:向有序集合中添加元素(score为排序依据,重复member会更新score);
  • ZRANGE key start stop [WITHSCORES]:按score从小到大排序,获取start到stop的元素(WITHSCORES显示score);
  • ZREVRANGE key start stop [WITHSCORES]:按score从大到小排序,获取元素;
  • ZSCORE key member:获取指定member的score;
  • ZRANK key member:获取member按score升序的排名(排名从0开始);
  • ZREVRANK key member:获取member按score降序的排名;
  • ZREM key member1 member2 ...:删除有序集合中的指定元素;
  • ZINCRBY key increment member:给指定member的score增加increment;
  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]:按score范围查询元素;
  • ZCOUNT key min max:统计score在min到max之间的元素个数;
  • ZREMRangeByScore key min max:删除score在min到max之间的元素;
  • ZREMRangeByRank key start stop:删除排名在start到stop之间的元素。

应用场景

  • 排行榜:如游戏战力榜、文章阅读量榜、商品销量榜(按score排序);
  • 优先级队列:按score设置任务优先级,ZRANGEBYSCORE获取最高优先级任务;
  • 延时任务:score设置为任务执行时间戳,定期ZRANGEBYSCORE获取到期任务;
  • 范围筛选:如筛选分数在90-100分的学生、价格在100-200元的商品。

三、Redis 过期策略(面试必考)

Redis支持给key设置过期时间,但不会实时删除所有过期key(会消耗大量CPU),而是采用「三种策略组合」的方式,平衡CPU和内存开销。

1. 惰性删除(Lazy Expiration)

核心逻辑

不主动删除过期key,只有当用户访问该key时,才检查是否过期:若过期则删除,返回nil;若未过期则正常返回值。

优缺点

  • 优点:完全不消耗CPU资源,只在访问时触发检查,对CPU友好;
  • 缺点:过期key若长期不被访问,会一直占用内存,导致内存泄漏(“僵尸key”)。

2. 定期删除(Periodic Expiration)

核心逻辑

Redis每隔一段时间(默认100ms),随机抽取一批设置了过期时间的key,检查并删除过期的key,具体流程:

  1. 从设置了过期时间的key集合中,随机抽取N个key(默认N=20);
  2. 删除其中过期的key;
  3. 若过期key的比例超过1/4,则重复步骤1-2,直到比例≤1/4或执行次数达到上限(避免阻塞主线程)。

优缺点

  • 优点:平衡CPU和内存,既能删除部分过期key,又不会消耗过多CPU;
  • 缺点:无法保证所有过期key都被删除,存在“漏删”情况,仍可能有部分过期key占用内存。

3. 内存淘汰机制(Memory Eviction Policy)

核心逻辑

当Redis内存使用达到配置的maxmemory(最大内存)时,会触发内存淘汰机制,删除部分key,释放内存,避免Redis宕机。

配置方式:通过redis.conf中的maxmemory-policy参数设置,共8种策略,分为4类:

8种淘汰策略(面试必背)

策略名称核心逻辑适用场景
volatile-lru仅对设置了过期时间的key,删除最近最少使用(LRU)的key大多数业务场景,优先保留未过期的key
allkeys-lru对所有key,删除最近最少使用的key缓存场景,不区分是否过期,优先保留常用key
volatile-lfu仅对设置了过期时间的key,删除使用频率最低(LFU)的key访问频率不均匀的场景,比LRU更精准
allkeys-lfu对所有key,删除使用频率最低的key高频访问场景,淘汰低频访问的key
volatile-random仅对设置了过期时间的key,随机删除无明确访问规律,无需优先保留某些key
allkeys-random对所有key,随机删除测试场景,生产环境极少使用
volatile-ttl仅对设置了过期时间的key,删除剩余过期时间最短(TTL最小)的key需要优先保留即将过期的key的场景
noeviction不删除任何key,内存满时拒绝所有写操作(返回OOM错误)不允许丢失数据的场景(如持久化存储)

生产环境常用策略:volatile-lru(默认推荐)、allkeys-lru(纯缓存场景)。


四、Redis 持久化(面试必问)

Redis是基于内存的数据库,宕机后内存数据会丢失,持久化机制就是将内存中的数据同步到磁盘,重启后恢复数据,分为RDB、AOF两种方式,以及混合持久化(4.0+)。

1. RDB(Redis Database File,快照持久化)

核心原理

在指定时间点,将Redis内存中的所有数据生成一份二进制快照(dump.rdb文件),存储到磁盘,重启时加载该文件,恢复数据。

触发方式(三种):

  1. 手动触发:

    1. save:同步执行,阻塞Redis主线程,直到快照生成完成(不推荐,会导致服务不可用);
    2. bgsave:异步执行,fork一个子进程,由子进程负责生成快照,主线程正常处理请求(推荐)。
  2. 自动触发:通过redis.conf配置触发条件,如:

    1. save 3600 1:3600秒内,至少有1个key被修改,触发bgsave;
    2. save 300 100:300秒内,至少有100个key被修改,触发bgsave;
    3. save 60 10000:60秒内,至少有10000个key被修改,触发bgsave。
  3. 被动触发:

    1. 主从复制:主节点自动触发bgsave,生成快照发送给从节点;
    2. Redis关机(shutdown):自动执行save,生成快照后关机;
    3. 执行flushall:清空所有数据,会生成空的RDB文件。

优缺点

优点缺点
1. 二进制文件,体积小,占用磁盘空间少;1. 数据丢失风险:两次快照之间宕机,会丢失这段时间的修改;
2. 恢复速度快,适合大规模数据恢复;2. fork子进程时,会占用与主进程相同的内存(写时复制),高内存场景可能阻塞主线程;
3. 适合冷备、灾难恢复,可将RDB文件拷贝到其他服务器恢复;3. 不适合实时持久化,无法保证数据零丢失。

2. AOF(Append Only File,日志持久化)

核心原理

将Redis执行的每一条写命令(SET、HSET、LPUSH等),以文本格式追加到aof文件中,重启时,Redis会重新执行aof文件中的所有命令,恢复数据。

核心配置:

  • appendonly yes:开启AOF持久化(默认关闭);
  • appendfilename "appendonly.aof":AOF文件名称;
  • appendfsync:刷盘策略(核心,面试必问)。

三种刷盘策略(面试必背)

刷盘策略核心逻辑安全性性能适用场景
appendfsync always每执行一条写命令,立即将数据刷到磁盘最高(数据几乎不丢失)最低(频繁IO,阻塞主线程)对数据安全性要求极高的场景(如金融)
appendfsync everysec每秒刷盘一次,将缓冲区的数据写入磁盘较高(最多丢失1秒数据)中等(平衡安全与性能)生产环境默认,大多数业务场景
appendfsync no不主动刷盘,由操作系统决定何时刷盘最低(可能丢失大量数据)最高(无IO阻塞)测试场景,生产环境不推荐

AOF重写(核心优化)

问题:AOF文件会随着写命令增多而越来越大,导致恢复速度慢、占用磁盘空间多。

解决方案:AOF重写,Redis会扫描内存中的所有数据,生成一份“最小指令集”(如多次SET同一个key,只保留最后一次),替换原来的AOF文件,缩小文件体积。

触发方式:

  • 手动触发:bgrewriteaof(异步执行,不阻塞主线程);

  • 自动触发:通过配置阈值,如:

    • auto-aof-rewrite-percentage 100:AOF文件体积比上次重写后增长100%(翻倍);
    • auto-aof-rewrite-min-size 64mb:AOF文件体积至少达到64MB,才触发重写。

优缺点

优点缺点
1. 数据安全性高,最多丢失1秒数据(everysec策略);1. 文件体积大,占用磁盘空间多;
2. 文本格式,可手动编辑、修复(如误执行flushall,可删除该命令行恢复数据);2. 恢复速度比RDB慢(需要重放所有命令);
3. 支持实时持久化,适合对数据零丢失要求高的场景;4. 写命令追加会产生一定的IO开销,性能略低于RDB。

3. 混合持久化(Redis 4.0+,生产标配)

核心原理

结合RDB和AOF的优点,开启混合持久化后,AOF重写时,会将当前内存数据以RDB格式写入AOF文件头部,后续的写命令以AOF格式追加到文件尾部。

重启恢复流程:

  1. 先加载AOF文件头部的RDB数据(快速恢复大量数据);
  2. 再重放AOF文件尾部的写命令(恢复RDB之后的增量数据)。

核心优势

  • 恢复速度快(接近RDB);
  • 数据安全性高(接近AOF);
  • 文件体积适中(比纯AOF小)。

配置方式:aof-use-rdb-preamble yes(Redis 5.0+默认开启)。


五、缓存三大问题(面试压轴,必背)

在分布式系统中,使用Redis作为缓存时,会遇到穿透、击穿、雪崩三大问题,需掌握每种问题的现象、危害和解决方案。

1. 缓存穿透(Cache Penetration)

现象

用户频繁查询「一定不存在的数据」(如ID=-1的用户、不存在的商品),这类数据既不在缓存中,也不在数据库中,导致所有请求都直接打穿到数据库,造成数据库压力过大,甚至宕机。

危害

  • 数据库负载剧增,响应变慢;
  • 可能被恶意攻击(如大量查询不存在的ID),导致数据库宕机;
  • 缓存失去作用,所有请求都穿透到数据库。

解决方案(必背)

  1. 缓存空值:查询数据库发现数据不存在时,将空值(或特殊标记)缓存起来,并设置较短的过期时间(如5分钟),避免后续相同请求打穿到数据库;

    1. 注意:需避免缓存大量空值导致内存浪费,可设置空值的过期时间短一些。
  2. 布隆过滤器(Bloom Filter):

    1. 原理:将数据库中所有存在的key,提前存入布隆过滤器(一种概率数据结构);
    2. 查询时,先通过布隆过滤器判断key是否存在:若不存在,直接返回空,不查询缓存和数据库;若存在,再查询缓存和数据库;
    3. 优势:占用内存小,查询效率高(O(1));缺点:存在误判率(无法100%准确判断key是否存在)。
  3. 接口层参数校验:在接口入口处,过滤无效参数(如ID≤0、非法字符),直接拒绝无效请求,避免请求到达缓存和数据库。

  4. 黑名单限制:对频繁发送无效请求的IP/用户,加入黑名单,限制其访问。

2. 缓存击穿(Cache Breakdown)

现象

一个「热点key」(如热门商品、热门文章)过期时,大量请求同时访问该key,此时缓存中无数据,所有请求都直接打穿到数据库,导致数据库瞬间压力剧增。

与缓存穿透的区别:缓存穿透是查询“不存在的数据”,缓存击穿是查询“存在但过期的数据”。

危害

  • 数据库瞬间被大量请求冲击,可能导致数据库宕机;
  • 热点key对应的业务不可用,影响用户体验。

解决方案(必背)

  1. 互斥锁(Mutex Lock):

    1. 原理:当缓存失效时,只有一个请求能获取到锁,去查询数据库并更新缓存,其他请求则阻塞等待,直到缓存更新完成;
    2. 实现:使用Redis分布式锁(SET NX PX),查询数据库前获取锁,更新缓存后释放锁;
    3. 优势:简单可靠;缺点:会增加请求延迟,存在死锁风险(需设置锁过期时间)。
  2. 热点key永不过期:

    1. 原理:不设置热点key的过期时间,避免key过期;
    2. 注意:需在后台异步更新缓存(如定时任务),确保缓存数据与数据库数据一致;
    3. 优势:无请求阻塞,性能好;缺点:占用内存,需确保缓存数据及时更新。
  3. 逻辑过期:

    1. 原理:给key设置一个“逻辑过期时间”(存储在value中),不设置Redis的过期时间;
    2. 查询时,先判断逻辑过期时间是否到期:若未到期,直接返回缓存数据;若到期,获取互斥锁,异步更新缓存,其他请求仍返回旧数据;
    3. 优势:无请求阻塞,用户体验好;缺点:存在短时间内数据不一致的情况。

3. 缓存雪崩(Cache Avalanche)

现象

大量key在同一时间过期,或Redis集群宕机,导致所有请求都打穿到数据库,数据库瞬间被压垮,引发系统雪崩。

与缓存击穿的区别:缓存击穿是单个热点key过期,缓存雪崩是大量key同时过期或Redis宕机。

危害

  • 数据库宕机,整个系统不可用;
  • 引发连锁反应,其他依赖数据库的服务也会瘫痪;
  • 系统恢复成本高,影响业务正常运行。

解决方案(必背)

  1. 过期时间加随机值:给每个key的过期时间加上一个随机值(如1-300秒),避免大量key在同一时间过期;

  2. Redis集群高可用:

    1. 部署主从复制+哨兵模式,或Redis Cluster集群;
    2. 主节点宕机时,哨兵自动将从节点切换为主节点,保证Redis服务不中断。
  3. 多级缓存:

    1. 部署本地缓存(如Caffeine、EhCache)+ Redis分布式缓存;
    2. 请求先查询本地缓存,再查询Redis,最后查询数据库,即使Redis宕机,本地缓存也能承接部分请求。
  4. 服务降级与熔断:

    1. Redis宕机或缓存失效时,通过熔断器(如Sentinel、Resilience4j)拒绝部分请求,或返回兜底数据(如默认商品、提示信息);
    2. 避免大量请求打穿到数据库,保护数据库。
  5. 互斥锁:与缓存击穿的互斥锁逻辑类似,限制同时查询数据库的请求数量,避免数据库被瞬间冲击;

  6. 持久化保障:开启Redis混合持久化,确保Redis宕机后能快速恢复数据,减少服务不可用时间。


六、Redis 分布式锁(面试必写、必问)

在分布式系统中,多个服务节点需要竞争同一资源(如库存扣减、订单创建),需通过分布式锁保证操作的原子性,避免并发问题,Redis是实现分布式锁的常用方案。

1. 分布式锁的核心要求

  • 互斥性:同一时间,只有一个服务节点能获取到锁;
  • 安全性:不能出现死锁,锁必须能释放(即使服务宕机);
  • 唯一性:不能误释放别人的锁;
  • 高可用:Redis集群宕机时,锁服务仍能正常工作;
  • 原子性:锁的获取、释放操作必须是原子的。

2. 基于Redis的分布式锁实现(核心命令)

获取锁(核心命令)

SET lock_key unique_value NX PX 30000

命令解析(面试必背):

  • lock_key:锁的key(如lock:stock:1001,标识具体资源);
  • unique_value:唯一值(如UUID+服务IP),用于标识锁的持有者,避免误释放别人的锁;
  • NX(Not Exist):只有当lock_key不存在时,才能设置成功(保证互斥性);
  • PX 30000:设置锁的过期时间为30000毫秒(30秒),避免服务宕机导致死锁。

返回结果:

  • OK:获取锁成功;
  • nil:获取锁失败(锁已被其他节点持有)。

释放锁(核心逻辑:Lua脚本原子操作)

不能直接用DEL命令释放锁(会误释放别人的锁),必须先判断锁的持有者是否是自己,再释放,该操作需用Lua脚本保证原子性(Redis执行Lua脚本时,会阻塞其他命令,确保操作原子)。

--  KEYS[1] = lock_key,ARGV[1] = unique_value
if redis.call("get", KEYS[1]) == ARGV[1]
then
    -- 锁是自己的,释放锁
    return redis.call("del", KEYS[1])
else
    -- 锁不是自己的,不做操作
    return 0
end

Lua脚本优势:将“判断+删除”两个操作合并为一个原子操作,避免并发场景下的误释放问题。

3. 分布式锁的问题与解决方案(面试必问)

问题1:锁过期,任务还没执行完

现象:锁设置了30秒过期,但任务执行需要40秒,锁过期后,其他节点会获取到锁,导致并发问题。

解决方案:看门狗(Watchdog)自动续期

  • 原理:获取锁成功后,启动一个后台线程(看门狗),每隔10秒(锁过期时间的1/3),检查锁是否还在(自己持有),如果在,就将锁的过期时间重置为30秒;
  • 实现:Java中可通过Redisson框架实现(Redisson内置看门狗机制);
  • 注意:任务执行完成后,需手动关闭看门狗,避免不必要的续期。

问题2:主从切换,锁丢失

现象:主节点持有锁,数据还未同步到从节点,主节点宕机,从节点切换为主节点,新主节点中没有该锁,其他节点可获取到锁,导致并发问题