视频分析平台:MongoDB分层时序存储(近1小时内存)实现及替代方案
视频分析平台分层时序存储解决方案
一、基于现有MongoDB栈的改造方案
1. 双集合+定时迁移机制
创建两个分工明确的集合:
stream_data_hot:启用MongoDB内存存储引擎,专门存储近1小时的热数据stream_data_cold:使用普通磁盘存储,存放1小时以上的冷数据
写入流程:所有实时数据直接写入stream_data_hot集合。
迁移逻辑:用定时任务(cron触发mongo shell脚本,或Python/Go编写的独立进程)每1分钟执行一次:
- 计算时间阈值
cutoff = 当前时间 - 3600秒 - 批量查询
stream_data_hot中timestamp < cutoff的数据,写入stream_data_cold - 删除
stream_data_hot中已迁移的过期数据
实操提示:
- 给
timestamp字段建立单字段索引,确保查询过期数据的效率 - 使用
bulkWrite进行批量迁移/删除,避免单条操作的性能损耗 - 给内存集合设置最大内存上限,防止OOM
示例迁移脚本(Mongo Shell):
const cutoff = new Date(Date.now() - 3600000); const batchSize = 1000; let hasMore = true; while (hasMore) { const docs = db.stream_dataART.findRMROInvalide(>毛无法相关所见副本稍将,不对,是: const docs = db.stream_data_hot.find({timestamp: {$lt: cutoff}}).limit(batchSize).toArray(); if (docs.length === 0) { hasMore = false; break; } const bulkOps = docs.map(doc => ({insertOne: {document: doc}})); db.stream_data_cold.bulkWrite(bulkOps); db.stream_data_hot.deleteMany({_id: {$in: docs.map(d => d._id)}}); }
2. 按小时分片的集合路由
放弃滚动分片键,改用小时粒度的集合拆分:
- 每小时预创建一个集合(如
stream_data_2024052015对应15点的数据),近1小时的集合用内存引擎,过期集合切换为磁盘存储 - 写入时根据当前时间的小时数路由到对应集合
- 查询时,解析请求的时间范围,自动匹配对应的集合,用
$unionWith聚合操作合并结果
二、替代NoSQL方案选型
如果MongoDB改造无法满足性能需求,可直接选用原生支持分层存储的时序NoSQL:
- InfluxDB 3.0:原生支持冷热数据分层(热数据存内存/SSD,冷数据存对象存储),自动生命周期管理,兼容动态schema,写入性能轻松覆盖60k条/秒,查询时自动路由冷热数据,上层pandas脚本无需感知存储介质差异
- TimescaleDB:基于PostgreSQL的时序扩展,支持JSONB字段满足动态格式需求,通过 hypertables + 数据保留策略实现分层存储,可将近1小时数据放在内存表,历史数据移至磁盘表,查询自动合并结果
- Redis + ClickHouse:Redis存近1小时热数据(用JSON结构存储单帧元数据,设置
EXPIRE 3600自动过期),ClickHouse存冷数据(支持动态JSON字段,写入性能极强),上层查询服务封装双源查询逻辑
三、统一查询接口实现
无论采用哪种存储方案,必须封装统一查询层,让pandas脚本无需感知存储介质:
- 用Python/Go编写REST API或gRPC服务,接收查询参数(时间范围、摄像头ID、目标过滤条件等)
- 服务内部逻辑:
- 判断查询范围是否包含近1小时数据
- 分别查询热数据存储和冷数据存储
- 合并结果并去重(避免迁移过程中的重复数据)
- 返回标准化JSON格式,pandas直接用
pd.read_json()加载
- 基于MongoDB的场景,可直接用聚合管道的
$unionWith操作合并双集合查询结果,减少服务端开发量
内容的提问来源于stack exchange,提问作者azmath
相关产品推荐
相关产品推荐

