Redis核心进阶

1. 更多数据结构

1.1 Bitmaps(位图)

定义:位图是Redis中的一种特殊字符串类型,将字符串视为位数组,每个位可以存储0或1,用于高效存储大量布尔值。

应用场景:用户签到、用户活跃度统计、布隆过滤器。

Bitmaps 常用指令

指令语法参数说明返回值应用场景
SETBITSETBIT key offset valuekey: 键名
offset: 位偏移量(从0开始)
value: 位的值(0或1)
指定偏移量原来存储的位用户签到、状态标记
GETBITGETBIT key offsetkey: 键名
offset: 位偏移量
偏移量的位值(0或1)检查状态
BITCOUNTBITCOUNT key [start end]key: 键名
start: 字节起始位置
end: 字节结束位置
被设置为1的位的数量统计活跃用户数
BITOPBITOP 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 常用指令

指令语法参数说明返回值应用场景
PFADDPFADD key element [element...]key: 键名
element: 要添加的元素
如果至少有个元素被添加返回1,否则返回0添加访问用户
PFCOUNTPFCOUNT key [key...]key: 键名基数的近似值统计UV(独立访客)
PFMERGEPFMERGE 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 常用指令

指令语法参数说明返回值应用场景
GEOADDGEOADD key longitude latitude member [lon lat member...]key: 键名
longitude: 经度
latitude: 纬度
member: 成员名称
新添加的成员数量添加地理位置
GEOPOSGEOPOS key member [member...]key: 键名
member: 成员名称
成员坐标的数组获取位置坐标
GEODISTGEODIST key member1 member2 [unit]key: 键名
member1/member2: 成员
unit: 单位(m/km/mi/ft)
两个成员之间的距离计算距离
GEORADIUSGEORADIUS 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/SUBLIST 方案)、事件溯源。

核心概念消息(由键值对组成)、条目ID(自动生成,格式为 <毫秒时间戳>-<序列号>)、消费者组(多个消费者共同消费一个流,实现负载均衡)。

Stream 常用指令

指令语法参数说明返回值应用场景
XADDXADD key * field value [field value...]key: 流键名
*: 自动生成ID
field value: 字段值对
生成的条目ID生产消息
XREADXREAD [COUNT count] [BLOCK ms] STREAMS key idCOUNT: 返回数量
BLOCK: 阻塞时间(毫秒)
key: 流键名
id: 起始ID(0从开始,$从最新)
消息列表消费消息
XGROUPXGROUP CREATE key groupname idkey: 流键名
groupname: 消费者组名
id: 起始ID
OK创建消费者组
XREADGROUPXREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] STREAMS key idgroup: 消费者组
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):支持通配符的频道匹配模式
  • 消息:在发布者和订阅者之间传递的数据

特性:

  • 实时性:消息发布后立即推送给所有订阅者
  • 无持久化:消息不会持久化,离线订阅者无法收到历史消息
  • 无确认机制:发布者不知道消息是否被成功接收
  • 轻量级:实现简单,性能高

发布订阅常用指令

指令语法参数说明返回值应用场景
SUBSCRIBESUBSCRIBE channel [channel...]channel: 频道名称订阅确认信息订阅一个或多个频道
PUBLISHPUBLISH channel messagechannel: 频道名称
message: 消息内容
接收到消息的客户端数量向指定频道发布消息
UNSUBSCRIBEUNSUBSCRIBE [channel...]channel: 频道名称取消订阅确认取消订阅频道
PSUBSCRIBEPSUBSCRIBE pattern [pattern...]pattern: 模式(支持通配符)模式订阅确认按模式订阅频道
PUNSUBSCRIBEPUNSUBSCRIBE [pattern...]pattern: 模式取消模式订阅确认取消模式订阅
PUBSUBPUBSUB 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 事务允许一次性、按顺序地执行一组命令,并且在执行期间,事务中的命令会被序列化,不会被其他客户端的命令打断。

事务常用指令

指令语法参数说明返回值应用场景
MULTIMULTI无参数返回OK标记事务开始
EXECEXEC无参数事务块内所有命令的返回值数组执行事务
DISCARDDISCARD无参数返回OK取消事务
WATCHWATCH 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 脚本指令

指令语法参数说明返回值应用场景
EVALEVAL 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管理常用指令

指令语法参数说明返回值应用场景
KEYSKEYS patternpattern: 匹配模式(* ? [abc]等)匹配的键列表查找键(生产环境慎用)
SCANSCAN cursor [MATCH pattern] [COUNT count]cursor: 游标(0开始)
MATCH: 匹配模式
COUNT: 每次返回数量
新的游标和键的数组安全遍历键
TTLTTL keykey: 键名剩余生存时间(秒)
-1表示永不过期
-2表示键不存在
查看键过期时间
EXPIREEXPIRE key secondskey: 键名
seconds: 过期时间(秒)
1表示设置成功,0表示键不存在设置键过期时间

4.2 性能监控

性能监控指令

指令语法参数说明返回值应用场景
INFOINFO [section]section: 信息模块(server/memory/stats等)Redis服务器信息监控服务器状态
CONFIGCONFIG GET parameterparameter: 配置参数名配置参数的值查看配置

4.3 内存优化

理解内存消耗:Redis 所有数据存储在内存中。内存消耗主要包括:数据本身、进程开销、复制缓冲区等。

内存管理配置
redis.conf 中相关参数

参数配置参数参数说明可选值作用
maxmemorymaxmemory <bytes>最大内存限制1gb, 512mb设置内存使用上限
maxmemory-policymaxmemory-policy policy内存淘汰策略volatile-lru
allkeys-lru
volatile-random
allkeys-random
volatile-ttl
noeviction
内存满时的处理策略

内存淘汰策略说明:

  • volatile-lru:从已设置过期时间的键中,移除最近最少使用的键
  • allkeys-lru:从所有键中,移除最近最少使用的键(推荐使用
  • volatile-random:从已设置过期时间的键中,随机移除一个键
  • allkeys-random:从所有键中,随机移除一个键
  • volatile-ttl:从已设置过期时间的键中,移除剩余生存时间最短的键
  • noeviction:不删除任何键,在写操作时返回错误(默认策略
内容简介: Redis的的是完全开源免费的,遵守BSD协议,是一个高性能的键值数据库。是当前最热门的的的NoSql数据库之一,也被人们称为数据结构服务器。本课程从Redis基本数据类型开始,了解不同数据类型的用法和底层实现 。进一步学习Redis的一些高级特性与工作原理。了解Redis在分布式环境中的工作方式,和实际项目的使用及问题解决。 为什么学Redis? 原因很简单,快!这个问题在大并发,高负载的网站中必须考虑.redis数据库中的所有数据都存储在内存中。由于内存的读写速度远快于硬盘,因此Redis在性能上对比其他基于硬盘存储的数据库有非常明显的优势。项目中使用Redis,主要是从两个角度去考虑:性能状语从句:并发。当然,Redis的的的还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件代替,并不是非要使用Redis的的的。因此,这个问题主要从性能和并发两个角度去答。性能:我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存,这样,后面的请求就去缓存中读取,请求使得能够迅速响应。 并发: 在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用的的Redis的做一个缓冲操作,让请求先访问到的Redis的的,而不是直接访问数据库。redis优势:1.运行在内存,速度快官方号称支持并发11瓦特读操作,并发8瓦特写操作,可以说是相当彪悍了。2.数据虽在内存,但是提供了持久化的支持,即可以将内存中的数据异步写入到硬盘中,同时不影响继续提供服务3.支持数据结构丰富(string(字符串),list(链表),set(集合),zset(sorted set - 有序集合))和Hash(哈希类型,md5加密出来的那个串)课程大纲: 为了让大家快速系统了解Redis核心知识全貌,我为你总结了「Redis核心框架图」,帮你梳理学习重点,建议收藏!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值