[数据库系统] 数据库系统的设计原理研究

0 序

  • 从上大学到今天,从事后端开发、大数据、AI系统开发工作,这些年来,也不同程度地使用数十款数据库。本篇尝试总结、提炼这些数据库系统的共性设计。

1 数据库系统的设计原理研究

存储引擎 / 底层的数据结构 (必读)

行存储

  • 行存储:
  • 数据结构=B-Tree或变体 : 将数据按照【顺序存储】在树节点 中,每一个节点上边都有一定的key和pointer。而Key代表这顺序,pointer则是指向子节点的路牌。这样就可以快速定位到目标数据,减少磁盘IO。但是每一个解决方案都像是硬币,都是两面的。数据不是一成不变的,在不断的数据操作时,节点会分裂,一变2之后,从【内存】flush到【硬盘】的时候就很有可以变得不连续 。并且在大量更改的时候,除了会造成写放大 ,同样也会因为【磁盘】循环写入产生不连续。
  • MySQL (InnoDB引擎) / PostgreSQL (Heap + B-Tree + WAL) / Oracle / SQL server / 达梦数据库 ((B + 树)页式索引,页式存储(数据页 / 索引页,共享表空间))
  • SQLLite(B + 树,页式存储)

列存储

  • 列存储:
  • 数据结构=LSM或变体(Log-Structured Merge Tree, 结构化的日志合并树)核心的解决方案: 通过【顺序写】替代【随机写】,最小化的控制碎片的产生,提升数据密度。
  • 经典 LSM 代表: LevelDB / RocksDB(都是 LSM 的鼻祖实现,是众多数据库的底层存储) / LiteDB (嵌入式数据库,其有LSM-Tree的倾向)
  • 数据湖仓: Apache Paimon
  • OLAP/数据仓库: Clickhouse / Apache Doris(基于LSM-Tree的变体设计) /
  • HATP : OceanBase(自研 LSM 引擎;HTAP型数据库:同时高效处理OLTP(在线事务处理)和OLAP(在线分析处理)负载 => Shared Nothing 多副本架构来支持 OLTP + 轻量 OLAP 场景)
  • 时序数据库/TSDB: InfluxDB (Time-Structured Merge tree,LSM 的变体) / OpenGemini (同 TSM 变体) / TimescaleDB
  • NoSQL: Apache HBase / BigTable
  • Cassandra / Dynamo(闭源)
  • 数据结构=其他
  • 达梦(HTAP-列存储模式): 其列存储走的是自家 HFS 机制,不是 LSM
  • 达梦的列存储表(HUGE 表)建立在 HFS(Huge File System) 存储机制上,针对海量数据分析设计。
  • 整个【存储体系】仍是经典的页 → 簇 → 段 → 表空间结构:行存储用 B-Tree(实际是 B+ 树变体)页式管理,列存储(HUGE 表)用 HFS 的段 / 区方式组织。
  • DuckDB:列式存储(PAX 格式)

其他

  • 其他
  • Redis:哈希表(主)、跳表(zset)

Q: RAG技术中的向量数据库(Mivlus / Qdrant 等)、搜索引擎数据库(ES等)的底层核心数据结构?

  • 向量数据库 和搜索引擎数据库的核心数据结构,其实都围绕"如何快速找相似/找相关"展开,但机制不同。

下面按类别拆解。

一、向量数据库(Milvus / Qdrant)

  • 核心是近似最近邻(ANN)索引,因为精确最近邻在【高维空间】(几百到上千维)下【无法靠遍历完成】,必须牺牲【精度】换【速度】。
1. 主导索引结构(三大家族)
索引家族 底层数据结构 原理 代表/默认
图索引 HNSW 多层"可导航小世界"图 + 邻接表(存节点id与边) 分层构图:顶层稀疏长跳,底层稠密精细;查询从顶层贪心下降,多层跳转 Qdrant 默认;Milvus 支持
聚类分桶 IVF 倒排分桶列表(聚类中心 → 桶内向量id) K-means 把向量分到 N 个桶,查询只进最近的几个桶做线性扫描 Milvus 经典方案
量化压缩 PQ 乘积量化码本(codebook) + 残差桶 把向量切段、每段聚类成码字,用码本 id 压缩内存,查询时查表近似距离 IVF-PQ、HNSW-PQ
  • 大数据量且内存受限时,Milvus 用 DiskANN / Vamana 图,把图放在磁盘(page 粒度),依赖显式重排序(rerank)保证召回。
  • 稀疏向量(如 SPLADE/BGE-M3 稀疏端)则回到倒排索引:term → 文档列表,本质上和搜索引擎同构。
2. 辅助存储结构
组件 结构 作用
标量过滤字段 B+树 / 哈希索引 支撑 filter 里的范围/等值条件,先过滤再 ANN 或联合过滤
写入与日志 LSM 思想 / WAL(日志)+ 列式 binlog Milvus 按 segment 组织,写路径走消息队列 + 落盘,后台合并
ID → 向量定位 段(segment)/ 分片(shard)内 id 映射表 定位向量物理存储位置
距离计算 SIMD 优化的浮点数组(连续内存) 向量本体即大数组,聚拢排列保证 cache 命中

图(HNSW)负责跳、桶(IVF)负责缩小范围、码本(PQ)负责省内存,倒排+B+树负责过滤。

二、搜索引擎数据库(Elasticsearch / Lucene)

核心是倒排索引(Inverted Index),本质是"词 → 文档"的映射,为全文检索而生。

1. 倒排索引的三个层次
层次 底层数据结构 说明
词项词典 FST(有限状态转换器) Lucene 用 FST 存词典,内存紧凑、支持前缀查询,是词典查词的标配
倒排列表 跳表(Skip List) + FOR/Delta 增量编码 文档 id 列表有序,跳表加速求交/求并;delta 压缩省空间
文档集合运算 Roaring Bitmap(压缩位图) 过滤、交集、并集用位图做,常数级且省内存
2. 其他关键结构
组件 底层数据结构 作用
数值/地理字段 BKD 树(块式 K-D 树) 支撑数值范围查询、排序、地理距离,替代早期朴素的区间结构
段管理 LSM 树 / 不可变 Segment + 分层合并 写入先进内存 buffer 再刷成小段,后台 tiered merge 成大段
正排列存 DocValues(列式存储) 独立于倒排的列存,供排序、聚合、脚本取值,不重复加载原文
词项信息 词频、位置、payload 数组 支撑 TF-IDF/BM25 打分与短语查询

小结:词靠 FST 查词典,文档集合靠跳表+位图做交并,数值靠 BKD 树,落盘靠 LSM 段,聚合靠列存。

三、两者对比速览

维度 向量数据库 搜索引擎
核心索引 ANN 图/桶/量化(HNSW·IVF·PQ) 倒排索引(FST+跳表+位图)
定位目标 向量相似度 词项/数值匹配
数值字段 B+树过滤 BKD 树
写路径 WAL + LSM 段 LSM 不可变段 + 合并
聚合支持 弱(多靠标量过滤) 强(DocValues 列存)

关键差异 :搜索引擎的倒排是"精确命中";向量库的 ANN 是"近似相似"。所以现代系统常把两者叠加------用稀疏向量/标量过滤走精确,用稠密向量走近似,正是 Milvus 混合检索(hybrid search)和 ES 的 knn + bool 组合的思路。

Q: 知识图谱数据库(TypeDB、GraphDB、Neo4j、NebulaGraph)的底层核心数据结构?

知识图谱/图数据库的底层核心,和前面两类完全不同:向量库解决"相似",搜索引擎解决"匹配",图库解决**"关联遍历"** 。它的灵魂是邻接表(index-free adjacency)------每条边直接指向两端的点,遍历关系靠指针跳转而非全局查表。但各家存储落地差异很大,尤其"原生图"和"RDF/逻辑推理"是两条路线。

一、各家底层核心数据结构

数据库 类型 底层核心数据结构 关键机制
Neo4j 原生属性图 定长记录存储(fixed-size record store)+ 双向链表关系链 每个节点记录固定大小,id×记录大小=文件偏移,O(1) 定位;每个节点维护"首条关系"指针,关系用双向链表串起进出边
NebulaGraph 分布式属性图 RocksDB(LSM 树)+ 自定义 KV 编码 存储层每个分区一个 RocksDB;点/边编码成排序 KV,边按"起点vid"前缀聚集保证邻接查询局部性;按顶点 hash 分片分布到多节点
TypeDB 逻辑推理图 RocksDB(LSM)+ 类型化超图编码 底层仍是 KV/LSM,但数据按类型系统组织(实体/关系/属性作为类型实例),之上叠 Datalog 式规则推理引擎
GraphDB (Ontotext) RDF 三元组 六重索引(Sextuple Indexing)+ 字典编码 对 S-P-O 三个位置全排列建 6 个索引(SPO/SOP/PSO/POS/OSP/OPS),任意查询模式都有对应索引命中

二、两条路线的本质差异

路线 A:原生属性图(Neo4j / NebulaGraph)

核心是"点+边",用邻接链表 或邻接前缀把关系"焊死"在数据结构里:

复制代码
Neo4j:  节点记录 ──首关系指针──▶ 关系记录(双向链表)
                                  ▲   │
                                  └───┘ (prev/next + 两端节点指针)
Nebula: key = (startVid + edgeType + rank + endVid)  → 前缀即邻接

路线 B:RDF/语义网(GraphDB)

核心是三元组 (主语, 谓语, 宾语) ,不区分"点/边",一切都是三元组。为满足任意 SPARQL 查询模式,直接对三个位置全排列建索引(六重索引),并用字典编码把每个 RDF 项压成长整型 id,三元组就变成三个整数的数组。

三、共同技术底座

组件 使用结构 用途
属性/标量索引 B+ 树 按属性值(name、age)定位起点,Neo4j 属性索引即是
持久化层 LSM 树(RocksDB) Nebula、TypeDB 直接以 RocksDB 为存储;高写入、顺序读友好
全文检索 Lucene Neo4j 全文、GraphDB 对字面量做全文,复用搜索引擎技术
遍历引擎 内存 BFS/DFS + 剪枝 邻接结构之上做跳数受限的图遍历

四、与向量库/搜索引擎对齐看

维度 向量库 搜索引擎 图数据库
核心索引 ANN(图/桶/量化) 倒排(FST+跳表+位图) 邻接表/邻接编码 + 属性 B+树
定位目标 相似度 词项匹配 关系遍历
关键查询 top-K 近邻 命中+打分 多跳路径/子图匹配
典型短板 弱关联推理 弱多跳 高扇出深度遍历退化

总结 :图库的"原生性"体现在边以指针/编码形式内联在存储结构里(Neo4j 指针链表、Nebula 前缀编码),而不是像关系库那样靠 join 现算;TypeDB 把"图"抽象成类型化超图并叠加推理,GraphDB 则把"图"还原成三元组加六重索引------这是两类思路,别混为一谈。

  • 扩展问题:NebulaGraph 的 KV key 编码规则 (边如何按起点前缀组织)或 GraphDB 六重索引为什么能覆盖所有查询模式?

多副本与容灾 | 高可用架构

  • Raft 协议(多副本的容灾/高可用架构的底层算法)

Q: 多款数据库(OpenGemini/达梦等)都自称基于Raft协议(多副本的容灾/高可用架构),该协议的定位是什么?用什么方法解决了什么问题?(必读)

  • 定位 :RAFT 在达梦 DM9 里不是"数据库本身",而是给数据库"当保镖" ------一个管多副本数据一致性的共识算法,让高可用有了可信的底层机制。

  • 它解决的痛点是:把数据复制到多台机器上之后,机器反而会"吵架"。

数据库要扛故障就得存多份副本,可副本一多就冒出三个新问题:

① 哪份数据算"权威的"?

② 主节点挂掉时,谁说了算、服务不能断?

③ 各副本会不会各写各的、数据"分叉"?RAFT 就是为这三问定规矩的。

  • 它的方法就四个字:少数服从多数。
  • 选主:所有副本先投票,选出一个 Leader(主节点)当"唯一话事人",只允许它接收写入并给每条日志编号排顺序,其他副本老老实实跟着复制;
  • 日志复制:Leader 把写入广播给多数派(如 3 副本至少 2 个)确认后才算提交------所以只要多数节点活着,数据就不丢;
  • 自动恢复:Leader 挂了,其余节点重新投票,记录最全的优先当选新主,服务秒级接管、数据不分裂。
  • 对【达梦 DM9】的意义 :它把这一套机制内嵌进多副本架构 ,实现了官方说的"节点自动选主、自动故障处理、自动故障恢复"的金融级高可用。

  • 特别提醒 :RAFT 只是"共识层",保证的是高可用与一致性协调 ,不代表分布式性能或兼容性 ------很多库"自称基于 RAFT"说明这是个成熟通用底座 ,真正拉开差距的是各自在存储、执行、优化器上的本事。选型时与其看"用没用 RAFT",不如看它在哪个环节用、故障切换实测多快。

Q: Raft 协议规范的权威 URL?其核心组件与架构设计?(必读)

1、Raft 协议规范权威 URL

这两处是 Raft 的"第一手权威",其余大多是解读或实现,均以此为准。


2、Raft 核心组件与架构设计

先给角色状态机(三种角色如何切换):
stateDiagram-v2 direction LR \* --> Follower : 节点启动即为Follower Follower --> Candidate : 选举超时&未收到Leader心跳 Candidate --> Leader : 获得过半数(多数派)选票 Candidate --> Follower : 发现更高任期 / 他节点当选 Leader --> Follower : 发现更高任期(Leader自降级) Leader --> Leader : 周期性广播心跳AppendEntries

再看核心组件与节点间通信架构:
flowchart TB subgraph LeaderLeader 节点 LS"持久化状态\
currentTerm 任期 / votedFor 投给谁 / log 日志"
LI"易失状态\
commitIndex 已提交位点 / lastApplied 已应用位点"
LLOG"日志模块\
有序条目 entry(term,index,command)"
LSM"状态机 StateMachine\
(上层的数据库/存储引擎)"
LS --- LI --- LLOG --- LSM end subgraph F1Follower 节点1 FS1"持久化状态 + 日志 + 状态机(同构副本)" end subgraph F2Follower 节点2 FS2"持久化状态 + 日志 + 状态机(同构副本)" end LLOG -- "① AppendEntries RPC<br/>(心跳+日志复制+确认)" --> FS1 LLOG -- "① AppendEntries RPC" --> FS2 F1 -- "② RequestVote RPC<br/>(选举期请求投票)" --> LS F2 -- "② RequestVote RPC" --> LS FS1 -- "日志/状态反馈" --> LSM FS2 -- "日志/状态反馈" --> LSM

总结 :Raft 把所有复杂度 收敛成"一个 Leader + 两个 RPC "------AppendEntries(日常复制日志、兼当心跳)和 RequestVote(选举时拉票),再加"多数派确认"这一条铁律。

任意时刻只有一个 Leader 说了算,日志按任期 (term)单调推进,提交即落盘多数节点,从而同时解决选举、日志复制、安全与故障恢复四件事。

事务管理

Q: 数据库的 MVCC机制(事务的多版本并发控制) 与 Raft 协议(多副本一致性协议) 的区别与联系?(必读)

  • 数据库的事务:基于 MVCC 多版本并发控制,保证隔离与一致性

  • 数据库的多副本、容灾:基于 RAFT 协议 的全新多副本架构:节点自动选主、自动故障处理、自动故障恢复。

MVCC 管"一个节点内,读写互不打架";

Raft 管"多个节点间,数据不分裂"。

一个管【并发】,一个【管共识】,层次不同、互为补充。

MVCC Raft
层面 单节点 事务并发层(内存/存储) 跨节点 分布式复制/一致性层
解决的问题 读写并发互不阻塞 + 事务隔离(读快照不锁写) 多副本达成一致 + 选举主节点 + 故障恢复
核心手段 给数据存多个版本 ,按版本号/快照判定可见性 给日志打 term(任期)+index(序号),按多数派确认排序提交
目标 并发性能 + 隔离性 高可用 + 不丢数据 + 不分裂

联系(本质相通)

  • 都在"给数据打版本、按序裁决":MVCC 用版本号裁决"谁能看到哪个版本",Raft 用 term+index 裁决"日志哪条先落盘"。
  • 是互补的两层,不是二选一 :现代高可用数据库通常是"MVCC 负责单节点本地并发,Raft 负责跨节点副本共识"的组合------例如分布式数据库每个分片内部用 MVCC 处理并发事务,分片间的副本同步交给 Raft。

小结 :MVCC 让一个库 能同时服务很多人还不乱;Raft 让坏掉几台机器数据还是那份数据。一个对"并发"负责,一个对"可靠"负责。

Y 推荐文献

X 参考文献

相关推荐
麦壳饼1 小时前
SonnetDB SQL 分页查询:LIMIT/OFFSET 与 FETCH 语法
数据库·sonnetdb
Gauss松鼠会1 小时前
【GaussDB】破除gaussdb ugin索引支持中文模糊查询的迷思-字符序
java·运维·服务器·网络·数据库·gaussdb·经验总结
l1t2 小时前
DeepSeek总结的PostgreSQL因选择而宽松,因偶然而永久
数据库·postgresql
殷色玫瑰2 小时前
C++ STL:map 与 set 详解——从底层红黑树到实际应用
c语言·数据结构·c++·算法·visualstudio·rpc
Nturmoils2 小时前
数据同步中断后,KFS 怎么把链路接回来
数据库
leisoo80972 小时前
股票筹码分布怎么用获利比例成本区间与集中度实战 IG50免费开源股票数据API接口
开发语言·jvm·数据库·python·开源
字节跳动的猫2 小时前
LikeShop 商品评价体系二开:追评、晒图审核与评价标签筛选功能开发
运维·数据结构·数据库
行业研究员4 小时前
Agent Memory降低Token消耗原理解析
数据库·人工智能·oracle·腾讯云·智能体
yixun_gdas4 小时前
企业网站内容巡查的价值与方法:企业官网的合规与形象管理
数据库·内容运营