这段时间在重构业务数据层时,有一个很深的感受:现在的业务系统几乎不会只用一种数据模型。用户要存,关系要查,报表要聚合,热点数据还要能毫秒级读取。如果用传统关系型数据库处理所有事情,多跳关系查询会写得很痛苦;如果把关系全部交给图数据库,报表聚合又变得别扭;如果为了性能引入列式存储和键值缓存,又要额外保证多套存储之间的数据一致性。
Positorium 这个名字所代表的思路,就是针对这个痛点出现的。它尝试把 RDBMS、Graph、Columnar、name-value 四类数据库的能力,放到同一个数据库系统中统一处理。简单说,不是把 MySQL、Neo4j、ClickHouse、Redis 四套系统拼在一起,而是让同一份数据可以根据业务需求,分别以关系表、图、列式存储、键值对四种视角被访问。
这篇文章会把 Positorium 这类多模型数据库的设计思路拆开来讲,并用一个 Mini Demo 演示“一份数据,四种视角”到底是怎么实现的。无论你是正在做技术选型的后端开发,还是想理解多模型数据库原理的初学者,都可以照着下面的代码和环境跑一遍,把概念变成可运行的程序。
1. 为什么需要 Positorium 这类数据库?
1.1 四种数据模型各自的强项与短板
先来看这四种模型的定位。
关系型数据库 RDBMS 最强的地方是约束和一致性。外键、唯一约束、事务、ACID,让它在订单、账户、库存这类对准确性要求极高的场景里无可替代。但它不擅长表达“多跳关系”,比如“A 关注了 B,B 关注了 C,C 又关注了 D”,用 SQL 写递归查询会非常繁琐,性能也容易失控。
图数据库 Graph DB 则把“关系”本身当成一等公民。节点和边都是存储的基本单位,查询朋友的朋友、权限继承链、资金流转路径都非常自然。但图数据库在“全表聚合统计”这种场景下通常没有列式数据库高效,而且它的强项是遍历,不是大规模并发更新。
列式数据库 Columnar DB 把同一列的数据连续存放,适合扫描大量行、计算 SUM、AVG、COUNT 这类分析型查询。压缩率高,IO 开销小。但它不擅长点查和复杂关联更新,如果每条记录都高频修改,列式存储会比较吃亏。
name-value DB 也就是我们常说的键值数据库,比如 Redis、RocksDB。它用最简单的 Key-Value 模型换来极致的点查性能和灵活的数据结构。但它的查询能力很弱,没有表结构、没有复杂条件过滤,也没有事务性很强的多行操作。
把这四种模型放在一起看,没有一个是“全能选手”。它们的强项恰好形成了互补关系。
1.2 散装数据库方案的问题
既然四种模型各有优势,很多团队会直接选择“散装组合”:MySQL 存核心业务,Neo4j 存关系,ClickHouse 存分析数据,Redis 做缓存。这套方案在初期很实用,但运行一段时间后,问题会慢慢暴露。
最典型的问题是数据一致性。用户在 MySQL 里更新了昵称,Redis 里可能还是旧值;订单在 MySQL 里产生了新记录,ClickHouse 里的报表数据要等 ETL 定时任务跑完才能看到。业务层不得不自己维护多套数据之间的同步逻辑,双写、重试、补偿、对账,每个环节都要额外开发。
第二个问题是跨存储查询很困难。想在一条业务链路里同时查询关系数据、图关系和分析结果,只能分别查完再在应用层拼接。这个拼接过程既增加接口耗时,也让代码变得复杂。
第三个问题是运维成本。四套数据库意味着四套监控、四套备份方案、四套权限体系,对中小团队来说压力不小。
Positorium 这类多模型数据库想解决的就是这个矛盾:底层是统一的存储引擎,对外提供多种数据访问方式。业务数据只写一份,但从应用层看,既能用 SQL 查关系,也能用图遍历看路径,还能按列聚合做分析,以及用 key 直接取热点数据。
1.3 Positorium 的定位:一份数据,四种视角
可以把 Positorium 理解成一个“多模型数据库”的实践。它的核心不是提供四个产品,而是在一个数据库内同时提供:
- 关系模型:支持表结构、主外键、事务和 SQL 风格查询。
- 图模型:支持节点、边、多跳遍历和图算法。
- 列式模型:支持大范围扫描、压缩和聚合分析。
- 键值模型:支持通过唯一键快速定位数据。
这里的难点在于,四种访问方式必须建立在同一份数据上,而不是复制出四份数据。Positorium 在架构上需要解决两个问题:怎么存储,才能让四种模型都高效;怎么写入,才能保证四种视角的数据始终一致。
2. Positorium 的核心概念与设计思路
2.1 名称与整体架构
从名字来看,Positorium 可以理解为 position 与 -orium 的组合,容易让人联想到“数据在不同位置、不同视角下都能被安放”。当然,具体命名的含义需要以项目资料为准,但从技术定位上看,它更强调统一收纳、多视角访问。
如果画一张简化架构图,可以这样看:
统一访问层:SQL 查询 / 图遍历 / 分析聚合 / KV 点查 | 查询规划与执行器 | 统一存储与索引引擎 行索引 / 列投影 / 邻接表 / 主键索引在上面这层,应用可以根据场景选择访问方式。遇到复杂关系,就发一个图查询;遇到报表统计,就发一个列式聚合;遇到热点数据读取,就走 KV 接口。但所有请求最终都会进入同一个查询规划器,并由统一的存储引擎处理。
这样做最大的好处是,不需要在多个数据库之间搬运数据。写入一条订单记录时,主键索引、邻接关系、列式投影可以一起更新;即使更新失败,也只有一个事务需要回滚,而不是四套系统分别回滚。
2.2 统一存储与统一访问
“一份数据,四种视角”听起来很理想,但实现起来要动存储层。如果底层只做行存,列式聚合性能差;如果只做列存,点查和关系更新又慢。所以多模型数据库通常会把一条记录拆成多个内部结构:
- 主键索引:用于 KV 点查和唯一约束。
- 行存结构:保存完整的属性字段,支持事务和关系查询。
- 邻接表:保存节点之间的边,支持图遍历。
- 列投影:按列组织数据,用于分析聚合。
关键在于,这些结构不是业务方手动维护的副本,而是数据库在写入时自动同步维护的。应用只提交一次数据,数据库内部负责把数据写入各个索引和投影。这样对外看是四种模型,对内看是一个整体。
2.3 多模型下的事务与一致性
多模型数据库最容易被质疑的一点是:同时维护行存、列存和邻接表,事务还能做吗?
如果所谓多模型只是四套存储之间做异步同步,那就不是数据库,而是数据管道。真正的多模型数据库应当提供统一事务边界。一次写入操作,要么同时更新行存、列存和邻接索引,要么全部不更新。这样应用层才能保证数据不会出现“关系表有数据,图里找不到边”的情况。
当然,事务范围越大,锁竞争和性能开销也越大。生产环境里通常还要配合隔离级别、快照读和异步合并来平衡。Positorium 这类项目在设计时也会面临同样的取舍,具体行为需要以项目文档为准。
3. 环境准备与最小概念验证
3.1 运行环境
这篇文章不依赖特定 Positorium 安装包,因为目前更希望先验证“四种模型如何统一”的工程设计思路。我们会用 Python 写一个内存版 Dem