共计 2365 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
ChatGPT 在企业级应用中产生的对话日志具有三个显著特点:

- 非结构化数据:对话内容包含自然语言文本、代码片段、多轮对话上下文等混合内容
- 高频生成:单个用户日均可能产生 50-100 条对话记录,百万用户规模下日增量可达亿级
- 内容敏感:可能包含个人信息、商业机密等需要特殊处理的数据
传统关系型数据库面临的主要问题:
- 全文检索性能低下,LIKE 查询在百万级数据时响应时间超过 3 秒
- 固定表结构难以适应动态变化的对话内容格式
- 横向扩展能力有限,分库分表维护成本高
技术选型
对比三种主流方案的性能指标(基于 AWS c5.2xlarge 实例测试):
| 指标 | Elasticsearch | MongoDB | ClickHouse |
|---|---|---|---|
| 写入吞吐量 | 15k docs/s | 8k docs/s | 20k docs/s |
| 关键词查询延迟 | 120ms | 300ms | 500ms |
| 语义搜索支持 | 原生支持 | 需插件 | 不支持 |
选择 Elasticsearch 的核心优势:
- 倒排索引 :对对话内容中的每个 token 建立反向索引,实现 O(1) 时间复杂度检索
- 分词器定制:支持 ICU、IK 等分词器处理中文混合内容
- 向量搜索:通过 dense_vector 字段类型直接存储 embedding
实现方案
数据结构设计
{
"chat_id": "uuidv4", # 对话唯一标识
"timestamp": "ISO8601", # 精确到毫秒
"user_id": "hash256", # 用户标识脱敏
"query": "原始用户输入",
"response": "AI 回复内容",
"embedding": [0.12, -0.34, ...], # 768 维向量
"metadata": {
"client_ip": "1.1.1.1",
"session_length": 5
}
}
批量写入实现(Python)
from elasticsearch import Elasticsearch, helpers
import backoff
@backoff.on_exception(backoff.expo, Exception, max_tries=3)
def bulk_insert(es_client, index_name, docs):
actions = [{"_op_type": "index", "_index": index_name, "_source": doc}
for doc in docs
]
try:
success, _ = helpers.bulk(es_client, actions, stats_only=True)
return success
except Exception as e:
logging.error(f"Bulk insert failed: {str(e)}")
raise
# 使用时设置 refresh_interval 为 30s 减少 IO 压力
集群架构设计
+-----------------------+
| Load Balancer |
+-----------+-----------+
|
+---------------+---------------+
| |
+---------v---------+ +-----------v-----------+
| Hot Nodes | | Warm Nodes |
| (NVMe SSD) | | (HDD) |
| - 最新 7 天数据 | | - 历史数据 |
| - 3 副本 | | - 1 副本 |
+-------------------+ +-----------------------+
性能优化
基准测试结果
| 数据规模 | 索引耗时 | 存储占用 |
|---|---|---|
| 1 百万 | 2 分 18 秒 | 4.2GB |
| 1 千万 | 22 分 | 42GB |
查询性能对比
- 关键词检索:”ERROR 403″ 在 1 千万数据中平均响应时间 78ms
- 语义搜索:cosineSimilarity(embedding, query_vector) > 0.9 平均响应时间 210ms
JVM 配置建议
# elasticsearch.yml
-Xms8g # 最小堆内存
-Xmx8g # 最大堆内存
-XX:MaxDirectMemorySize=4g # 堆外内存
避坑指南
- 敏感信息处理:
- 使用 SHA-256 哈希处理用户 ID
-
采用正则表达式过滤信用卡号等 PII
-
分片策略:
- 每个分片大小控制在 30-50GB
-
按日期模板创建索引:
chatlogs-2023-08-20 -
段合并优化:
# 每周日凌晨执行 force merge 0 3 * * 0 curl -XPOST "localhost:9200/_forcemerge?max_num_segments=1"
延伸思考
自动摘要生成
def generate_summary(text):
# 使用 LLM 提取关键信息
prompt = f"总结以下对话的核心内容:\n{text}"
return openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
K8s 自动扩缩容
# HPA 配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: es-data-nodes
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: StatefulSet
name: es-data
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
实际部署中发现,当单个索引超过 500 个分片时,集群管理开销会显著增加。建议通过 ILM(Index Lifecycle Management)自动滚动创建新索引,并设置 7 天后自动转移到 warm 节点。对于需要长期保留的数据,可以定期快照到 S3 存储。
正文完
发表至: 技术分享
近三天内
