当前位置: 首页 > news >正文

LevelDB介绍和内部机制

介绍

        LevelDB 是 Google 开源的高性能键值对嵌入式数据库,具有一系列设计上的优势,特别适合写多读少、对存储空间要求高效的场景。

核心优势

1. 高写入性能(顺序写磁盘)

  • 基于 LSM-Tree(Log Structured Merge Tree)

  • 所有写操作首先写入内存(memTable)+ WAL;

  • 后台异步 flush 到磁盘,避免频繁随机写;

  • 大量写入性能远高于 B+Tree 类数据库(如 SQLite)。

2. 数据压缩与空间利用率高

  • 支持 Snappy 压缩

  • 自动 Compaction(压缩合并) 机制;

  • 清理删除或覆盖的旧版本,回收磁盘空间;

  • .sst 层级结构使数据有序紧凑。

3. 支持有序遍历与范围查询

  • 支持通过 Iterator 顺序遍历键值;

  • 可高效进行范围查询(Range Scan):

4. 轻量、嵌入式、依赖少

  • 无服务器(serverless),直接嵌入你的应用;

  • 单一 .a 静态库或 .so 动态库,无需依赖外部组件;

  • 跨平台支持良好(Linux、macOS、Windows)。

5. Crash-safe 崩溃恢复机制

  • 所有写入先写入 .log 文件(Write-Ahead Log);

  • 崩溃后可自动恢复到一致状态;

  • 无需额外事务机制即可保证写入安全性。

6. 支持快照(Snapshot)和原子批处理(WriteBatch)

  • Snapshot: 提供一致性读取视图;

  • WriteBatch: 批量写入操作可原子提交,提升性能并简化逻辑。

使用场景

场景类型是否适合说明
批量数据写入✅ 非常适合写优化结构(LSM-tree),顺序写性能极高
嵌入式/边缘设备存储✅ 非常适合轻量、无服务端,资源开销小
日志系统、缓存系统✅ 合适大量写入、偶尔查询
用户画像、指标记录✅ 合适小 key-value、高速写入
离线分析数据落地✅ 合适批量写入,偶尔按 key 扫描或查询
查询很少、但插入频繁的系统✅ 非常合适查询通过前置缓存减少压力

不太适合的场景

场景类型原因
高并发读场景(每秒几千次以上)读放大严重,需大量优化(如加 Bloom Filter、前置缓存)
大量随机读 + 大量随机写写放大 + 查询慢
多表 join、事务一致性需求强不支持 SQL 和事务
需要按字段查询、复杂查询不支持索引,只有键排序

数据结构

  • 内存表(MemTable):最新写入的数据,保存在内存中。

  • Immutable MemTable:旧内存表,尚未 flush。

  • SST 文件(磁盘):排序的 Key-Value 存储在多层磁盘文件中。

  • Block Cache(块缓存):LevelDB 会缓存 SST 文件中的数据块(默认 8MB)。

文件结构

  • 000123.log:正在写的 WAL
  • 000124.sst: L0 的第一个 SST 文件
  • 000125.sst: L0 的第二个 SST 文件(写入后产生)
  • 000130.sst: L1 的文件(Compaction 后生成)
  • MANIFEST-000001:元数据文件
  • CURRENT:指向当前 MANIFEST

memTable

        它是数据库打开时创建的空内存结构,用于接收写入。

生命周期如下:

  1. DB.open(),创建一个空的 memTable

  2. 写入数据时(put()/delete()),首先写 WAL(Write-Ahead Log),然后写入 memTable

  3. memTable 的大小超过阈值(writeBufferSize)时,会:

    • 将当前 memTable 移入 immutable memTable

    • 异步触发 flush 操作,将其写入 .sst 文件;

    • 创建新的 memTable 接收写入。

触发时机会怎样?
memTable flush提高 L0 score
Compaction 完成降低某层 score,增加下一层
写入压力上升多文件生成,score 提高
删除数据未及时 compactscore 居高不下,空间占用大

compactionScore 是 LevelDB 内部的一个关键指标,它决定是否需要触发 Compaction(压缩),以及优先压缩哪个 Level。

  • 每个 Level(L0 ~ L6)都有一个 compactionScore

  • 值越大,代表该 Level 越“拥堵”,越需要被压缩;

  • compactionScore ≥ 1.0,LevelDB 会调度一次 Compaction;

  • 通常由 VersionSet::Finalize() 计算。

JAVA客户端配置

参数默认值说明
createIfMissingfalse如果数据库不存在,是否自动创建(通常你会手动设为 true
errorIfExistsfalse如果数据库已存在,是否抛出异常
paranoidChecksfalse是否进行一致性检查
verifyChecksumsfalse读取时是否校验数据块的校验和
cacheSize8 * 1024 * 1024(8MB)内存缓存大小
blockSize4 * 1024(4KB)每个 block 的大小
writeBufferSize4 * 1024 * 1024(4MB)写缓冲区大小
maxOpenFiles1000打开的文件数上限(仅 native JNI 实现中有效)
compressionTypeSnappy是否启用 Snappy 压缩

使用逻辑

写数据流程

  1. 写入 WAL(Write-Ahead Log)

    1. 写入先追加到 .log 文件(顺序写);

    2. 保证宕机后数据可恢复;

    3. 默认采用 同步写(Sync=true)才能确保持久化;

    4. .log 文件位于 Level 0

  2. 写入 memTable(跳表)

    1. 同步写入内存结构 memTable

    2. 快速写入(无锁 skiplist),数据可被读取;

    3. 内存中结构,不会持久化;

    4. 达到 writeBufferSize 限制(默认 4MB)后变为 immutable memTable,进入 flush。

  3. 触发 MemTable Flush,生成 SST 文件

    1. immutable memTable 转为 SST 文件;

    2. 写入磁盘(SST 为排序存储);

    3. 这些文件是查询的主要来源(也是 compaction 的输入);

    4. 会和已有 Level 0 文件产生重叠。

  4. 进行 Compaction(压缩)

    1. 定期触发(或写压力大时自动触发);

    2. 将多个 SST 文件合并、去重、合并覆盖;

    3. 数据从 L0 -> L1 -> L2 逐层下推;

    4. 保证后期查询高效,数据唯一性提升。

举例

执行 db.put("user123", "value1"),可能流程如下:

  1. 日志:写入 .log 文件中(WAL);

  2. 内存:写入到 memTable

  3. 若 memTable 满:

    • 转为 immutable;

    • 后台线程 flush 成一个新的 000123.sst

  4. 触发 compaction,将多个 sst 合并入更低 level;

  5. .log 文件在 flush 成功后被删除。

读取方式

  1. 先查 memTable(内存表)

    1. memTable 是当前活跃写入的跳表结构;

    2. 有序、支持二分查找;

    3. 如果找到了 key,直接返回对应的 value。

  2. 再查 immutable memTable(只读内存表)

    1. memTable flush 到磁盘前,会被转为 immutable;

    2. 查询会优先在这查找;

    3. 如果 key 存在,会返回。

  3. 然后查各层 .sst 文件(从 Level 0 开始)

    1. Level 0:
      1. 文件之间的 key 区间可能重叠;

      2. 必须逐个文件遍历查找;

      3. 优先查最新的文件(文件编号大 → 数据新);

    2.  Level 1~N(通常到 Level 6):
      1. 每层中的文件 key 区间互不重叠;

      2. 可以通过 key 二分定位到最多一个文件

      3. 只需要在该文件中查找一次;

  4. 使用 Bloom Filter 加速排除(配置生效)

    1. 每个 .sst 文件都可带一个 Bloom Filter;

    2. 在读文件前先看 Bloom Filter 是否可能包含该 key;

      1. 否 → 立即跳过;

      2. 是 → 真正读取磁盘文件;

    3. 可大幅降低磁盘 IO 次数,特别是 key 不存在时。

  5. 读取 Block → 解压缩 → 查找 KV

    1. .sst 文件是由多个 Block 组成的;

    2. 使用索引块定位 block;

    3. 如果有 Snappy 压缩,先解压再查找;

    4. 查到 key 返回 value,否则继续查下一个层级。

举例

执行 db.get("user123"),可能流程如下:

  1. 不在 memTable;

  2. immutable memTable 也没有;

  3. 查 L0 中的 3 个文件,(Bloom Filter 排除 2 个),只查 1 个;

  4. 没找到,再查 L1;

  5. 在 L1 的某个 .sst 文件中命中

  6. 去除文件中的block

  7. 解压 block → 返回 value。

重启恢复步骤

  1. 读取 .log 文件;

  2. 重建 memTable;

  3. 保证数据一致性;

  4. 再继续写入。

http://www.xdnf.cn/news/1010161.html

相关文章:

  • CC工具箱使用指南:【面要素四至】
  • 深度学习5——循环神经网络
  • 智能PDU:从单一功能到多维度升级
  • 洛谷P4555 最长双回文串
  • Ntfs!NtfsFreeRestartTableIndex函数分析
  • 图片压缩工具类
  • Photoshop 2025 性能配置全攻略:硬件选购与软件优化指南
  • 医疗器械行业系统如何提升医疗器械企业的核心竞争力?
  • JavaWeb(Servlet预习)
  • CANopen转PROFINET网关应用:西门子S7-1500主站控制台达AS系列CANopen设备
  • 【金仓数据库征文】_KingbaseES V8R6 运维最佳实践
  • 报表工具顶尖对决系列 --- 文本数据源
  • 大数据学习(139)-数仓设计
  • 量化投资中的Alpha模型与Beta模型的结合
  • 基于鹅优化算法(GOOSE)和三次样条插值的机器人路径规划MATLAB完整实现方案
  • Linux系统详解
  • LeetCode 72. 编辑距离(Edit Distance)| 动态规划详解
  • 网络调试中的难题与破解:跨平台抓包方案实战对比与技巧分享(含Sniffmaster经验)
  • mapstruct中的@Mapper注解详解
  • linux日志工具Rsyslog
  • 【力扣 简单 C++】206. 反转链表
  • 网络的那些事——初级——路由策略
  • 数据库专家 OCP 认证培训:开启职业黄金赛道
  • 关于机器驾驶和人工驾驶的安全若干问题
  • 江苏艾立泰引领塑料包装绿色革命:闭环布局实现环保与经济效益双赢
  • 取得客运资格证后,可以从事哪些具体岗位?(如网约车、班线客车等)
  • 汇编语言深度指南:从基础到字符串操作
  • SimpleDateFormat线程安全终极方案:ThreadLocal魔法抽屉实践
  • 天猫代运营哪个公司比较靠谱
  • 黑马教程强化day2-4