元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Redis核心
▶
数据结构
字符串实现
哈希表实现
列表实现
集合实现
有序集合实现
▶
内存管理
内存分配策略
淘汰算法
▶
持久化
▶
RDB机制
快照生成原理
文件格式解析
▶
AOF机制
命令追加策略
重写过程分析
▶
高可用
▶
主从复制
SYNC原理
增量复制
▶
哨兵机制
故障检测
领导选举
▶
高级特性
▶
事务系统
ACID实现
WATCH原理
▶
Lua脚本
沙盒环境
执行管道
▶
实战问题
▶
缓存问题
缓存雪崩
缓存穿透
缓存击穿
缓存预热
▶
数据一致性
读写一致性
双写一致性
▶
性能优化
大key处理
热点key优化
发布时间:
2025-03-22 10:36
↑
☰
# Redis集合(Set)实现原理 ## 引言 Redis集合是一个无序的字符串集合,它提供了高效的成员检测和集合运算功能。本文将深入探讨Redis集合的实现原理、内部编码方式以及实际应用场景,帮助读者更好地理解和使用这一数据结构。 ## 底层实现 ### 整数集合(IntSet) 当集合中的元素都是整数且元素数量较少时,Redis会使用整数集合作为集合的底层实现: ```c typedef struct intset { uint32_t encoding; // 编码方式 uint32_t length; // 集合包含的元素数量 int8_t contents[]; // 保存元素的数组 } intset; ``` ### 整数集合的升级 当添加的新元素类型比现有元素类型的长度更长时,整数集合需要进行升级: 1. **升级过程** - 根据新元素的类型,扩展底层数组空间 - 将现有元素转换为新类型,并放置到正确的位置 - 添加新元素 2. **特点** - 升级是不可逆的 - 升级操作的时间复杂度为O(n) - 支持的整数类型:int16_t、int32_t、int64_t ### 哈希表实现 当集合不满足整数集合的条件时,Redis使用哈希表作为集合的底层实现: ```c typedef struct dict { dictType *type; // 类型特定函数 void *privdata; // 私有数据 dictht ht[2]; // 哈希表 long rehashidx; // rehash索引 int iterators; // 当前正在使用的迭代器数量 } dict; ``` ## 内部编码 Redis集合有两种内部编码方式: ### 1. intset(整数集合) 当集合满足以下条件时,使用intset编码: - 集合中的元素都是整数 - 集合的元素数量不超过512个 ```redis SADD numbers 1 2 3 4 5 ``` ### 2. hashtable(哈希表) 当不满足intset的条件时,使用hashtable编码: ```redis SADD mixed "string" 1 "another_string" ``` ## 常用命令 ### 基本操作 ```redis # 添加元素 SADD key member [member ...] # 删除元素 SREM key member [member ...] # 判断元素是否存在 SISMEMBER key member # 获取集合中的所有元素 SMEMBERS key ``` ### 集合运算 ```redis # 交集 SINTER key [key ...] # 并集 SUNION key [key ...] # 差集 SDIFF key [key ...] # 将运算结果保存 SINTERSTORE destination key [key ...] SUNIONSTORE destination key [key ...] SDIFFSTORE destination key [key ...] ``` ### 随机操作 ```redis # 随机返回元素 SRANDMEMBER key [count] # 随机弹出元素 SPOP key [count] ``` ## 应用场景 ### 1. 用户标签系统 ```redis # 为用户添加标签 SADD user:1:tags "python" "redis" "mysql" # 获取用户的所有标签 SMEMBERS user:1:tags # 查找共同兴趣的用户 SINTER user:1:tags user:2:tags ``` ### 2. 网站访客统计 ```redis # 记录每日访客IP SADD visitors:20230901 "192.168.1.1" "192.168.1.2" # 获取访客数量 SCARD visitors:20230901 # 获取连续多天的独立访客 SUNION visitors:20230901 visitors:20230902 ``` ### 3. 抽奖系统 ```redis # 添加抽奖用户 SADD lottery:users "user1" "user2" "user3" # 随机抽取一名中奖用户 SPOP lottery:users # 随机抽取多名用户 SPOP lottery:users 3 ``` ### 4. 好友关系 ```redis # 添加好友关系 SADD user:1:friends 2 3 4 SADD user:2:friends 1 3 # 查找共同好友 SINTER user:1:friends user:2:friends # 推荐可能认识的人(好友的好友) SUNION user:2:friends user:3:friends user:4:friends ``` ## 性能优化 ### 1. 内存优化 - 优先使用整数集合 - 及时清理无用数据 - 合理设置集合大小 ```redis # 获取集合大小 SCARD key # 及时清理过期数据 DEL old_set_key ``` ### 2. 集合运算优化 - 小集合在前,大集合在后 - 使用SSCAN代替SMEMBERS - 注意并集运算的内存消耗 ```redis # 使用SSCAN遍历大集合 SSCAN key 0 COUNT 100 ``` ### 3. 批量操作 优先使用批量命令: ```redis # 优先使用 SADD myset "a" "b" "c" # 而不是多次 SADD myset "a" SADD myset "b" SADD myset "c" ``` ## 注意事项 1. **内存管理** - 注意集合大小对内存的影响 - 合理使用整数集合编码 - 避免存储过大的元素 2. **集合运算** - 注意运算结果的内存占用 - 大集合运算考虑使用STORE命令 - 避免对大集合做频繁的交集运算 3. **并发操作** - 单个命令是原子的 - 多个命令需要使用事务 - 注意集合运算的原子性 4. **数据一致性** - 考虑使用WATCH机制 - 重要操作使用事务 - 注意数据过期策略 ## 总结 Redis集合通过整数集合和哈希表两种数据结构实现,提供了高效的成员检测和丰富的集合运算功能。它适用于标签系统、访客统计、抽奖系统等场景。在使用时需要注意内存管理、性能优化和并发操作等问题,合理运用可以充分发挥Redis集合的优势。