文章目录
1. 更多数据结构
1.1 Bitmaps(位图)
定义:位图是Redis中的一种特殊字符串类型,将字符串视为位数组,每个位可以存储0或1,用于高效存储大量布尔值。
应用场景:用户签到、用户活跃度统计、布隆过滤器。
Bitmaps 常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| SETBIT | SETBIT key offset value | key: 键名offset: 位偏移量(从0开始)value: 位的值(0或1) | 指定偏移量原来存储的位 | 用户签到、状态标记 |
| GETBIT | GETBIT key offset | key: 键名offset: 位偏移量 | 偏移量的位值(0或1) | 检查状态 |
| BITCOUNT | BITCOUNT key [start end] | key: 键名start: 字节起始位置end: 字节结束位置 | 被设置为1的位的数量 | 统计活跃用户数 |
| BITOP | BITOP operation destkey key [key...] | operation: 操作(AND/OR/XOR/NOT)destkey: 结果存储键key: 源键 | 结果字符串的长度(字节) | 多条件用户筛选 |
操作案例:用户签到
# 用户UID为1001的用户在2023年第100天签到
SETBIT sign:1001:2023 100 1
# 检查该用户在第100天是否签到
GETBIT sign:1001:2023 100 # 返回 1
# 统计该用户2023年总签到次数
BITCOUNT sign:1001:2023
1.2 HyperLogLog
定义:HyperLogLog是一种用于估计集合中唯一元素数量(基数)的算法,占用空间极小。
应用场景:大规模数据的去重计数,如网站的唯一访客(UV)统计。
重要提示:HyperLogLog的统计结果不是精确的,标准误差约为 0.81%,但可以接受。
HyperLogLog 常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| PFADD | PFADD key element [element...] | key: 键名element: 要添加的元素 | 如果至少有个元素被添加返回1,否则返回0 | 添加访问用户 |
| PFCOUNT | PFCOUNT key [key...] | key: 键名 | 基数的近似值 | 统计UV(独立访客) |
| PFMERGE | PFMERGE destkey sourcekey [sourcekey...] | destkey: 目标键sourcekey: 源键 | 返回OK | 合并多日数据 |
操作案例:UV 统计
# 用户1、2、3访问了网站
PFADD uv:20231001 "user1" "user2" "user3"
# 用户3、4、5访问了网站
PFADD uv:20231001 "user3" "user4" "user5"
# 统计20231001这天的独立访客数
PFCOUNT uv:20231001 # 返回 5 (user1, user2, user3, user4, user5)
1.3 Geospatial(地理空间)
定义:用于存储地理位置信息(经纬度),并支持范围查询、距离计算等操作。
应用场景:附近的人、摇一摇、打车软件附近的车辆。
Geospatial 常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| GEOADD | GEOADD key longitude latitude member [lon lat member...] | key: 键名longitude: 经度latitude: 纬度member: 成员名称 | 新添加的成员数量 | 添加地理位置 |
| GEOPOS | GEOPOS key member [member...] | key: 键名member: 成员名称 | 成员坐标的数组 | 获取位置坐标 |
| GEODIST | GEODIST key member1 member2 [unit] | key: 键名member1/member2: 成员unit: 单位(m/km/mi/ft) | 两个成员之间的距离 | 计算距离 |
| GEORADIUS | GEORADIUS key longitude latitude radius unit [options] | longitude/latitude: 中心点坐标radius: 半径unit: 单位options: WITHDIST/WITHCOORD等 | 范围内的成员列表 | 查找附近的人/地点 |
操作案例:附近的车
# 添加几辆车的坐标到 'cars' 集合
GEOADD cars 116.397128 39.916527 "car_A" 116.406315 39.910463 "car_B" 116.415296 39.911112 "car_C"
# 查找位于 (116.405285, 39.904989) 位置 2 公里范围内的车,并显示距离
GEORADIUS cars 116.405285 39.904989 2 km WITHDIST
# 1) 1) "car_B"
# 2) "0.6340"
# 2) 1) "car_C"
# 2) "1.2384"
1.4 Stream
定义:Redis 5.0 引入的新的数据类型,是一个更强大的、支持多播的、可持久化的消息队列。
应用场景:消息队列(替代 PUB/SUB 和 LIST 方案)、事件溯源。
核心概念:消息(由键值对组成)、条目ID(自动生成,格式为 <毫秒时间戳>-<序列号>)、消费者组(多个消费者共同消费一个流,实现负载均衡)。
Stream 常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| XADD | XADD key * field value [field value...] | key: 流键名*: 自动生成IDfield value: 字段值对 | 生成的条目ID | 生产消息 |
| XREAD | XREAD [COUNT count] [BLOCK ms] STREAMS key id | COUNT: 返回数量BLOCK: 阻塞时间(毫秒)key: 流键名id: 起始ID(0从开始,$从最新) | 消息列表 | 消费消息 |
| XGROUP | XGROUP CREATE key groupname id | key: 流键名groupname: 消费者组名id: 起始ID | OK | 创建消费者组 |
| XREADGROUP | XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] STREAMS key id | group: 消费者组consumer: 消费者名id: 消息ID(>表示新消息) | 分配给该消费者的消息 | 消费者组消费 |
操作案例:简单消息队列
# 生产者:发布消息
XADD mystream * sensor-id 1234 temperature 19.8
# 消费者:从头开始读取所有消息
XREAD STREAMS mystream 0
# 创建消费者组
XGROUP CREATE mystream mygroup 0
# 消费者组内的消费者 'consumer1' 读取消息,> 表示读取从未传递给其他消费者的新消息
XREADGROUP GROUP mygroup consumer1 COUNT 1 BLOCK 0 STREAMS mystream >
2. 持久化机制
2.1 RDB(Redis Database)
工作原理:在指定的时间间隔内,fork一个子进程,将内存中的数据集快照写入一个临时文件,写入完成后,替换之前的快照文件。
配置(在 redis.conf 中):
save 900 1 # 900秒(15分钟)内至少有1个key发生变化
save 300 10 # 300秒(5分钟)内至少有10个key发生变化
save 60 10000 # 60秒内至少有10000个key发生变化
dbfilename dump.rdb # RDB文件名称
dir ./ # RDB文件保存路径
优点:
- 性能高,fork 子进程进行持久化,主进程继续提供服务。
- 适合大规模数据恢复,且恢复速度比 AOF 快。
- 生成的 RDB 文件是紧凑的二进制文件,非常适合备份和灾难恢复。
缺点:
- 数据安全性较低,在两次快照之间发生故障,会丢失这段时间的数据。
fork()过程在数据量大时可能会阻塞主进程。
2.2 AOF(Append Only File)
工作原理:记录每一个对数据进行修改的写命令,以日志的形式追加到文件末尾。
同步策略(appendfsync 配置):
always:每个写命令都同步写入磁盘。数据最安全,性能最低。everysec:每秒同步一次。是默认策略,兼顾性能和安全,最多丢失1秒数据。no:由操作系统决定何时同步。性能最好,但数据安全性最差。
配置(在 redis.conf 中):
appendonly yes # 开启AOF
appendfilename "appendonly.aof" # AOF文件名称
appendfsync everysec # 同步策略
auto-aof-rewrite-percentage 100 # AOF文件增长比例超过100%时触发重写
auto-aof-rewrite-min-size 64mb # AOF文件体积大于64MB时触发重写
优点:
- 数据安全性高,根据策略不同,最多丢失1秒数据。
- AOF 文件易于理解和解析,因为它是一个纯文本的日志文件。
缺点:
- AOF 文件通常比 RDB 文件大。
- 数据恢复速度慢。
2.3 混合持久化(Redis 4.0+)
工作原理:结合了 RDB 和 AOF。在 AOF 重写时,子进程会将当前内存数据以 RDB 格式写入 AOF 文件的前半部分,然后再将重写缓冲区的增量命令以 AOF 格式写入文件后半部分。
优点:结合了 RDB 快速加载和 AOF 丢失数据少的优点。
配置:
aof-use-rdb-preamble yes # 开启混合持久化
3. 发布订阅、事务与Lua 脚本
3.1 发布订阅
定义:Redis 发布订阅(Pub/Sub)是一种消息通信模式,发送者(发布者)发送消息,订阅者接收消息。Redis 客户端可以订阅任意数量的频道。
核心概念:
- 频道(Channel):消息的类别,发布者向频道发布消息,订阅者订阅频道接收消息
- 模式(Pattern):支持通配符的频道匹配模式
- 消息:在发布者和订阅者之间传递的数据
特性:
- 实时性:消息发布后立即推送给所有订阅者
- 无持久化:消息不会持久化,离线订阅者无法收到历史消息
- 无确认机制:发布者不知道消息是否被成功接收
- 轻量级:实现简单,性能高
发布订阅常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| SUBSCRIBE | SUBSCRIBE channel [channel...] | channel: 频道名称 | 订阅确认信息 | 订阅一个或多个频道 |
| PUBLISH | PUBLISH channel message | channel: 频道名称message: 消息内容 | 接收到消息的客户端数量 | 向指定频道发布消息 |
| UNSUBSCRIBE | UNSUBSCRIBE [channel...] | channel: 频道名称 | 取消订阅确认 | 取消订阅频道 |
| PSUBSCRIBE | PSUBSCRIBE pattern [pattern...] | pattern: 模式(支持通配符) | 模式订阅确认 | 按模式订阅频道 |
| PUNSUBSCRIBE | PUNSUBSCRIBE [pattern...] | pattern: 模式 | 取消模式订阅确认 | 取消模式订阅 |
| PUBSUB | PUBSUB subcommand [argument...] | subcommand: 子命令 | 发布订阅系统状态 | 查看发布订阅状态 |
操作案例:
# 终端1:订阅者1订阅 news 频道
SUBSCRIBE news
# 终端2:订阅者2订阅 news 频道
SUBSCRIBE news
# 终端3:发布者向 news 频道发布消息
PUBLISH news "Hello, Redis Pub/Sub!"
PUBLISH news "This is a test message."
# 终端1和终端2都会收到这两条消息
3.2 事务
定义:Redis 事务允许一次性、按顺序地执行一组命令,并且在执行期间,事务中的命令会被序列化,不会被其他客户端的命令打断。
事务常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| MULTI | MULTI | 无参数 | 返回OK | 标记事务开始 |
| EXEC | EXEC | 无参数 | 事务块内所有命令的返回值数组 | 执行事务 |
| DISCARD | DISCARD | 无参数 | 返回OK | 取消事务 |
| WATCH | WATCH key [key...] | key: 要监视的键 | 返回OK | 实现乐观锁:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断 |
特性:
- 原子性:Redis 事务是原子的,意味着所有命令要么全部执行,要么全部不执行。
- 没有回滚:如果事务中的某个命令执行失败,其他命令依然会继续执行,Redis 不会自动回滚。开发者需要自己处理这种情况。
操作案例:
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
WATCH实现乐观锁:
import redis
import time
import threading
def safe_purchase_with_watch(product_id, user_id, quantity, max_retries=5):
"""
使用 WATCH 实现乐观锁的安全购买方法
参数:
product_id: 商品ID
user_id: 用户ID
quantity: 购买数量
max_retries: 最大重试次数
"""
r = redis.Redis(host='localhost', port=6379, db=0)
for attempt in range(max_retries):
try:
# 监视商品库存键
r.watch(f'product:{product_id}:stock')
# 获取当前库存
current_stock = int(r.get(f'product:{product_id}:stock'))
if current_stock < quantity:
r.unwatch()
print(f"用户 {user_id} 购买失败,库存不足,当前库存: {current_stock}")
return False
# 开始事务
pipeline = r.multi()
# 扣减库存
pipeline.decrby(f'product:{product_id}:stock', quantity)
# 执行事务
result = pipeline.execute()
if result is None:
# 事务执行失败,说明在 WATCH 期间库存被其他客户端修改
print(f"用户 {user_id} 第 {attempt + 1} 次尝试失败,正在重试...")
continue
else:
print(f"用户 {user_id} 成功购买 {quantity} 件商品,剩余库存: {result[0]}")
return True
except redis.WatchError:
print(f"用户 {user_id} 第 {attempt + 1} 次尝试发生 WatchError,正在重试...")
continue
except Exception as e:
print(f"用户 {user_id} 购买过程中发生错误: {e}")
r.unwatch()
return False
print(f"用户 {user_id} 经过 {max_retries} 次尝试后购买失败")
return False
# 测试乐观锁
def test_safe_purchase():
# 重置库存
r = redis.Redis(host='localhost', port=6379, db=0)
r.set('product:1001:stock', 10)
threads = []
for i in range(15): # 15个用户同时抢购10件商品
t = threading.Thread(target=safe_purchase_with_watch, args=(1001, i+1, 1))
threads.append(t)
t.start()
for t in threads:
t.join()
# 检查最终库存
final_stock = int(r.get('product:1001:stock'))
print(f"最终库存: {final_stock}")
# 运行测试
test_safe_purchase()
3.3 Lua 脚本
定义:在 Redis 服务器端原子性地执行复杂的 Lua 脚本。
优势:
- 原子性:整个脚本在执行期间不会被其他命令打断,是原子操作。
- 减少网络开销:可以将多个操作封装在一个脚本中,一次发送,一次返回。
- 复用:脚本会缓存在服务器,其他客户端可以复用。
Lua 脚本指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| EVAL | EVAL script numkeys key [key...] arg [arg...] | script: Lua脚本numkeys: 键的数量key: 键名arg: 参数 | Lua脚本的执行结果 | 执行原子操作 |
操作案例:实现一个简单的访问频率限制
-- KEYS[1] 是限流的key,ARGV[1] 是时间窗口,ARGV[2] 是最大次数
local key = KEYS[1]
local window = tonumber(ARGV[1])
local max = tonumber(ARGV[2])
local current = redis.call('GET', key)
if current and tonumber(current) > max then
return 0 -- 超过限制
end
current = redis.call('INCR', key)
if tonumber(current) == 1 then
redis.call('EXPIRE', key, window) -- 第一次调用时设置过期时间
end
return 1 -- 在限制内
# 在Redis中执行,60秒内最多允许5次访问
EVAL "上面的Lua脚本" 1 rate_limit:user123 60 5
4. 管理与运维
4.1 Key管理
key管理常用指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| KEYS | KEYS pattern | pattern: 匹配模式(* ? [abc]等) | 匹配的键列表 | 查找键(生产环境慎用) |
| SCAN | SCAN cursor [MATCH pattern] [COUNT count] | cursor: 游标(0开始)MATCH: 匹配模式COUNT: 每次返回数量 | 新的游标和键的数组 | 安全遍历键 |
| TTL | TTL key | key: 键名 | 剩余生存时间(秒) -1表示永不过期 -2表示键不存在 | 查看键过期时间 |
| EXPIRE | EXPIRE key seconds | key: 键名seconds: 过期时间(秒) | 1表示设置成功,0表示键不存在 | 设置键过期时间 |
4.2 性能监控
性能监控指令
| 指令 | 语法 | 参数说明 | 返回值 | 应用场景 |
|---|---|---|---|---|
| INFO | INFO [section] | section: 信息模块(server/memory/stats等) | Redis服务器信息 | 监控服务器状态 |
| CONFIG | CONFIG GET parameter | parameter: 配置参数名 | 配置参数的值 | 查看配置 |
4.3 内存优化
理解内存消耗:Redis 所有数据存储在内存中。内存消耗主要包括:数据本身、进程开销、复制缓冲区等。
内存管理配置
redis.conf 中相关参数
| 参数 | 配置参数 | 参数说明 | 可选值 | 作用 |
|---|---|---|---|---|
| maxmemory | maxmemory <bytes> | 最大内存限制 | 如 1gb, 512mb | 设置内存使用上限 |
| maxmemory-policy | maxmemory-policy policy | 内存淘汰策略 | volatile-lruallkeys-lruvolatile-randomallkeys-randomvolatile-ttlnoeviction | 内存满时的处理策略 |
内存淘汰策略说明:
volatile-lru:从已设置过期时间的键中,移除最近最少使用的键allkeys-lru:从所有键中,移除最近最少使用的键(推荐使用)volatile-random:从已设置过期时间的键中,随机移除一个键allkeys-random:从所有键中,随机移除一个键volatile-ttl:从已设置过期时间的键中,移除剩余生存时间最短的键noeviction:不删除任何键,在写操作时返回错误(默认策略)

667

被折叠的 条评论
为什么被折叠?



