文章目录
- [《现代 Key-Value 数据库原理:从 B+Tree 到 LSM Tree》](#《现代 Key-Value 数据库原理:从 B+Tree 到 LSM Tree》)
- 第五篇:现代数据库横向对比
- [第一章 现代数据库整体分类](#第一章 现代数据库整体分类)
- [第二章 现代数据库横向总表](#第二章 现代数据库横向总表)
- [第三章 LMDB](#第三章 LMDB)
- [3.1 LMDB的数据结构](#3.1 LMDB的数据结构)
- [3.2 LMDB为什么读取快?](#3.2 LMDB为什么读取快?)
- [3.3 LMDB的核心优势](#3.3 LMDB的核心优势)
- [3.4 LMDB的限制](#3.4 LMDB的限制)
- [第四章 LevelDB](#第四章 LevelDB)
- [4.1 LevelDB为什么选择LSM?](#4.1 LevelDB为什么选择LSM?)
- [4.2 LevelDB核心组件](#4.2 LevelDB核心组件)
- [4.3 LevelDB的缺点](#4.3 LevelDB的缺点)
- [第五章 RocksDB](#第五章 RocksDB)
- [5.1 为什么需要RocksDB?](#5.1 为什么需要RocksDB?)
- [5.2 Column Family](#5.2 Column Family)
- [5.3 RocksDB适合什么?](#5.3 RocksDB适合什么?)
- [第六章 BadgerDB](#第六章 BadgerDB)
- [6.1 为什么Key和Value分开?](#6.1 为什么Key和Value分开?)
- [6.2 Badger适合什么?](#6.2 Badger适合什么?)
- [第七章 BoltDB / bbolt](#第七章 BoltDB / bbolt)
- [7.1 bbolt结构](#7.1 bbolt结构)
- [7.2 bbolt与LMDB](#7.2 bbolt与LMDB)
- [第八章 SQLite](#第八章 SQLite)
- [8.1 SQLite底层是什么?](#8.1 SQLite底层是什么?)
- [8.2 SQLite最大的特点](#8.2 SQLite最大的特点)
- [8.3 SQLite为什么这么流行?](#8.3 SQLite为什么这么流行?)
- [8.4 SQLite vs LMDB](#8.4 SQLite vs LMDB)
- [第九章 Berkeley DB](#第九章 Berkeley DB)
- [9.1 Berkeley DB适合什么?](#9.1 Berkeley DB适合什么?)
- [第十章 Redis](#第十章 Redis)
- [10.1 Redis不是单一数据结构](#10.1 Redis不是单一数据结构)
- [10.2 Redis为什么快?](#10.2 Redis为什么快?)
- [10.3 Redis为什么还能持久化?](#10.3 Redis为什么还能持久化?)
- [第十一章 WiredTiger](#第十一章 WiredTiger)
- [11.1 WiredTiger为什么重要?](#11.1 WiredTiger为什么重要?)
- [11.2 MongoDB为什么需要独立存储引擎?](#11.2 MongoDB为什么需要独立存储引擎?)
- [第十二章 Pebble](#第十二章 Pebble)
- [12.1 Pebble为什么出现?](#12.1 Pebble为什么出现?)
- [第十三章 TiKV](#第十三章 TiKV)
- [13.1 TiKV整体架构](#13.1 TiKV整体架构)
- [13.2 为什么TiKV使用RocksDB思想?](#13.2 为什么TiKV使用RocksDB思想?)
- [第十四章 FoundationDB](#第十四章 FoundationDB)
- [14.1 FoundationDB最大的特点](#14.1 FoundationDB最大的特点)
- [第十五章 B+Tree vs LSM Tree](#第十五章 B+Tree vs LSM Tree)
- [第十六章 写入性能](#第十六章 写入性能)
- [第十七章 读取性能](#第十七章 读取性能)
- [第十八章 Read Amplification](#第十八章 Read Amplification)
- [第十九章 Write Amplification](#第十九章 Write Amplification)
- [第二十章 Space Amplification](#第二十章 Space Amplification)
- [第二十一章 三种放大之间的关系](#第二十一章 三种放大之间的关系)
- [第二十二章 mmap vs Buffer Pool](#第二十二章 mmap vs Buffer Pool)
-
- mmap
- [Buffer Pool](#Buffer Pool)
- [22.1 两者区别](#22.1 两者区别)
- [第二十三章 SSD时代的数据库](#第二十三章 SSD时代的数据库)
- [23.1 SSD改变的是成本,而不是原理](#23.1 SSD改变的是成本,而不是原理)
- [第二十四章 HDD、SSD、NVMe对比](#第二十四章 HDD、SSD、NVMe对比)
- [24.1 HDD](#24.1 HDD)
- [24.2 NVMe](#24.2 NVMe)
- [第二十五章 AI数据集应该选什么?](#第二十五章 AI数据集应该选什么?)
- [第二十六章 日志系统应该选什么?](#第二十六章 日志系统应该选什么?)
- [第二十七章 缓存系统应该选什么?](#第二十七章 缓存系统应该选什么?)
- [第二十八章 嵌入式设备应该选什么?](#第二十八章 嵌入式设备应该选什么?)
- [第二十九章 Go项目应该选什么?](#第二十九章 Go项目应该选什么?)
- [第三十章 分布式数据库应该选什么?](#第三十章 分布式数据库应该选什么?)
- [第三十一章 Storage Engine与Database的区别](#第三十一章 Storage Engine与Database的区别)
- [第三十二章 数据库的分层](#第三十二章 数据库的分层)
- [第三十三章 为什么没有"最好的数据库"](#第三十三章 为什么没有“最好的数据库”)
- [第三十四章 从访问模式选择数据库](#第三十四章 从访问模式选择数据库)
- [第三十五章 AI/视觉行业数据库选择](#第三十五章 AI/视觉行业数据库选择)
- [第三十六章 一个工业视觉系统应该如何组合?](#第三十六章 一个工业视觉系统应该如何组合?)
- [第三十七章 B+Tree与LSM Tree的最终理解](#第三十七章 B+Tree与LSM Tree的最终理解)
-
- B+Tree
- [LSM Tree](#LSM Tree)
- [第三十八章 从LMDB到LevelDB,再到RocksDB](#第三十八章 从LMDB到LevelDB,再到RocksDB)
- [第三十九章 现代数据库真正的核心技术](#第三十九章 现代数据库真正的核心技术)
- [第四十章 数据库学习路线](#第四十章 数据库学习路线)
- [第四十一章 如果要阅读源码,应该先看什么?](#第四十一章 如果要阅读源码,应该先看什么?)
- [第四十二章 最终总结](#第四十二章 最终总结)
《现代 Key-Value 数据库原理:从 B+Tree 到 LSM Tree》
第五篇:现代数据库横向对比
前面四篇分别从数据库存储基础、B+Tree、LMDB、LevelDB出发,逐渐建立了一个完整的认知体系。
现在可以把这些知识放在一起。
现代数据库看起来种类非常多:
text
LMDB
LevelDB
RocksDB
SQLite
Redis
BadgerDB
bbolt
WiredTiger
TiKV
FoundationDB
Pebble
但如果从底层存储结构来看,它们并没有想象中那么复杂。
大量数据库最终都可以归结到几种核心思想:
text
B+Tree
LSM Tree
Hash
SkipList
Log Structured Storage
Memory Data Structure
Distributed KV
因此,学习数据库真正重要的不是记住几十个数据库的名字,而是理解:
为什么这个数据库选择这种数据结构,以及这种选择解决了什么问题。
第一章 现代数据库整体分类
先建立一个全局视图。
text
Database
|
+-----------------+------------------+
| | |
v v v
B+Tree LSM Tree Memory
| | |
+----+----+ +----+----+ +----+----+
| | | | | |
LMDB SQLite LevelDB RocksDB Redis ...
| | | |
bbolt WiredTiger | Pebble
|
TiKV
再进一步:
text
B+Tree
|
+-- LMDB
+-- SQLite
+-- bbolt
+-- WiredTiger
LSM
|
+-- LevelDB
+-- RocksDB
+-- Pebble
+-- BadgerDB
+-- TiKV
Memory
|
+-- Redis
但是需要注意:
现实数据库往往不是纯粹的一种结构。
例如:
text
BadgerDB
=
LSM Tree
+
Value Log
Redis:
text
Hash
SkipList
Rax
List
Set
Sorted Set
FoundationDB则更加复杂:
text
Distributed Transaction
+
Storage Engine
所以:
"数据库属于什么类型"通常应该理解为它的核心存储架构,而不是说整个数据库只包含一种数据结构。
第二章 现代数据库横向总表
先给出本篇最重要的一张表。
| 数据库 | 核心结构 | 核心特点 | 典型场景 |
|---|---|---|---|
| LMDB | B+Tree | mmap、Copy-On-Write、MVCC、读性能优秀 | AI数据集、嵌入式 |
| LevelDB | LSM Tree | MemTable、SSTable、Compaction | 嵌入式KV、日志 |
| RocksDB | LSM Tree | 高性能、丰富Compaction和缓存优化 | 高写入、高并发 |
| BadgerDB | LSM + Value Log | Go生态、Key/Value分离 | Go服务 |
| WiredTiger | B+Tree等 | 高性能通用存储引擎 | MongoDB |
| SQLite | B+Tree | 单文件、完整SQL | 桌面、移动、嵌入式 |
| Berkeley DB | B+Tree / Hash等 | 经典嵌入式数据库 | 嵌入式系统 |
| bbolt | B+Tree | Go、单文件、简单 | Go嵌入式应用 |
| Redis | 内存数据结构 | 极低延迟、丰富数据结构 | 缓存、消息、实时数据 |
| TiKV | RocksDB等LSM | 分布式KV、Raft | 云原生数据库 |
| Pebble | LSM Tree | CockroachDB生态 | 分布式数据库 |
| FoundationDB | 分布式KV | 强事务、可组合 | 分布式数据库 |
第三章 LMDB
LMDB:
text
Lightning Memory-Mapped Database
它是OpenLDAP项目中的核心嵌入式数据库之一。
核心设计:
text
B+Tree
+
mmap
+
Copy-On-Write
+
MVCC
3.1 LMDB的数据结构
可以简单表示:
text
Root
|
+-----+-----+
| |
Branch Branch
| |
+--+--+ +--+--+
| | | |
Leaf Leaf Leaf Leaf
数据库文件:
text
data.mdb
直接通过:
text
mmap()
映射到进程地址空间。
于是:
text
Disk
|
v
Virtual Memory
|
v
Page
|
v
CPU
3.2 LMDB为什么读取快?
传统数据库:
text
Disk
↓
Read
↓
Buffer Pool
↓
Copy
↓
Application
LMDB:
text
Disk
↓
mmap
↓
Virtual Address
↓
Application
因此:
减少了大量:
text
系统调用
用户态/内核态切换
数据复制
3.3 LMDB的核心优势
读取优秀
尤其适合:
text
大量读取
少量写入
事务简单
采用:
text
MVCC
多个Reader可以并发。
数据结构稳定
B+Tree:
text
查询:
O(logN)
非常适合AI数据集
例如:
text
image_001 → JPEG
image_002 → JPEG
image_003 → JPEG
最终:
text
dataset.mdb
3.4 LMDB的限制
最大的特点其实也是最大的限制:
text
一个Writer
因此如果业务是:
text
大量并发写入
LMDB通常不是第一选择。
第四章 LevelDB
LevelDB 是Google设计的嵌入式KV数据库。
核心:
text
LSM Tree
完整写路径:
text
Put
|
+----> WAL
|
+----> MemTable
|
v
Immutable
|
v
SSTable
|
v
Compaction
4.1 LevelDB为什么选择LSM?
因为它希望解决:
text
B+Tree
大量随机写
的问题。
LSM:
text
Random Write
↓
Memory
↓
Sequential Write
所以:
text
写入吞吐
通常非常优秀。
4.2 LevelDB核心组件
text
MemTable
SkipList
WAL
Immutable MemTable
SSTable
Bloom Filter
Compaction
Manifest
Snapshot
Iterator
这些概念实际上已经成为现代LSM数据库的基础。
4.3 LevelDB的缺点
LSM不是没有代价。
主要问题:
text
Compaction
会导致:
text
Write Amplification
Read Amplification
Space Amplification
例如:
text
写1GB
↓
后台Compaction
↓
可能产生多个GB的数据读写
所以:
LSM把随机写问题转化成了后台整理问题。
第五章 RocksDB
RocksDB 是基于LevelDB思想发展起来的高性能LSM存储引擎。
可以理解成:
text
LevelDB
|
+----------------+
| |
性能优化 功能扩展
| |
+-------+--------+
|
RocksDB
5.1 为什么需要RocksDB?
Facebook面对:
text
SSD
NVMe
多核CPU
高并发
海量数据
LevelDB的设计比较简单。
RocksDB进一步强化:
text
Compaction
Cache
Concurrency
SSD
Write Buffer
Compression
Column Family
Bloom Filter
5.2 Column Family
RocksDB一个非常重要的设计:
text
Column Family
例如:
text
Database
├── metadata
├── image
├── feature
└── result
不同数据可以:
text
独立配置
独立Compaction
独立Cache策略
这对于大型系统很重要。
5.3 RocksDB适合什么?
例如:
text
高写入日志
时序数据
缓存
状态存储
分布式数据库底层存储
如果系统:
text
写入量非常大
RocksDB通常比LMDB更值得考虑。
第六章 BadgerDB
BadgerDB 是Go生态中的高性能KV数据库。
核心思想:
text
LSM Tree
+
Value Log
6.1 为什么Key和Value分开?
传统:
text
SSTable
Key + Value
Key + Value
Key + Value
如果Value非常大:
text
10KB
100KB
1MB
Compaction就会产生大量数据移动。
Badger:
text
LSM
Key
Key
Key
Key
而Value:
text
Value Log
Value
Value
Value
形成:
text
Key
↓
Value Pointer
↓
Value Log
例如:
text
Key:
image_001
Value Pointer:
offset = 100000
size = 200KB
↓
Value Log
100000
|
v
JPEG Data
这样可以减少大Value参与LSM Compaction的成本。
6.2 Badger适合什么?
特别适合:
text
Go服务
大Value
高写入
嵌入式KV
例如:
text
Go
↓
Badger
↓
RocksDB风格LSM
第七章 BoltDB / bbolt
Go生态还有一个非常经典的数据库:
bbolt。
它的前身是BoltDB。
核心:
text
B+Tree
和LMDB思路比较接近。
7.1 bbolt结构
text
Database
|
v
B+Tree
|
+-----+-----+
| |
Bucket Bucket
Bucket可以理解成:
text
Key Namespace
例如:
text
users
images
config
7.2 bbolt与LMDB
两者都属于:
text
B+Tree
但是:
text
LMDB
重点:
text
mmap
Copy-On-Write
MVCC
而:
text
bbolt
是Go生态中更加简单直接的嵌入式B+Tree方案。
第八章 SQLite
SQLite 是完全不同于LevelDB的数据库。
它不仅是:
text
KV
而是:
text
关系型数据库
支持:
text
SQL
Table
Index
Transaction
Join
Query
8.1 SQLite底层是什么?
SQLite主要使用:
text
B-Tree
进行表和索引的组织。
例如:
text
Database
+-------------------+
| Table |
| |
| B-Tree |
+-------------------+
+-------------------+
| Index |
| |
| B-Tree |
+-------------------+
8.2 SQLite最大的特点
一个数据库:
text
database.db
可以直接复制。
例如:
text
app.db
里面:
text
用户
配置
历史记录
索引
全部存在一个文件。
8.3 SQLite为什么这么流行?
因为它解决的是:
不需要部署数据库服务器,但又需要完整关系型数据库能力。
例如:
text
Android
iOS
Windows桌面
嵌入式设备
浏览器
单机软件
8.4 SQLite vs LMDB
如果数据是:
text
Key → Value
而且:
text
极致简单
LMDB非常合适。
如果需要:
sql
SELECT *
FROM image
WHERE camera_id = 10
AND timestamp > ...
那么:
text
SQLite
明显更加合适。
第九章 Berkeley DB
Berkeley DB 是非常经典的嵌入式数据库。
历史非常悠久。
它支持多种数据访问方式:
text
B+Tree
Hash
Queue
Recno
因此它不是单纯的:
text
B+Tree数据库
而是一个:
多种嵌入式存储结构集合。
9.1 Berkeley DB适合什么?
历史上大量用于:
text
嵌入式系统
网络服务
配置数据
缓存
本地数据库
其重要意义在于:
它代表了早期:
text
Embedded Database
的经典设计路线。
第十章 Redis
Redis 和LMDB、LevelDB有很大区别。
Redis的核心思想:
数据主要驻留在内存中。
10.1 Redis不是单一数据结构
Redis内部使用多种数据结构。
例如:
text
String
Hash
List
Set
Sorted Set
Stream
底层还涉及:
text
Hash Table
SkipList
Rax
ZipList / Listpack
10.2 Redis为什么快?
典型路径:
text
Client
↓
Redis Server
↓
Memory
↓
Data Structure
而不是:
text
Client
↓
Disk
↓
B+Tree
↓
Data
因此:
text
访问延迟
非常低。
10.3 Redis为什么还能持久化?
Redis提供:
text
RDB
AOF
例如:
text
Memory
|
+---- RDB
|
+---- AOF
所以Redis并不是:
text
纯内存 = 数据一定不持久化
而是:
以内存为核心数据层,同时提供持久化机制。
第十一章 WiredTiger
WiredTiger 是MongoDB长期使用的重要存储引擎。
MongoDB:
text
Document Database
底层:
text
MongoDB
|
WiredTiger
|
Storage Engine
11.1 WiredTiger为什么重要?
它代表了一种:
text
通用高性能存储引擎
设计。
涉及:
text
B-Tree
LSM相关能力
Compression
Cache
Transactions
MVCC
WiredTiger并不能简单概括为"就是一个B+Tree数据库",实际实现包含多个存储和事务机制。
11.2 MongoDB为什么需要独立存储引擎?
因为MongoDB上层解决:
text
Document
Query
Aggregation
Replication
而存储引擎解决:
text
Page
Index
Transaction
Disk
Cache
Concurrency
于是:
text
MongoDB
|
v
WiredTiger
|
v
Disk
这也是数据库分层架构的重要体现。
第十二章 Pebble
Pebble 是CockroachDB生态中的LSM存储引擎。
它与LevelDB、RocksDB属于同一个思想体系:
text
LSM
|
+-- MemTable
+-- WAL
+-- SSTable
+-- Compaction
12.1 Pebble为什么出现?
CockroachDB最初大量借鉴RocksDB。
随着自身需求不断发展:
text
分布式
事务
Raft
Cloud Native
需要更加贴合自身系统的存储引擎。
于是发展出:
text
Pebble
第十三章 TiKV
TiKV 是分布式KV数据库。
它与前面最大的区别:
text
LMDB
单机
text
LevelDB
单机
text
RocksDB
单机存储引擎
而:
text
TiKV
分布式KV
13.1 TiKV整体架构
可以理解为:
text
Client
|
v
TiKV
|
+----------+----------+
| |
Raft RocksDB
| |
v v
Replication Storage
多个TiKV节点:
text
Node1
Node2
Node3
通过:
text
Raft
保证副本一致性。
13.2 为什么TiKV使用RocksDB思想?
因为:
RocksDB已经很好地解决:
text
单机高性能存储
TiKV可以在其基础上增加:
text
分布式
Raft
事务
Region
Replication
形成:
text
Distributed Database
+
Local Storage Engine
第十四章 FoundationDB
FoundationDB 的设计思路又有所不同。
它强调:
text
Distributed
+
Transactions
+
Ordered Key-Value
可以把它理解成:
text
Application
↓
FoundationDB API
↓
Distributed Transaction Layer
↓
Storage Servers
↓
Storage Engine
14.1 FoundationDB最大的特点
不是:
text
提供很多SQL语法
而是:
提供一个强一致的、有序Key-Value基础层。
在它之上:
可以构建:
text
SQL
Document DB
Metadata Store
Queue
Index
这是一种:
text
Database as a Foundation
的思想。
第十五章 B+Tree vs LSM Tree
现在回到整个系列最核心的问题。
B+Tree
text
Root
|
+----+----+
| |
Branch Branch
| |
Leaf Leaf
数据始终维护在:
text
Tree
中。
LSM
text
MemTable
↓
L0
↓
L1
↓
L2
↓
L3
数据先进入:
text
Memory
再不断:
text
Merge
第十六章 写入性能
B+Tree
典型:
text
Write
↓
Locate Page
↓
Modify Page
↓
Write Page
可能出现:
text
Random IO
LSM
text
Write
↓
MemTable
↓
Sequential WAL
↓
Flush
因此:
text
大量写入
通常更加有优势。
第十七章 读取性能
B+Tree:
text
Root
↓
Branch
↓
Leaf
通常只需要:
text
O(logN)
并且:
text
Key Range
天然适合范围扫描。
LSM:
text
MemTable
Immutable
L0
L1
L2
L3
可能需要:
text
多个层级
多个SST
所以:
LSM需要Bloom Filter、Block Cache、Index等机制来降低读取成本。
第十八章 Read Amplification
读放大:
为了读取一个用户数据,系统实际读取了多少额外数据。
例如:
text
用户需要:
1KB
数据库可能实际:
text
读取:
4KB Block
这就是Block级读放大。
LSM还可能:
text
MemTable
+
L0
+
L1
+
L2
多个层级检查。
第十九章 Write Amplification
写放大:
text
用户写入:
1GB
数据库实际:
text
WAL
+
SSTable
+
Compaction
+
Rewrite
最终磁盘可能写:
text
3GB
5GB
10GB
这就是:
text
Write Amplification
LSM数据库尤其需要关注这一指标。
第二十章 Space Amplification
空间放大:
text
用户有效数据:
100GB
磁盘实际:
text
150GB
甚至:
text
200GB
原因可能包括:
text
旧版本
Tombstone
Compaction尚未完成
冗余Block
Index
Bloom Filter
第二十一章 三种放大之间的关系
这是LSM数据库非常重要的概念。
text
Database Performance
|
+------------+------------+
| | |
v v v
Read Ampl. Write Ampl. Space Ampl.
| | |
读取 写入 空间
| | |
SSTable Compaction Old Data
三者往往存在Trade-off。
例如:
text
Compaction更积极
可能:
text
Read Amplification ↓
Space Amplification ↓
Write Amplification ↑
因此现代LSM数据库实际上一直在寻找:
读、写、空间三者之间的最佳平衡。
第二十二章 mmap vs Buffer Pool
这是LMDB与传统数据库非常重要的区别。
mmap
LMDB:
text
data.mdb
|
v
mmap
|
v
Virtual Memory
操作系统负责:
text
Page Cache
Page Fault
Memory Mapping
Buffer Pool
传统数据库:
text
Disk
↓
Buffer Pool
↓
Page
↓
Database
数据库自己管理:
text
Page Cache
Eviction
Dirty Page
22.1 两者区别
| mmap | Buffer Pool | |
|---|---|---|
| 管理者 | OS为主 | 数据库 |
| 地址空间 | 直接映射 | 数据库控制 |
| 数据复制 | 通常更少 | 通常存在Page管理 |
| 控制能力 | 较弱 | 较强 |
| 实现 | 简单 | 复杂 |
| 典型 | LMDB | SQLite等数据库体系中常见 |
但是不能简单理解为:
text
mmap一定比Buffer Pool快
实际性能取决于:
text
工作负载
数据大小
访问模式
内存压力
操作系统
存储设备
第二十三章 SSD时代的数据库
传统观点:
text
HDD
随机IO很慢
↓
LSM非常有优势
到了SSD:
text
SSD
随机IO变快
那么:
B+Tree是不是就没用了?
不是。
23.1 SSD改变的是成本,而不是原理
SSD依然存在:
text
Random IO
Write Amplification
Garbage Collection
Erase Block
尤其NVMe:
text
高IOPS
高并发
低延迟
反而使:
text
后台Compaction
多线程IO
并发Flush
成为新的优化重点。
第二十四章 HDD、SSD、NVMe对比
可以简单理解:
| 存储 | 特点 | 数据库影响 |
|---|---|---|
| HDD | 随机访问非常昂贵 | 尽量顺序IO |
| SATA SSD | 随机IO明显提升 | LSM/B+Tree均可 |
| NVMe SSD | 高IOPS、低延迟 | 更关注并发和写放大 |
24.1 HDD
HDD:
text
磁头移动
+
盘片旋转
随机:
text
Read A
Seek
Read B
Seek
Read C
非常慢。
所以:
text
Sequential IO
优势巨大。
LSM Tree非常适合这种思路。
24.2 NVMe
NVMe:
text
PCIe
|
SSD Controller
|
Flash
可以同时处理大量IO。
于是:
text
Compaction
可以使用:
text
多个线程
多个IO队列
第二十五章 AI数据集应该选什么?
假设:
text
1000万图片
每张:
100KB ~ 5MB
目标:
text
训练读取
方案一:LMDB
text
图片
↓
LMDB
↓
DataLoader
↓
GPU
非常合适。
尤其:
text
写一次
读很多次
方案二:LevelDB
也可以。
但是:
text
Compaction
对于大Value并不一定理想。
方案三:RocksDB
适合:
text
持续更新
数据不断写入
大规模服务
但如果只是:
text
训练集静态读取
LMDB通常更加直接。
第二十六章 日志系统应该选什么?
假设:
text
每秒:
100万条日志
核心要求:
text
高吞吐
顺序写
后台整理
LSM:
text
RocksDB
LevelDB
会更加自然。
第二十七章 缓存系统应该选什么?
如果目标:
text
微秒级访问
大量热点数据
TTL
Counter
List
Set
那么:
text
Redis
更加适合。
因为:
text
数据
|
Memory
而不是:
text
Disk
|
B+Tree
第二十八章 嵌入式设备应该选什么?
假设:
text
工业设备
Windows/Linux
单机
配置数据
状态数据
可以考虑:
text
SQLite
LMDB
bbolt
如果需要:
text
SQL
复杂查询
关系
选择:
text
SQLite
如果:
text
Key-Value
读多写少
追求极低读取开销
可以考虑:
text
LMDB
第二十九章 Go项目应该选什么?
如果:
text
Go
嵌入式KV
可以考虑:
text
bbolt
BadgerDB
Pebble
大致:
text
B+Tree
|
bbolt
LSM
|
Badger
Pebble
第三十章 分布式数据库应该选什么?
如果已经进入:
text
多机器
多副本
故障恢复
一致性
事务
就不应该只考虑:
text
LMDB
LevelDB
RocksDB
因为这些本质上主要是:
text
Local Storage Engine
需要:
text
Raft
Replication
Transaction
Distributed Scheduling
此时可以考虑:
text
TiKV
FoundationDB
CockroachDB
第三十一章 Storage Engine与Database的区别
这是非常重要的概念。
很多初学者会把:
text
RocksDB
直接理解成:
text
完整数据库系统
实际上更准确的是:
text
RocksDB
↓
Storage Engine
而:
text
TiKV
↓
Distributed Database
↓
RocksDB
同样:
text
MongoDB
↓
Database
↓
WiredTiger
↓
Storage Engine
所以可以形成:
text
Database
|
v
Storage Engine
|
v
File System
|
v
SSD
第三十二章 数据库的分层
现代数据库大致可以拆成:
text
Application
|
v
Query Layer
|
v
Transaction
|
v
Storage Engine
|
+-----------+-----------+
| |
Memory Disk
| |
Cache/MemTable SST/B+Tree
| |
+-----------+-----------+
|
v
File System
|
v
SSD
如果是分布式数据库:
text
Client
|
v
Distributed Layer
|
+----------+----------+
| | |
Node Node Node
| | |
Storage Storage Storage
第三十三章 为什么没有"最好的数据库"
这是数据库选择中最重要的结论。
因为:
text
数据库性能
不是一个单一数字。
必须考虑:
text
Read
Write
Latency
Throughput
Space
Consistency
Transaction
Scale
Availability
例如:
LMDB
text
Read:
★★★★★
Write:
★★★
Distributed:
☆
RocksDB
text
Read:
★★★★
Write:
★★★★★
Distributed:
☆
Redis
text
Memory Read:
★★★★★
Memory Write:
★★★★★
Disk Storage:
取决于持久化策略
SQLite
text
SQL:
★★★★★
Embedded:
★★★★★
Distributed:
☆
因此:
数据库不是越复杂越好,而是要匹配访问模式。
第三十四章 从访问模式选择数据库
这是比记数据库名字更加重要的方法。
场景一:读多写少
text
Read >>> Write
考虑:
text
LMDB
B+Tree
场景二:写多读多
text
Write ≈ Read
考虑:
text
RocksDB
LSM
场景三:超低延迟缓存
text
Memory
考虑:
text
Redis
场景四:需要SQL
text
SQL
考虑:
text
SQLite
PostgreSQL
MySQL
而不是:
text
RocksDB
然后自己实现SQL。
场景五:分布式KV
text
Multi Node
考虑:
text
TiKV
FoundationDB
第三十五章 AI/视觉行业数据库选择
结合视觉行业,可以得到一个比较实用的选择表。
| 数据类型 | 推荐方案 | 原因 |
|---|---|---|
| 静态图片数据集 | LMDB | 读性能好、简单 |
| 点云数据集 | LMDB / RocksDB | 取决于读写比例 |
| Tensor数据集 | LMDB | 顺序读取方便 |
| 推理结果 | SQLite | 需要查询和统计 |
| 实时设备状态 | Redis | 低延迟 |
| 高频日志 | RocksDB | 高写入 |
| 本地配置 | SQLite / LMDB | 单机嵌入式 |
| 大规模分布式KV | TiKV | 分布式一致性 |
| Go本地KV | bbolt / Badger / Pebble | Go生态 |
| MongoDB底层 | WiredTiger | 文档数据库存储引擎 |
第三十六章 一个工业视觉系统应该如何组合?
实际项目通常不会:
整个系统只选择一种数据库。
而是:
text
Vision System
|
+-------------+-------------+
| | |
v v v
Redis SQLite LMDB
| | |
实时状态 元数据 图片数据
|
v
Device Cache
例如:
text
Camera
|
+---- Image ------> LMDB/NAS
|
+---- Result ------> SQLite
|
+---- Status ------> Redis
|
+---- Log ---------> RocksDB
这才是实际工程更加常见的架构。
第三十七章 B+Tree与LSM Tree的最终理解
可以用一句话概括:
B+Tree
直接维护最终的数据结构。
text
Write
↓
Tree
↓
Disk
LSM Tree
先接受写入,再通过后台合并维护最终的数据结构。
text
Write
↓
Memory
↓
SST
↓
Merge
↓
Final State
因此:
text
B+Tree:
Write Cost ↑
Read Cost ↓
而:
text
LSM:
Write Cost ↓
Read/Compaction Cost ↑
当然,这是总体趋势,不是绝对规律。
现代数据库通过:
text
Cache
Bloom Filter
Prefetch
Compaction
Index
Compression
Batch
Parallelism
不断缩小这种差距。
第三十八章 从LMDB到LevelDB,再到RocksDB
整个系列可以串成一条技术发展路线。
text
Database
|
+--------+--------+
| |
v v
B+Tree LSM Tree
| |
v v
LMDB LevelDB
|
v
RocksDB
|
+--------------+--------------+
| | |
v v v
TiKV Pebble Badger
然后进一步:
text
Local Storage Engine
|
v
Distributed Database
|
+----+----+
| |
TiKV FoundationDB
第三十九章 现代数据库真正的核心技术
如果从源码和工程角度继续深入,实际上最终会发现:
现代数据库真正重要的是下面这些技术。
text
1. B+Tree
2. LSM Tree
3. WAL
4. MVCC
5. Snapshot
6. Copy-On-Write
7. Cache
8. Bloom Filter
9. Compaction
10. Transaction
11. Lock
12. Iterator
13. Serialization
14. Compression
15. Checksum
16. Recovery
17. Concurrency
18. Replication
而:
text
LMDB
LevelDB
RocksDB
SQLite
TiKV
只是这些技术不同组合的结果。
第四十章 数据库学习路线
如果希望真正达到:
text
能够阅读数据库源码
推荐按照下面的顺序学习。
text
第一阶段
Array
Linked List
Hash
Tree
Heap
↓
第二阶段
BST
AVL
Red-Black Tree
B Tree
B+Tree
↓
第三阶段
Disk IO
Page
Buffer Pool
WAL
Transaction
↓
第四阶段
LMDB
B+Tree
mmap
MVCC
Copy-On-Write
↓
第五阶段
LSM Tree
SkipList
MemTable
SSTable
Bloom Filter
↓
第六阶段
LevelDB
↓
第七阶段
RocksDB
Compaction
Cache
Column Family
SSD Optimization
↓
第八阶段
Distributed Storage
Raft
MVCC
Replication
Sharding
↓
第九阶段
TiKV
FoundationDB
CockroachDB
第四十一章 如果要阅读源码,应该先看什么?
对于C++开发者,建议不要一开始直接阅读整个RocksDB。
可以按照:
text
LevelDB
作为入口。
因为LevelDB:
text
代码量相对较小
设计思想清晰
核心模块集中
建议阅读顺序:
text
DBImpl
↓
Write
↓
MemTable
↓
SkipList
↓
WAL
↓
VersionSet
↓
SSTable
↓
Block
↓
Iterator
↓
Compaction
理解LevelDB之后:
再看:
text
RocksDB
会容易很多。
第四十二章 最终总结
整个系列最终可以浓缩成下面这张图:
text
Key-Value Storage
|
+--------------+--------------+
| |
v v
B+Tree LSM
| |
+--------+--------+ +--------+--------+
| | | | | |
LMDB SQLite bbolt LevelDB RocksDB Pebble
| | | |
mmap SQL SSTable |
| Compaction |
MVCC |
v
TiKV
而不同数据库实际上是在解决不同的问题:
text
LMDB
解决:
如何让嵌入式B+Tree读取非常快?
LevelDB
解决:
如何把随机写转换成顺序写?
RocksDB
解决:
如何把LSM做到高吞吐、高并发、适合SSD?
SQLite
解决:
如何在一个文件里提供完整关系型数据库?
Redis
解决:
如何提供极低延迟的内存数据结构?
TiKV
解决:
如何把KV存储扩展到分布式环境?
FoundationDB
解决:
如何构建强事务、可组合的分布式KV基础设施?
最终应该形成这样一个认知:
数据库不是一堆API,而是一套"数据结构 + 内存管理 + 磁盘布局 + 并发控制 + 事务 + 崩溃恢复"的系统工程。
B+Tree解决的是:
text
如何组织磁盘上的有序数据
LSM Tree解决的是:
text
如何降低随机写成本
WAL解决的是:
text
程序崩溃以后如何恢复
MVCC解决的是:
text
读写并发时如何看到一致的数据
Bloom Filter解决的是:
text
如何快速判断数据大概率不存在
Compaction解决的是:
text
如何把大量SSTable重新整理
Cache解决的是:
text
如何减少昂贵的磁盘访问
Raft解决的是:
text
多个机器之间如何保持一致
而这些技术组合起来,才形成我们今天看到的:
text
SQLite
LMDB
LevelDB
RocksDB
Redis
TiKV
FoundationDB
MongoDB
CockroachDB
因此,真正掌握现代数据库的关键,不是记住:
text
"哪个数据库性能最高"
而是看到一个业务需求后能够判断:
text
数据规模?
↓
读多还是写多?
↓
Value大小?
↓
是否需要范围查询?
↓
是否需要事务?
↓
是否需要SQL?
↓
是否需要分布式?
↓
延迟还是吞吐?
↓
HDD / SSD / NVMe?
↓
最终选择合适的Storage Engine
这也是从数据结构学习数据库 ,最终走向数据库系统设计的关键一步。