一、Redis 安装

  • 参考安装文档,安装文档最后一步修改密码是临时密码,重启后就会恢复默认密码
  • 安装好 Redis 的默认密码为空
  • 如果想永久修改密码,打开 Redis 安装目录下的redis.windows-service.conf,找到# requirepass foobared ,设置密码
requirepass root
  • Redis 安装完成后,默认有 16 个数据库,索引值从 0-15,默认选择第 0 个
  • Redis 图形化界面工具可以下载 redis-desktop,或者直接用 navicat

1、基本使用

  • 略

2、python API

  • 通过 python 相关库来操作 Redis
  • 安装redis库
pip install -i https://repo.huaweicloud.com/repository/pypi/simple/ redis 

2.1 连接

  • 连接成功输出 True
import redis

r = redis.Redis(
    host="127.0.0.1",  # Redis 服务器地址
    port=6379,  # Redis 端口号
    db=0,  # Redis 数据库编号
    password="root",  # Redis 密码
    decode_responses=True,  # 让 Redis 返回字符串,而不是字节
)
print(r.ping())
  • 封装连接工具:RedisUtil
import redis


def redis_conn():
    return redis.Redis(
        host="127.0.0.1",
        port=6379,
        db=0,
        password="root",
        decode_responses=True,  # 返回结果为字符串
    )

2.2 字符串操作

  • 字符串操作是 Redis 中用的最多的操作
  • 常用方法
方法 功能 参数 说明
set(name, value, ex=None, px=None, nx=False, xx=False) 设置字符串类型的 key-value name:keyvalue:要存的值(支持字符串、数字、bytes)ex:过期时间(秒)px:过期时间(毫秒)nx:只在 key 不存在时设置xx:只在 key 存在时设置 返回 True 设置成功,False 失败(用于 nx/xx)
get(name) 获取 key 对应的 value name:key 返回字符串(如果 decode_responses=True)或 bytes(默认)
getset(name, value) 设置新值并返回旧值 name:keyvalue:新值 原子操作,返回旧值,如果 key 不存在返回 None
mget(keys, *args) 批量获取多个 key 的值 keys:key 列表 返回列表,对应每个 key 的值,key 不存在返回 None
mset(mapping) 批量设置多个 key-value mapping:字典 {"key1": "val1", "key2": "val2"} 返回 True
incr(name, amount=1) 自增整数 name:keyamount:增加的值(默认1) 返回新的整数值,如果 key 不存在,会先设置为 0 再自增
decr(name, amount=1) 自减整数 name:keyamount:减少的值(默认1) 返回新的整数值
  • 常用属性
属性/参数 说明
ex 设置 key 的过期时间(秒)
px 设置 key 的过期时间(毫秒)
nx 仅当 key 不存在时设置
xx 仅当 key 存在时设置
decode_responses(Redis 连接属性) 如果为 True,get 返回字符串而非 bytes
  • 案例代码
from sturedis import RedisUtil

r = RedisUtil.redis_conn()

# 设置 key
r.set("name", "Alice")
r.set("temp", "123", ex=10)  # 10秒后过期
r.set("score", 100, nx=True)  # 只有 score 不存在时才设置
r.set("score", 200, xx=True)  # 只有 score 已存在时才修改

# 获取 key
print(r.get("name"))  # Alice
print(r.get("score"))  # 200

# 自增/自减
r.set("counter", 10)
r.incr("counter", 5)
print(r.get("counter"))  # 15
r.decr("counter", 3)
print(r.get("counter"))  # 12

image-20251017151012500

2.3 哈希操作

  • 类似 Python dict,适合存对象
  • 常用方法
方法 功能 参数 说明
hset(name, key, value=None, mapping=None) 设置 hash 中的字段 name:hash keykey:字段名value:字段值mapping:字典批量设置 返回被新增字段的数量
hget(name, key) 获取字段值 name:hash keykey:字段名 返回字段值,如果字段不存在返回 None
hgetall(name) 获取整个 hash name:hash key 返回字典,包含所有字段和值
hmget(name, keys, *args) 批量获取字段 keys:字段列表 返回对应字段值列表,不存在字段返回 None
hmset(name, mapping) 批量设置字段 mapping:字典 返回 True(已废弃,可用 hset(name, mapping=mapping) 替代)
hdel(name, *keys) 删除字段 keys:字段名列表 返回成功删除字段数量
hlen(name) 获取 hash 长度 name:hash key 返回字段数量
hexists(name, key) 判断字段是否存在 key:字段名 返回 True/False
  • 案例代码
from sturedis import RedisUtil

r = RedisUtil.redis_conn()

r.hset("user:1001", mapping={"username": "Alice", "age": 25})
print(r.hget("user:1001", "username"))  # Alice
print(r.hgetall("user:1001"))  # {'username': 'Alice', 'age': '25'}
r.hdel("user:1001", "age")

image-20251017151501535

2.4 列表操作

  • 有序、可重复,适合队列、栈
  • 常用方法
方法 功能 参数 说明
lpush(name, *values) 从左侧推入元素 values:一个或多个元素 返回列表长度
rpush(name, *values) 从右侧推入元素 values:一个或多个元素 返回列表长度
lpop(name) 左侧弹出元素 - 返回弹出的元素
rpop(name) 右侧弹出元素 - 返回弹出的元素
lrange(name, start, end) 获取列表指定范围 start/end:索引 返回列表元素列表,索引可为负数
lindex(name, index) 获取指定索引元素 index:位置 返回元素值
lrem(name, count, value) 删除元素 count:删除个数value:值 count>0从左删除,<0从右删除,=0删除所有匹配值
llen(name) 获取列表长度 - 返回元素个数
  • 案例代码
from sturedis import RedisUtil

r = RedisUtil.redis_conn()

r.lpush("tasks", "task1", "task2")
r.rpush("tasks", "task3")
print(r.lrange("tasks", 0, -1))  # ['task2', 'task1', 'task3']
r.lpop("tasks")  # task2
r.rpop("tasks")  # task3

image-20251017151619584

2.5 集合操作

  • 无序、不重复,适合去重、交并差集操作
  • 常用方法
方法 功能 参数 说明
sadd(name, *values) 添加元素 values:一个或多个元素 返回成功添加元素数量
srem(name, *values) 删除元素 values:一个或多个元素 返回删除元素数量
smembers(name) 获取所有元素 - 返回集合(set)
sismember(name, value) 判断元素是否存在 value:元素 返回 True/False
scard(name) 获取集合大小 - 返回元素数量
sinter(keys, *args) 求交集 key 列表 返回交集元素集合
sunion(keys, *args) 求并集 key 列表 返回并集元素集合
sdiff(keys, *args) 求差集 key 列表 返回差集元素集合
  • 案例代码
from sturedis import RedisUtil

r = RedisUtil.redis_conn()

r.sadd("fruits", "apple", "banana", "orange")
print(r.smembers("fruits"))  # {'apple', 'banana', 'orange'}
r.srem("fruits", "banana")
print(r.sismember("fruits", "banana"))  # False

image-20251017151828950

2.6 有序集合操作

  • 常用方法
方法 功能 参数 说明
zadd(name, mapping) 添加元素及分数 mapping:字典 {element: score} 返回成功新增元素数量
zrange(name, start, end, withscores=False) 按分数从小到大获取元素 withscores=True返回分数 返回列表
zrevrange(name, start, end, withscores=False) 按分数从大到小获取元素 - 返回列表
zrem(name, *values) 删除元素 - 返回删除数量
zscore(name, value) 获取元素分数 - 返回元素分数
zcard(name) 获取集合大小 - 返回元素数量
zincrby(name, amount, value) 对元素分数自增 amount:增加量 返回新的分数
  • 案例代码
from sturedis import RedisUtil

r = RedisUtil.redis_conn()

r.zadd("scores", {"Alice": 100, "Bob": 80})
print(r.zrange("scores", 0, -1, withscores=True))  # [('Bob', 80.0), ('Alice', 100.0)]
r.zincrby("scores", 20, "Bob")
print(r.zscore("scores", "Bob"))  # 100.0

image-20251017152016718

二、LLM 多轮对话

  • 我们可以把 LLM 多轮对话中的历史对话信息存储在 Redis 中,推荐使用列表存储,原因如下:
  • List 的特点:
    • 有序(按插入顺序)
    • 可以重复元素
    • 左右推入弹出操作很方便
    • 非常适合存储多条消息的时间顺序

1、一次存储整个对话

  • 这种方案比较简单,易操作
import redis, json
r = redis.Redis(decode_responses=True)

conversation = [
    {"role": "user", "content": "你好"},
    {"role": "assistant", "content": "你好呀,有什么可以帮你?"}
]

# 存储
r.set("chat:1001", json.dumps(conversation))

# 获取
history = json.loads(r.get("chat:1001"))

image-20251017152949155

image-20251017152955146

  • 弊端
问题 说明
无法增量追加 如果用户又发了一条消息,你必须先取出整个列表,append 再存回去。多线程或并发场景容易覆盖数据。
性能问题 对话历史长时,每次都要序列化/反序列化整个列表,消耗大。
无法限制长度 不能直接用 Redis 的 ltrim 等方法限制条数,只能自己处理。
原子性差 在高并发下容易出现“最后写入覆盖先前消息”的问题。

2、List + JSON 每条消息

  • 直接向 Redis 中的 List 中加入新数据即可,不需要全部存储进去
from sturedis import RedisUtil
import redis, json

r = RedisUtil.redis_conn()


# 存储
r.rpush("chat2:1001", json.dumps({"role": "user", "content": "你好"}))
r.rpush("chat2:1001", json.dumps({"role": "assistant", "content": "你好呀,有什么可以帮你?"}))
r.rpush("chat2:1001", json.dumps({"role": "user", "content": "新消息"}))
r.rpush("chat2:1001", json.dumps({"role": "assistant", "content": "新消息的回复"}))
# 获取
history = [json.loads(item) for item in r.lrange("chat2:1001", 0, -1)]
print(history)

image-20251017153601048

image-20251017153537556

3、实战

  • 搭建 FastAPI 项目,引入一个大模型,实现多轮对话
  • 在多轮对话中,服务器在将用户新输入发送给大模型前,会先将其追加到对应用户或会话的 Redis 历史记录中(key 唯一保证数据隔离),并将完整历史作为模型输入生成连续回复
  • 代码实现:不想写了。。。