《Redis》专题学习记录

张开发
2026/4/8 6:55:46 15 分钟阅读

分享文章

《Redis》专题学习记录
Redis几种数据类型和使用场景String场景缓存数据、计数器(利用 INCR 和 DECR 命令实现)、存储会话信息(用户的会话信息如登录状态、权限信息等)value 最大可以存储 512MB 的数据.SET key value、GET key、INCR key、DECR key、APPEND key value(‘oldValue’-‘oldValueNewValue’)Hash场景存储对象信息、商品详情其中 value 又是一个键值对field - value的结构1、HSET key field value将哈希表 key 中的字段 field 的值设为 value。例HSET user:100 name “zhangsan” age “25” gender “male”。2、HGET key field获取哈希表 key 中指定字段 field 的值。例HGET user:100 name返回 “zhangsan”。3、HGETALL key获取哈希表 key 中所有的字段和值。List场景消息队列LPUSH/RPOP、最新公告列表LPUSH/LRANGE 0 9List 类型是一个双向链表结构它可以存储有序的字符串元素。List 类型的底层实现在元素数量较少时使用 ziplist压缩列表当元素数量较多或元素体积较大时会自动转换为 linkedlist双向链表。1、LPUSH key value1 [value2…]将一个或多个值插入到列表 key 的表头(左端)。(RPUSH 右端。2、LPOP key移除并返回列表 key 的表头元素。(RPOP key表尾元素)Set场景数据去重、好友关系管理、抽奖活动参与者Set 类型是一个无序的、不允许重复元素的集合。它支持交集、并集、差集等集合运算适合存储去重数据以及进行数据之间的关联操作。Set 类型的底层实现是一个哈希表因此添加、删除、查找元素的时间复杂度都是 O (1)。1、SADD key member1 [member2…]将一个或多个成员元素加入到集合 key 中。SADD tags “redis” “java” “mysql”。2、SMEMBERS key返回集合 key 中的所有成员元素。SMEMBERS tags返回 “redis”、“java”、“mysql”。3、SINTER key1 [key2…]返回所有给定集合的交集。例如有集合 Akey 为 setA成员为 “a”、“b”、“c”和集合 Bkey 为 setB成员为 “b”、“c”、“d”执行SINTER setA setB返回 {“b”“c”}。4、SUNION key1 [key2…]返回所有给定集合的并集。SUNION setA setB返回 {“a”“b”“c”“d”}。5、SDIFF key1 [key2…]返回给定集合之间的差集即 key1 中存在但其他集合中不存在的元素。SDIFF setA setB返回 {“a”}。Sorted Set场景排行榜系统、范围查询、带优先级任务队列不允许重复可以按照分数有序排列。底层实现是跳表(Skip List)和哈希表的结合跳跃表用于保证元素的有序性哈希表用于快速查找元素因此它既支持按照分数范围获取元素也支持快速查找元素的分数和排名。1、ZADD key score1 member1 [score2 member2…]将一个或多个成员元素及其分数值加入到有序集合 key 中。ZADD ranking 95 “zhangsan” 88 “lisi” 92 “wangwu”。2、ZRANGE key start stop [WITHSCORES]返回有序集合 key 中指定索引范围内的成员。ZRANGE ranking 0 -1 WITHSCORES返回 [“lisi”“88”“wangwu”“92”“zhangsan”“95”]。Bitmap场景用户行为统计、活跃用户分析、权限控制HyperLogLog场景网站 UV 统计、APP 日活 / 月活统计、大数据量集合基数统计Geo场景附近的人 / 地点推荐、地理位置距离计算、区域范围筛选基于 Sorted Set 实现通过将经纬度坐标映射为一个 64 位的整数GeoHash 编码作为 Sorted Set 的 score分布式锁是什么怎么设计Redis中怎么实现分布式锁1、Redis 分布式锁是一种在分布式系统中实现资源互斥访问的机制通过 Redis 的特性确保多个进程/服务在访问共享资源时的协调性2、Redis 分布式锁的核心设计目标互斥性同一时刻只有一个客户端能持有锁。安全性锁必须由加锁的客户端释放防止其他客户端误删。避免死锁即使客户端崩溃锁必须能自动释放通过过期时间。高可用Redis 集群故障时仍能正常工作需特殊设计。3、加锁操作SETresource_name unique_valueNXEX30//unique_value唯一标识如 UUID用于安全释放锁。4、解锁操作通过 Lua 脚本保证原子性检查值 删除ifredis.call(get,KEYS[1])ARGV[1]thenreturnredis.call(del,KEYS[1])elsereturn0endRedis事务是如何工作的事务执行流程Redis 事务通过 MULTI、EXEC、DISCARD 三个关键命令控制1、MULTI标记事务开始后续命令会被放入队列而非立即执行。MULTIOK2、命令入队输入的所有命令会按顺序缓存在服务端的队列中返回 QUEUED 表示成功入队。SETkey1AQUEUEDINCRkey2QUEUED3、EXEC执行队列中的所有命令返回所有命令的结果按顺序。EXEC1)OK#SET的结果2)(integer)1#INCR的结果4、DISCARD取消事务清空队列中的命令。MULTIOKSETkey3BQUEUEDDISCARDOK原子性所有命令一起执行EXEC 触发后队列中的命令会按顺序一次性执行不会被其他客户端命令打断。无回滚机制若某个命令执行失败如对字符串执行 INCR不会影响其他命令的执行也不会回滚已执行的命令。MULTIOKSETkey1abcQUEUEDINCRkey1 # 对字符串执行INCR会报错QUEUEDEXEC1)OK2)(error)ERRvalue is not an integer or out of range错误处理1、入队时错误语法错误场景命令语法错误如命令不存在、参数错误处理Redis 会直接拒绝入队后续 EXEC 时整个事务被丢弃MULTIOKSETkey1AQUEUEDINCRkey1 key2 # 错误INCR只能接受一个参数(error)ERRwrong number of argumentsforincrcommandEXEC(error)EXECABORTTransactiondiscarded because of previous errors.2、执行时错误逻辑错误场景命令语法正确但执行失败如对字符串执行 INCR。处理错误命令自身失败但其他命令继续执行。SETkey1abcOKMULTIOKINCRkey1QUEUEDSETkey2BQUEUEDEXEC1)(error)ERRvalue is not an integer or out of range2)OK#SETkey2 仍执行成功乐观锁WATCH 命令Redis 通过 WATCH 实现乐观锁用于在事务执行前检测键是否被修改避免脏写。1、工作流程WATCHbalance//1、监视键使用 WATCH 监视一个或多个键。OK//2、开启事务执行 MULTI 和后续命令。//执行事务若被 WATCH 的键在 WATCH 后到 EXEC 前被修改事务会被放弃返回 nil。// 若未被修改事务正常执行。示例账户扣款# 客户端1WATCHbalanceOKGETbalance100MULTIOKDECRBYbalance50QUEUED# 客户端2在客户端1执行EXEC前修改 balanceINCRBYbalance20(integer)120# 客户端1执行事务EXEC(nil)# 事务失败因 balance 被修改Redis 事务通过命令队列和 WATCH 实现简单的事务模型适用于对原子性要求不严格的场景。若需要强一致性需结合业务逻辑如重试机制或使用其他方案如 Lua 脚本。Redission锁优点分布式锁实现原理Redisson 的分布式锁通过 Lua 脚本原子性、Watchdog 自动续期、可重入设计 等机制解决了原生 Redis 锁的常见问题如锁提前释放、不可重入。其核心优势在于1、高可靠性自动处理锁续期和异常场景。2、功能丰富支持公平锁、联锁、红锁等多种模式。3、高性能基于事件通知的锁竞争机制减少 Redis 压力。对于高并发分布式场景Redisson 是比直接使用 Redis 命令更成熟的选择。Redisson 分布式锁的核心优点1、可重入性Reentrant Lock优点允许同一线程多次获取同一把锁避免因递归调用或重复加锁导致的死锁。实现细节使用 Redis 的 Hash 结构存储锁信息Key 为锁名称Field 为客户端唯一标识UUID 线程IDValue 为重入次数。每次加锁时重入次数递增解锁时递减归零后删除锁。2、自动续期Watchdog 机制优点防止业务执行时间超过锁的过期时间导致锁提前释放。实现细节加锁成功后启动一个后台守护线程Watchdog默认每隔 10 秒锁超时时间的 1/3检查锁状态。若锁仍被当前线程持有则通过 pexpire 命令将锁的过期时间重置为初始值默认 30 秒。3、多种锁类型支持公平锁Fair Lock按请求顺序分配锁避免线程饥饿。实现方式通过 Redis 的 队列 和 发布订阅 机制维护请求顺序。联锁MultiLock同时向多个锁加锁全部成功才算加锁成功。适用场景需同时操作多个资源的原子性操作。红锁RedLock基于多 Redis 节点的高可用锁半数以上节点加锁成功才算有效。解决单点故障问题原生 Redis 主从架构的锁丢失风险。4、高性能与低竞争发布订阅机制客户端通过订阅 Redis 频道监听锁释放事件而非轮询查询锁状态减少 Redis 压力。Lua 脚本原子性所有加锁、解锁、续期操作均通过 Lua 脚本保证原子性避免竞态条件。5、容错与高可用支持 Redis 集群模式自动处理节点故障转移兼容 Redis Cluster 和主从架构。异常处理客户端与 Redis 断开连接时Watchdog 停止续期锁最终自动过期释放。6、易用性与灵活性API 简洁提供类似 Java 原生锁的接口如 lock(), unlock(), tryLock()。可配置参数支持自定义锁超时时间、等待时间、重试策略等。分布式锁实现1、加锁流程2.1、生成唯一标识使用 UUID 线程ID 作为客户端唯一标识如 b983c153-4801-4915-8ba3-8ae9059a755a:1。Hash 结构存储锁信息//Key锁名称(myLock),Field客户端唯一标识,Value重入次数。HSETmyLockb983c153-...-a755a:112.2、Lua 脚本原子加锁锁不存在时加锁锁已存在且属于当前线程重入次数 1返回锁剩余过期时间供客户端重试2.3、启动 Watchdog 线程若未显式指定锁超时时间启动后台线程定期续期。(默认锁超时 30 秒)Watchdog 续期间隔超时时间的 1/3默认 10 秒确保续期及时。2、解锁流程2.1、Lua 脚本原子解锁检查锁是否属于当前线程重入次数 -12.2、发布解锁通知通过 Redis 的PUBLISH命令通知其他等待客户端触发它们重新尝试加锁。3、锁竞争处理订阅锁释放事件客户端在加锁失败后订阅锁对应的 Redis 频道等待解锁通知。收到通知后立即尝试加锁减少无效的轮询请求。4、注意事项避免手动设置超时时间若调用 lock(10, TimeUnit.SECONDS)Watchdog 将不生效需自行确保业务在 10 秒内完成。确保解锁必须在 finally 块中调用 unlock()防止线程异常导致死锁。锁名称唯一性不同业务使用不同的锁名称避免冲突。WatchDog看门狗机制时间轮算法原理WatchDog 是一种用于自动续期分布式锁的机制工作原理1、锁获取时启动客户端成功获取锁后启动一个后台线程看门狗。2、周期性续期每隔一定时间如锁过期时间的 1/3向 Redis 发送续期命令如 PEXPIRE。3、续期条件仅当客户端仍持有锁即 Redis 中锁的值与客户端标识匹配时执行续期。4、自动终止业务逻辑执行完毕释放锁后看门狗线程终止。时间轮算法Time Wheel1、核心作用时间轮是一种高效管理定时任务的算法用于处理大量延迟或周期性任务如超时控制、心跳检测。Netty、Kafka 等框架广泛使用。2、数据结构**环形数组**时间轮由多个槽位Bucket组成的环形结构每个槽位对应一个时间间隔如 100ms。**时间指针**按固定间隔tick顺时针移动指向当前槽位。**任务链表**每个槽位挂载一个链表存储到期时间落在该槽位时间范围内的任务。3、工作原理任务添加根据任务的到期时间计算其应放入的槽位。槽位索引(当前指针位置延迟时间/tick)%槽位数时间推进由外部驱动如独立线程每隔一个 tick 移动指针触发当前槽位的任务执行。层级时间轮若任务延迟超过时间轮范围使用多层时间轮类似时钟的时、分、秒针。Redis数据结构、扩容数据结构1、字符串String底层结构简单动态字符串SDSSimple Dynamic String。struct sdshdr{intlen;// 已用长度intfree;// 剩余空间charbuf[];// 字节数组兼容 C 字符串};2、列表List底层结构QuickList3.2 版本后由多个 Ziplist 组成的双向链表。Ziplist 结构连续内存块存储多个元素每个元素包含前驱长度prevlen和当前长度encoding content。优点内存紧凑减少碎片缺点插入/删除需内存重分配。QuickList 设计每个节点为一个 Ziplist平衡内存效率与操作性能。通过 list-max-ziplist-size 控制单个 Ziplist 的最大容量默认 8KB。3、哈希Hash底层结构Ziplist当元素数量 ≤ hash-max-ziplist-entries默认 512且所有值长度 ≤ hash-max-ziplist-value默认 64 字节时使用。Hashtable不满足上述条件时转换为字典Dict。Hashtable 实现渐进式 Rehash扩容时同时存在两个哈希表ht[0] 和 ht[1]逐步迁移数据。哈希冲突链地址法解决冲突新元素插入链表头部。4、集合Set底层结构Intset当元素全为整数且数量 ≤ set-max-intset-entries默认 512时使用。Hashtable不满足条件时转换为字典值存储为 NULL。Intset 结构动态编码根据元素大小选择 int16_t、int32_t 或 int64_t。有序数组支持二分查找插入时维持有序性。5、有序集合ZSet底层结构Ziplist当元素数量 ≤ zset-max-ziplist-entries默认 128且所有值长度 ≤ zset-max-ziplist-value默认 64 字节时使用。跳跃表SkipList 字典Dict不满足条件时使用实现 O(1) 查找Dict和范围查询SkipList。跳跃表结构多层链表节点随机层高幂次定律高层链表跨越多个节点加速查询。排序依据分值score → 成员member字典序。扩容1、内存扩容哈希表的渐进式 Rehash扩容步骤1、分配新哈希表大小为第一个 ≥ used * 2 的 2^n如 used3新 size8。2、设置 rehash 索引rehashidx 0表示开始迁移。3、渐进式迁移每次对哈希表的操作增删改查触发迁移一个桶bucket的数据。定时任务serverCron在 1ms 内迁移 100 个桶。4、完成迁移当 rehashidx -1释放旧哈希表。访问逻辑查询时同时检查两个哈希表先查旧表再查新表。新增操作直接写入新表。过期策略 淘汰策略Redis高可用方案-持久化、主从哨兵、集群

更多文章