redis = Remote Dictionary Server,远程字典服务。redis是一个内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列(一般用消息列表都会使用Kafka)。它支持字符串,哈希表,列表,集合,有序结合等数据类型。
Strings (字符串)
最简单的类型,可以存储文本、数字(可进行自增/自减)或二进制数据。
字符数组,该字符串是动态字符串row,字符串长度小于1M时,加倍扩容;超过1M每次只多扩1M;字符串最大长度为512M;
注意:redis字符串是二进制安全字符串;可以存储图片,二进制协议等二进制数据;
基础命令
| 分类 | 命令 | 描述 | 示例 |
|---|---|---|---|
| 基本操作 | SET key value | 设置键值对 | SET user:1 "Alice" |
GET key | 获取键的值 | GET user:1 | |
DEL key | 删除键 | DEL user:1 | |
EXISTS key | 检查键是否存在 | EXISTS user:1 | |
| 数字操作 | INCR key | 将键的整数值增加1 | INCR views |
DECR key | 将键的整数值减少1 | DECR stock | |
INCRBY key increment | 将键的值增加指定的整数 | INCRBY score 5 | |
DECRBY key decrement | 将键的值减少指定的整数 | DECRBY stock 3 | |
| 高级操作 | MSET key1 value1 key2 value2 | 同时设置多个键值对 | MSET a 1 b 2 |
MGET key1 key2 | 同时获取多个键的值 | MGET a b | |
SETEX key seconds value | 设置键值对并指定过期时间(秒) | SETEX session:abc 3600 "data" | |
SETNX key value | 仅当键不存在时才设置其值(用于实现锁) | SETNX lock:resource true |
存储结构
字符串长度小于等于20且能转成整数,则使用 int 存储;
字符串长度小于等于44,则使用 embstr 存储;
字符串长度大于44,则使用 raw存储;
应用
对象存储
set role::1001 '{["name"]:"zhangsan",["sex"]:"male",["age"]:30}'
set role::1001 '{["name"]:"xishi",["sex"]:"female",["age"]:21}'
get role:1001
累加器
#累计加1
incr books
#累计加100
incrby books 100
分布式锁
setnx lock 1 #不存在才能设置,定义加锁行为,占用锁
位运算
# 签到功能,用户id 101 202509 2025年9月1日签到
setbit sign:101:202509 1 1
# 计算2025年9月份的签到情况
bitcount sign:101:202509
#获取 2025年9月份第二天签到情况 1为已签到 2为未签到
gitbit sign:101:202509 2
Hashes (哈希)
散列表,在很多高级语言中包含这种数据结构;c++ unordered_map 通过key快速索引value;
基础命令
适合存储对象,例如用户信息(用户ID、姓名、年龄等)。
| 分类 | 命令 | 描述 | 示例 |
|---|---|---|---|
| 字段操作 | HSET key field value | 设置哈希表中字段的值 | HSET user:1000 name "Bob" age 30 |
HGET key field | 获取哈希表中字段的值 | HGET user:1000 name | |
HDEL key field | 删除哈希表中的一个或多个字段 | HDEL user:1000 age | |
HGETALL key | 获取哈希表中所有的字段和值 | HGETALL user:1000 | |
| 批量操作 | HMGET key field1 field2 | 同时获取哈希表中多个字段的值 | HMGET user:1000 name age |
HMSET key field1 value1 field2 value2 | 同时设置哈希表中多个字段的值(HSET现在也支持) | HMSET user:1000 name "Bob" age 30 | |
| 其他操作 | HKEYS key | 获取哈希表中的所有字段名 | HKEYS user:1000 |
HVALS key | 获取哈希表中的所有字段值 | HVALS user:1000 | |
HINCRBY key field increment | 为哈希表中的整数字段值增加指定量 | HINCRBY user:1000 score 5 |
存储结构
节点数量大于512(hash-max-ziplist-entries)或所有字符串长度大于64(hash-max-ziplist-value),则使用dict 实现;节点数量小于等于512且有一个字符串长度小于64,则使用 ziplist 实现
应用
存储对象
hmset hash:1001 name zhangsan age 18 sex male
# 与string比较
set hash:1001 '{["name:]:"zhangsan",["sex"]:"male",["age"]:18}"
# 修改张三的年龄为19岁
# hash:
hset hash:1001 age 19
# string:
get hash:1001
# 将得到的字符串调用json解密,取出字段,修改age的值
# 再调用json加密
set hash:1001 '{["name"]:"zhangsan",["sex"]:"male",["age"]:19}'
购物车
# 用户id-key
# 商品id-field
# 商品数量-value
# 添加商品:
hmset MyCart:1001 40001 1 cost 5099 desc "笔记本电脑"
lpush MyItem:1001 40001
# 增加数量
hincrby MyCart:1001 40001 1
hincrby MyCart:1001 40001 -1
# 显示所有物品数量
hlen MyCart:1001
# 删除商品
hdel MyCart:1001 40001
lrem MyTtem:1001 1 40001
# 获取所有物品
lrange MyItem:1001
# 40001 40002 40003
heget MyCart:1001 40001
heget MyCart:1001 40002
heget MyCart:1001 40003
Lists (列表)
双向链表实现,列表首尾操作(增加和删除)时间复杂度为O(1);查找中间元素时间复杂度为O(n);列表中数据是否压缩的依据:
1.元素长度小于48,不压缩;
2.元素长度压缩前后长度不超过8,不压缩;
基础命令
字符串列表,按插入顺序排序,是双向链表实现。可作为栈、队列使用。
| 分类 | 命令 | 描述 | 数据结构类比 |
|---|---|---|---|
| 添加元素 | LPUSH key value | 将一个或多个值插入到列表头部(左边) | 左进 |
RPUSH key value | 将一个或多个值插入到列表尾部(右边) | 右进 | |
| 移除元素 | LPOP key | 移除并获取列表的第一个元素(左边) | 左出 |
RPOP key | 移除并获取列表的最后一个元素(右边) | 右出 | |
| 阻塞操作 | BLPOP key timeout | 移出并获取列表的第一个元素,如果列表没有元素会阻塞直到超时或有元素可用 | 阻塞队列 |
BRPOP key timeout | 移出并获取列表的最后一个元素,阻塞版本 | 阻塞队列 | |
| 查询 | LRANGE key start stop | 获取列表指定范围内的元素(0 到 -1 表示所有) | LRANGE mylist 0 -1 |
LLEN key | 获取列表长度 | LLEN mylist |
存储结构
双向循环列表
应用
栈(先进后出FILO)
lpush + lpop
rpush + rpop
队列(先进先出FIFO)
lpush + rpop
rpush + lpop
阻塞队列(blocking queue)
lpush + brpop
rpush + blpop
异步消息队列
操作与队列一样,但是在不同系统间;生产者和消费者;
Sets (集合)
集合:用来存储唯一性字段,不要求有序;
存储不需要有序,操作(交并差集的时候)排序
基础命令
String 类型的无序集合,元素唯一,不重复。适合存储唯一性数据和实现交集、并集等。
| 命令 | 描述 | 示例 |
|---|---|---|
SADD key member | 向集合添加一个或多个成员 | SADD tags redis db |
SREM key member | 移除集合中一个或多个成员 | SREM tags db |
SMEMBERS key | 获取集合中的所有成员 | SMEMBERS tags |
SISMEMBER key member | 判断 member 元素是否是集合的成员 | SISMEMBER tags redis |
SINTER key1 key2 | 返回多个集合的交集 | SINTER group:A group:B |
SUNION key1 key2 | 返回多个集合的并集 | SUNION group:A group:B |
SDIFF key1 key2 | 返回第一个集合与其他集合之间的差集 | SDIFF group:A group:B |
SCARD key | 获取集合的成员数 | SCARD tags |
存储结构
元素都为整数且节点数量小于等于512(set-max-insert-entries),则使用整数数组存储;
元素当中有一个不是整数或者节点数量大于512,则使用字典存储;
应用
抽奖
# 添加抽奖用户
sadd Award:1 1001 1002 1003 1004 1005 1006
sadd Award:1 1008
# 查看所有抽奖用户
smembers Award:1
# 抽取多名幸运用户
srandmember Award:1 10
共同关注
sadd follow:A liubei sunquan caocao zhugeliang
sadd follow:B liubei zhugeliang
sinter follow:A follow:B
推荐好友
sadd follow:A liubei sunquan caocao zhugeliang
sadd follow:B liubei zhugeliang
# B可能认识的人:
sdiff follow:A follow:B
Sorted Sets (有序集合)
有序集合;用来实现排行榜;有序唯一;
基础命令
与 Set 类似,也是 String 类型元素的集合,且不允许重复成员。但每个元素都会关联一个 score(分数),用于按分数从小到大排序。非常适合排行榜、带优先级的队列。
| 命令 | 描述 | 示例 |
|---|---|---|
ZADD key score member | 向有序集合添加一个或多个成员,或更新其分数 | ZADD leaderboard 100 "Alice" |
ZREM key member | 移除有序集合中的一个或多个成员 | ZREM leaderboard "Bob" |
ZRANGE key start stop [WITHSCORES] | 按分数从低到高返回索引范围内的成员(WITHSCORES会同时返回分数) | ZRANGE leaderboard 0 2 WITHSCORES |
ZREVRANGE key start stop [WITHSCORES] | 按分数从高到低返回索引范围内的成员(用于查排行榜前N名) | ZREVRANGE leaderboard 0 2 |
ZRANK key member | 返回成员在集合中的正序排名(从0开始) | ZRANK leaderboard "Alice" |
ZREVRANK key member | 返回成员在集合中的逆序排名 | ZREVRANK leaderboard "Alice" |
ZSCORE key member | 返回成员的分数 | ZSCORE leaderboard "Alice" |
ZCARD key | 获取有序集合的成员数 | ZCARD leaderboard |
存储结构
节点数量大于128或者有一个字符串长度大于64,则使用跳表(skiplist);
节点数量小于等于128(zset-max-ziplist-entries)且所有字符串长度小于等于64(zset-max-ziplist-value),则使用ziplist存储;
数量少的时候,节省空间;O(n);
数量多的时候,访问性能;O(1) or O(logn);
应用
热榜
# 点击新闻:
zincrby hot:20250922 1 10001
zincrby hot:20250922 1 10002
zincrby hot:20250922 1 10003
zincrby hot:20250922 1 10004
zincrby hot:20250922 1 10005
zincrby hot:20250922 1 10006
zincrby hot:20250922 1 10007
zincrby hot:20250922 1 10008
zincrby hot:20250922 1 10009
#获取排行榜:
zrevrange hot:20250922 0 9 withscores
延时队列
将消息序列化成一个字符串作为zset的member;这个消息的到期处理时间作为score,然后用多个线程轮询zset获取到期的任务进行处理。
def delay(msg):
msg.id= str(uuid.uuid4O) #保证member唯一
value = json.dumps (msg)
retry_ts=time.time()+5 # 5s后重试
redis.zadd("delay-queue",retry_ts,value)
# 使用连接池
def loop ():
while True:
values = redis.zrangebyscore("delay-queue",O, time.time(), start=O, num=1)
if not values:
time.sleep (1)
continue
value = values [O]
success = redis.zrem("delay-queue", value)
if success:
msg = json.loads (value)
handle_msg (msg)
#缺点:loop是多线程竞争,两个线程都从zrangebyscore获取到数据,但是zrem一个成功一个失败,
#优化:为了避免多余的操作,可以使用lua脚本原子执行这两个命令
# 解决:漏斗限流
分时式定时器
生产者将定时任务hash到不同的redis实体中,为每一个redis实体分配一个dispatcher进程,用来定时获取redis中超时事件并发布到不同的消费者中;
时间窗口限流
系统限定用户的某个行为在指定的时间范围内(动态)只能发生N次;
#指定用户user_id的某个行为action在特定时间内period只允许发生该行为做大次数max_count
local function is_action_allowed(red,userid,action,period,max_count)
local key = tab_concat({"hist",userid,action},":")
local now = zv.time()
red:init_pipeline()
-- 记录行为
red:zadd(key,now,now)
-- 移除时间窗口之前的行为记录,剩下的都是时问窗口内的记录
red:zremrangebyscore(key,O,now - period *100)
-- 获取时间窗口内的行为数量
red:zcard(key)
-- 设置过期时间,避免冷用户持续占用内存时间窗口的长度+1秒
red:expire(key,period + 1)
local res =red:commit_pipeline
return res[3]< max_count
end
#维护一次时间窗口,将窗口外的记录全部清理掉,只保图窗口内的记录;
#缺点:记录了所有时间窗口内的数据,如果这个量很大,不适合做这样的限流;漏斗限流;
#注意:如果用key+expire操作也能实现,但是实现的是熔断限流,这里是时间窗口限流的功能;
通用键命令 (适用于所有类型)
| 命令 | 描述 |
|---|---|
DEL key | 删除一个或多个key |
EXISTS key | 检查某个key是否存在 |
EXPIRE key seconds | 为key设置过期时间(秒) |
TTL key | 查看key剩余的生存时间(秒) |
TYPE key | 返回key所存储的数据类型 |