Agent会话数据存储方案,Redis缓存热会话
当Agent服务需要处理大量用户会话时,最稳妥的存储方式是“Redis缓存热会话,MySQL持久化全量数据”。
Redis负责扛住高频读写,MySQL负责兜底保存最终记录,即使Redis重启或淘汰旧数据,会话历史也不会丢。
本文面向零基础运维,用可执行的命令和配置演示这套方案怎么落地。
先想清楚:热会话和持久化分别解决什么问题
Agent会话通常会有两种状态:正在活跃交互的和已经结束或长期不动的。
活跃会话每次请求都要读取上下文,如果直接查MySQL,几十万条历史记录堆在一起,响应会越来越慢。
Redis把当前活跃的会话放内存里,读写速度能到微秒级;
MySQL只做最终落库,定期或异步把Redis里的会话同步过去,相当于给数据加了保险。
准备条件:一台服务器和两个基础服务
你不需要从零搭环境,只需确认以下内容已就绪:
- Linux服务器(CentOS 7+ 或 Ubuntu 20.04+)
- Redis 5.0+,已启动并能设置密码
- MySQL 5.7+,已有可用的数据库和账号
- 服务端语言环境,本文用Python 3演示,其他语言逻辑一样
可以用下面的命令检查服务是否正常:
redis-cli -a 你的密码 ping
mysql -u用户名 -p -e "select 1"
核心操作:建表、连Redis、写会话读写逻辑
1. 在MySQL创建会话表
会话表只需要记录四个关键字段:会话ID、会话内容(JSON格式)、更新时间、过期时间。
执行下面的SQL:
CREATE TABLE agent_session (
session_id VARCHAR(64) PRIMARY KEY,
data JSON NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
expire_at DATETIME NOT NULL,
INDEX idx_expire_at (expire_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
idx_expire_at 这个索引一定不能省,否则清理过期会话时会全表扫描。
2. Redis连接与热会话读写
在Python里使用 redis-py,连接后写入会话时先操作Redis,再异步写MySQL。
核心逻辑如下:
import redis, json, pymysql
from datetime import datetime, timedelta
# 连接Redis
r = redis.Redis(host='127.0.0.1', port=6379, password='你的密码', db=0)
def get_session(session_id):
# 先从Redis热缓存里取
data = r.get(f"agent:session:{session_id}")
if data:
return json.loads(data)
# Redis没命中,回源MySQL并重新缓存
conn = pymysql.connect(host='127.0.0.1', user='root', password='数据库密码', database='mydb')
with conn.cursor() as cur:
cur.execute("SELECT data FROM agent_session WHERE session_id=%s", (session_id,))
row = cur.fetchone()
if row:
r.setex(f"agent:session:{session_id}", 1800, json.dumps(row[0]))
return row[0]
return None
def save_session(session_id, data):
# 写Redis,过期时间设30分钟
r.setex(f"agent:session:{session_id}", 1800, json.dumps(data))
# 写MySQL,过期时间设为7天后
expire = datetime.now() + timedelta(days=7)
conn = pymysql.connect(host='127.0.0.1', user='root', password='数据库密码', database='mydb')
with conn.cursor() as cur:
sql = "INSERT INTO agent_session (session_id, data, expire_at) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE data=%s, expire_at=%s"
cur.execute(sql, (session_id, json.dumps(data), expire, json.dumps(data), expire))
conn.commit()
这里给Redis键设置了1800秒过期,也就是热会话在内存里只保留30分钟。
Agent和用户连续对话时,每次读写都会刷新这个过期时间;
一旦对话中断超过30分钟,Redis里的键自动消失,后续请求回源MySQL,重新加载数据并再放进Redis。
3. MySQL定时清理过期会话
为了让MySQL不无限膨胀,可以写一个定时任务,每天删除已过期的会话:
mysql -u用户名 -p密码 -e "DELETE FROM agent_session WHERE expire_at < NOW();"
建议放到crontab里每天凌晨执行一次,避免手工操作。
避坑指南:这五个问题最容易踩
- Redis不要开持久化兜底热会话。Redis只是缓存,即使开了AOF重启后恢复,也容易和MySQL数据不一致;正确做法是让MySQL作为唯一权威数据源。
- 设置内存淘汰策略时别选noeviction。否则Redis内存满了会直接报错。建议用
maxmemory-policy volatile-lru,只淘汰带过期时间的键,正好把旧会话腾出去。 - MySQL的data字段用JSON类型前要确认版本。MySQL 5.7才开始支持JSON,5.6只能改用TEXT,否则建表会报语法错误。
- Redis和MySQL写入顺序不能反。先写Redis成功后再写MySQL,如果MySQL失败要能回滚Redis键,避免缓存里出现脏数据。
- 会话ID一定要用索引。如果Agent会话量一天超过几万条,不加索引的
WHERE session_id=...查询会拖垮数据库。
验证方案:两条命令确认数据流动正常
先跑一段Python代码写入一个测试会话:
save_session("test123", {"name": "agent_demo", "step": 1})
然后从Redis里读取并检查键是否存在:
redis-cli -a 你的密码 get "agent:session:test123"
正常情况下返回JSON字符串。
再把Redis里的键删掉,模拟热缓存失效:
redis-cli -a 你的密码 del "agent:session:test123"
再次调用get_session("test123"),看是否能从MySQL回源并重新写入Redis。
最后检查MySQL表:
SELECT * FROM agent_session WHERE session_id='test123';
能查到完整数据且updated_at时间正确,说明这套“Redis缓存热会话、MySQL持久化”的方案已经正常运转。
如果你在配置时遇到Redis连不上、MySQL字符集乱码或数据不同步的报错,优先检查连接密码、数据库字符集和时间字段的默认值。
整体跑通后,可以根据业务峰值调整Redis过期时间和MySQL清理周期,让二者保持在最舒服的配合状态。