共计 2134 个字符,预计需要花费 6 分钟才能阅读完成。
从问题出发:为什么我们需要聊天归档
去年某金融公司因未保存客户服务对话记录,在纠纷中被判赔偿 230 万美元。这个真实案例揭示了聊天归档的三个核心需求:

- 合规性:GDPR 等法规要求对话数据至少保存 6 个月
- 可追溯性:需要支持按用户 ID、时间范围等多维度检索
- 性能:高峰期可能产生 10 万 + 条 / 分钟的写入压力
存储方案选型矩阵
我们对比了三种主流数据库在聊天归档场景的表现(5 分制):
| 特性 | MongoDB | PostgreSQL | Elasticsearch |
|---|---|---|---|
| 写入吞吐量 | 4.5 | 3.8 | 4.2 |
| 复杂查询能力 | 3.2 | 4.5 | 4.8 |
| 水平扩展性 | 4.7 | 3.0 | 4.5 |
| 压缩存储效率 | 4.0 | 3.5 | 3.8 |
| 学习曲线 | 3.5 | 4.2 | 3.0 |
最终选型建议:
– 纯归档场景:MongoDB + zstd 压缩
– 需要复杂检索:Elasticsearch 为主 +MongoDB 冷存储
核心归档流水线实现
以下是 Python 实现的核心组件(使用 FastAPI 框架):
class ChatArchiver:
def __init__(self):
# 布隆过滤器初始化(防重复)self.msg_filter = BloomFilter(
capacity=1000000,
error_rate=0.001
)
async def process_message(self, message: ChatMessage):
"""处理单条聊天消息"""
# 去重检查
msg_hash = hashlib.sha256(f"{message.user_id}-{message.timestamp}-{message.content[:200]}".encode()).hexdigest()
if msg_hash in self.msg_filter:
return False
# 敏感信息过滤(示例:信用卡号)cleaned_content = self._sanitize_content(message.content)
# 异步写入
await self._publish_to_queue({
'user_id': message.user_id,
'timestamp': message.timestamp,
'content': cleaned_content,
'hash': msg_hash
})
return True
def _sanitize_content(self, text: str) -> str:
"""使用正则表达式过滤敏感信息"""
# 移除信用卡号(示例)return re.sub(r'\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\b',
'[REDACTED]', text)
Elasticsearch 索引优化模板
{
"settings": {
"index": {
"number_of_shards": 6,
"number_of_replicas": 1,
"refresh_interval": "30s",
"codec": "best_compression",
"mapping": {"nested_objects.limit": 5000}
},
"analysis": {
"analyzer": {
"chat_analyzer": {
"type": "custom",
"tokenizer": "icu_tokenizer",
"filter": ["lowercase", "stop"]
}
}
}
},
"mappings": {
"properties": {"user_id": {"type": "keyword"},
"timestamp": {"type": "date", "format": "epoch_millis"},
"content": {
"type": "text",
"analyzer": "chat_analyzer",
"fields": {"keyword": {"type": "keyword", "ignore_above": 256}
}
}
}
}
}
压测结果(AWS c5.2xlarge 集群):
| 并发量 | 平均响应时间 | 吞吐量 | 错误率 |
|---|---|---|---|
| 100 | 23ms | 4200/s | 0% |
| 500 | 41ms | 9800/s | 0.2% |
| 1000 | 89ms | 11500/s | 1.1% |
生产环境避坑指南
- 冷热数据分离
- 热数据(7 天内):SSD 存储,保持 3 副本
- 温数据(30 天内):HDD 存储,1 副本
-
冷数据:转储到 S3+Glacier
-
权限继承陷阱
- 使用 ABAC(属性基访问控制)替代 RBAC
- 在文档级别添加
tenant_id字段 -
查询时自动注入权限过滤条件:
def build_query(user, raw_query): return { "bool": { "must": raw_query, "filter": [{"term": {"tenant_id": user.tenant}}] } } -
连接池管理
- MongoDB 连接数公式:
max_pool_size = (核心数 x 2) + 磁盘数 - Elasticsearch HTTP 客户端启用 sniff
开放性问题
当我们需要实现 ” 找出所有讨论过 API 设计规范的会话 ” 这类需求时,简单的关键词搜索已经不够。可能的解决方向:
- 使用 sentence-transformers 生成对话向量
- 构建跨会话的 Faiss 索引
- 结合对话 topic modeling(如 LDA)
期待你在评论区分享实践经验。
正文完
