多模型数据库实践:一份数据,四种视角的Positorium设计解析 这段时间在重构业务数据层时有一个很深的感受现在的业务系统几乎不会只用一种数据模型。用户要存关系要查报表要聚合热点数据还要能毫秒级读取。如果用传统关系型数据库处理所有事情多跳关系查询会写得很痛苦如果把关系全部交给图数据库报表聚合又变得别扭如果为了性能引入列式存储和键值缓存又要额外保证多套存储之间的数据一致性。Positorium 这个名字所代表的思路就是针对这个痛点出现的。它尝试把 RDBMS、Graph、Columnar、name-value 四类数据库的能力放到同一个数据库系统中统一处理。简单说不是把 MySQL、Neo4j、ClickHouse、Redis 四套系统拼在一起而是让同一份数据可以根据业务需求分别以关系表、图、列式存储、键值对四种视角被访问。这篇文章会把 Positorium 这类多模型数据库的设计思路拆开来讲并用一个 Mini Demo 演示“一份数据四种视角”到底是怎么实现的。无论你是正在做技术选型的后端开发还是想理解多模型数据库原理的初学者都可以照着下面的代码和环境跑一遍把概念变成可运行的程序。1. 为什么需要 Positorium 这类数据库1.1 四种数据模型各自的强项与短板先来看这四种模型的定位。关系型数据库 RDBMS 最强的地方是约束和一致性。外键、唯一约束、事务、ACID让它在订单、账户、库存这类对准确性要求极高的场景里无可替代。但它不擅长表达“多跳关系”比如“A 关注了 BB 关注了 CC 又关注了 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