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

RocksDB

简介

RocksDB项目是起源于Facebook,是一个高性能的嵌入式键值存储库,特别适合用于需要低延迟和高吞吐量的场景。。RocksDB 借鉴了开源leveldb项目的重要代码以及Apache HBase的想法。最初的代码是从开源 leveldb 1.5 fork出来的。
库组件形式嵌入程序中,为大规模分布式应用在ssd上运行提供优化。RocksDB不提供高层级的操作,例如备份、负载均衡、快照等,而是选择提供工具支持将实现交给上层应用。正是这种高度可定制化能力,允许RocksDB对广泛的需求和工作负载场景进行定制。
很多项目都接纳了RocksDB作为其后端存储的一种解决方案,如Mysql, Ceph, Flink, MongoDB, TiDB等。

RocksDB 架构

RocksDB使用Log-Structured Merge(LSM)trees做为基本的数据存储结构。
在这里插入图片描述

Block Cache

纯内存存储结构,存储SST文件被经常访问的热点数据

一个Cache对象可以被同一个进程中的多个RocksDB实例共享,用户可以控制整体的缓存容量。
块缓存存储未压缩的块。用户可以选择设置存储压缩块的二级块缓存。读取将首先从未压缩的块缓存中获取数据块,然后是压缩的块缓存。如果使用 Direct-IO,压缩块缓存可以替代 OS 页面缓存。

Block Cache有两种缓存实现,分别是 LRUCache 和 ClockCache。两种类型的缓存都使用分片以减轻锁争用。容量平均分配给每个分片,分片不共享容量。默认情况下,每个缓存最多会被分成 64 个分片,每个分片的容量不小于 512k 字节。

  • LRUCache: 默认的缓存实现。使用容量为8MB的基于LRU的缓存。缓存的每个分片都维护自己的LRU列表和自己的哈希表以供查找。通过每个分片的互斥锁实现同步,查找与插入都需要对分片加锁。
    极少数情况下,在块上进行读或迭代的,并且固定的块总大小超过限制,缓存的大小可能会大于容量。如果主机没有足够的内存,这可能会导致意外的 OOM 错误,从而导致数据库崩溃
  • ClockCache: ClockCache 实现了 CLOCK 算法。时钟缓存的每个分片都维护一个缓存条目的循环列表。时钟句柄在循环列表上运行,寻找要驱逐的未固定条目,但如果自上次扫描以来已使用过,也给每个条目第二次机会留在缓存中。
    ClockCache 还不稳定,不建议使用

Write Buffer Manager

Write Buffer Manager 用于控制多个列族或者多个数据库实例的内存表总使用量。
使用方式:用户创建一个write buffer manager对象,并将对象传递到需要控制内存的列族或数据库实例中。

有两种限制方式:

  1. 限制 memtables 的总内存用量

触发其中一个条件将会在实例的列族上触发flush操作:

1. 如果活跃的 memtables 使用超过阈值的90%
2. 总内存超过限制,活跃的 mamtables 使用也超过阈值的 50% 时。
  1. memtable 的内存占用转移到 block cache

大多数情况下,block cache中实际使用的block远小于block cache中缓存的,所以当用户启用该功能时,block cache容量将覆盖block cache和memtable两者的内存使用量。
如果用户同时开启 cache_index_and_filter_blocks,那么RocksDB的三大内存区域(index and filter cache, memtables, block cache)内存占用都在block cache中。

SST文件格式

BlockBasedTable 是 SSTable 的默认表格式。

<beginning_of_file>
[data block 1]
[data block 2]
...
[data block N]
[meta block 1: filter block]                  (see section: "filter" Meta Block)
[meta block 2: index block]
[meta block 3: compression dictionary block]  (see section: "compression dictionary" Meta Block)
[meta block 4: range deletion block]          (see section: "range deletion" Meta Block)
[meta block 5: stats block]                   (see section: "properties" Meta Block)
...
[meta block K: future extended block]  (we may add more meta blocks in the future)
[metaindex block]
[Footer]                               (fixed size; starts at file_size - sizeof(Footer))
<end_of_file>

数据块 DataBlock:键值对序列按照根据排序规则顺序排列,划分为一系列数据块(data block)。这些块在文件开头一个接一个排列,每个数据块可选择性压缩。

元数据块 MetaBlock:紧接着数据块的是一堆元数据块(meta block),元数据块包括:过滤块(filter block)、索引块(index block)、压缩字典块(compression dictionary block)、范围删除块(range deletion block)、属性块(properties block)。

MetaIndexBlock: 元索引块包含一个映射表指向每个meta block,key是meta block的名称,value是指向该meta block的指针,指针通过offset、size指向数据块。

页脚 Footer:文件末尾是固定长度的页脚。包括指向metaindex block的指针,指向index block的指针,以及一个magic number。

Meta Block的具体种类

索引块 Index Block

索引块用于查找包含指定key的数据块。是一种基于二分搜索的数据结构。一个文件可能包含一个索引块,也可能包含一组分区索引块,这取决于使用配置。即存在全局索引与分区索引两种索引方式。

过滤器块 Filter Block

全局过滤器、分区过滤器,都是通过用布隆过滤器实现
全局过滤器 Full Filter: 在此过滤器中,整个 SST 文件只有一个过滤器块。
分区过滤器 Partitioned Filter: Full Filter 被分成多个子过滤器块,在这些块的顶层有一个索引块用于将key映射到相应的子过滤器块。
压缩字典块 Compression Dictionary Block

包含用于在压缩/解压缩每个块之前准备压缩库的字典。

范围删除块 Range Deletion Block

范围删除块包含文件中key与序列号中的删除范围。在读请求下发到sst的时候能够从sst中的指定区域判断key是否在deleterange 的范围内部,存在则直接返回NotFound。memtable中也有一块区域实现同样的功能。
compaction或者flush的时候会清除掉过时的tombstone数据。

属性块 Properties Block

包含属性信息。
统计块格式:

 [prop1] 每个property都是一个键值对    
 [prop2]
 ...
 [propN]

属性信息保证顺序且没有重复,默认情况下包含了以下信息:

 data size               // data block总大小
 index size              // index block总大小
 filter size             // filter block总大小
 raw key size            // 所有key的原始大小
 raw value size          // 所有value的原始大小
 number of entries
 number of data blocks

RocksDB 子模块

RocksDB 5大子模块,分别为:

  • Basic Operation,基本操作定义
  • Terminology,内部术语定义
  • Tool,内部工具
  • Logging/Monitoring ,日志和监控
  • System Behavior,内部系统行为

Basic Operation

除了 RocksDB 核心的KV的操作接口get,put两类操作外,RocksDB 还在此模块中封装了如下几类能适用于特殊使用场景的操作:

  • Iteration,Rocks DB能够支持区间范围内的key迭代器的遍历查找。
  • Compaction Filter,用户可使用 Compaction Filter 对 key
    值进行删除或其它更新操作的逻辑定义,当系统进行 Compact 行为的时候。
  • Creating and Ingesting SST files,当用户想要快速导入大批量数据到系统内时,可以通过线下创建有效格式的
    SST 文件并导入的方式,而非使用 API 进行 KV 值的单独PUT操作。
  • Delete Range,区间范围的删除操作,比一个个 Key 的单独删除调用使用更方便。
  • Low Priority Write,当用户执行大批量数据 load
    的操作时但担心可能会影响到系统正常的操作处理时,可以开启此属性进行优先级的调整。
  • Read-Modify-Write,这个操作的实际含义是 Merge操作的含义,读取现有键值,进行更新(累加计数或依赖原有值的任何更新操作),将新的值写入到原 Key 下。 如果使用原始 Get/Set API 的前提下,我们要调用2次 Get 1次,然后再 Set 1次,在 Merge API 下,使用者调用1次就足够了。
  • Transaction,RocksDB 内部提供乐观式的 OptimisticTransactionDB 和悲观式(事务锁方式)的
    TransactionDB 来支持并发的键值更新操作。

Terminology

首先是RocksDB内部的相关术语定义说明,如上图所示,主要有以下一些术语:

  • Write-Ahead-Log File,类似于HDFS
    JournalNode中的editlog,用于记录那些未被成功提交的数据操作,然后在重启时进行数据的恢复。
  • SSTFile,SST文件是一段排序好的表文件,它是实际持久化的数据文件。里面的数据按照key进行排序能方便对其进行二分查找。在SST文件内,还额外包含以下特殊信息:
    • Bloom Fileter,用于快速判断目标查询key是否存在于当前SST文件内。
    • Index / Partition Index,SST内部数据块索引文件快速找到数据块的位置。
    • Memtable,内存数据结构,用以存储最近更新的db更新操作,memtable空间写满后,会触发一次写出更新操作到SST文件的操作。
    • Block Cache,纯内存存储结构,存储SST文件被经常访问的热点数据。

System Behavior

在RocksDB内部,有着许多系统操作行为来保障系统的平稳运行。

  • Compression,SST文件内的数据能够被压缩存储来减小占用空间。
  • Rate Limit行为。用户能够对其写操作进行速度控制,以此避免写入速度过快造成系统读延迟的现象。
  • Delete Schedule,系统文件删除行为的速度控制。
  • Direct IO,RocksDB支持绕过系统Page Cache,通过应用内存从存储设置中直接进行IO读写操作。
  • Compaction,数据的Compact行为,删除SST文件中重复的key以及过期的key数据。

Logging/Monitoring

RocksDB内部有以下的日志监控工具:

  • Logger,可用的Logger使用类。
  • Statistic / Perf Context and IO Stats
    Context,RocksDB内部各类型操作的时间,操作数计数统计信息,此数据信息能被用户用来发现系统的性能瓶颈操作。
  • EventListener,此监听接口提供了一些event事件发生后的接口回调,比如完成一次flush操作,开始Compact操作的时候等等。

使用

java使用rocksdb

添加依赖

首先,你需要在项目中添加 RocksDB 的依赖。如果你使用 Maven,可以在 pom.xml 中添加以下依赖:

<dependency>
    <groupId>org.rocksdb</groupId>
    <artifactId>rocksdbjni</artifactId>
    <version>6.29.3</version> <!-- 请使用最新版本 -->
</dependency>

基本操作

以下是一些基本的 RocksDB 操作示例:

import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;

public class RocksDBExample {
    static {
        RocksDB.loadLibrary();
    }

    public static void main(String[] args) {
        try (final Options options = new Options().setCreateIfMissing(true)) {
        //打开数据库
            try (final RocksDB db = RocksDB.open(options, "path/to/db")) {
            	//插入数据
                db.put("key1".getBytes(), "value1".getBytes());
                //读取数据
				byte[] value = db.get("key1".getBytes());
				if (value != null) {
				    System.out.println(new String(value));
				}
				//删除
				db.delete("key1".getBytes());
				//迭代数据
				try (final RocksIterator iterator = db.newIterator()) {
				    for (iterator.seekToFirst(); iterator.isValid(); iterator.next()) {
				        System.out.println(new String(iterator.key()) + ": " + new String(iterator.value()));
				    }
				}
            }
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }
}

关闭

如果没有使用try-with-resource,需要关闭数据库

db.close();
options.close();

配置

RocksDB 提供了许多配置选项来优化性能和行为。以下是一些常见的配置:

setCreateIfMissing(true):如果数据库不存在则创建。
setMaxOpenFiles(5000):设置最大打开文件数。
setWriteBufferSize(64 * 1024 * 1024):设置写缓冲区大小。
setMaxWriteBufferNumber(4):设置最大写缓冲区数量。

列族

在 RocksDB 中,列族(Column Families) 是一种将键值对分组管理的机制。每个列族都是一个独立的命名空间,允许在同一数据库中存储多个逻辑上独立的数据集。db可以认为是数据库,列族对应数据库中的表。列族的使用可以提高数据管理的灵活性,并支持更高效的查询和操作。

如果没有指定 Column Family,键值对将会结合到“default” 列族。

列族的特点

  • 独立命名空间:每个列族有自己独立的键值对集合。
  • 共享 WAL(Write-Ahead Log):所有列族共享同一个 WAL,确保原子性。
  • 独立配置:每个列族可以有自己的配置(如压缩策略、内存大小等)。
  • 高效查询:可以单独查询某个列族的数据,减少扫描范围。

列族的基本操作

import org.rocksdb.*;

import java.util.ArrayList;
import java.util.List;

public class RocksDBColumnFamilyExample {
    static {
        RocksDB.loadLibrary();
    }

    public static void main(String[] args) {
        try (final Options options = new Options().setCreateIfMissing(true);
             final ColumnFamilyOptions cfOptions = new ColumnFamilyOptions().optimizeUniversalStyleCompaction()) {

            // 定义列族名称
            final List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>();
            cfDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOptions));
            cfDescriptors.add(new ColumnFamilyDescriptor("cf1".getBytes(), cfOptions));
            cfDescriptors.add(new ColumnFamilyDescriptor("cf2".getBytes(), cfOptions));

            // 打开数据库并获取列族句柄
            final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
            try (final RocksDB db = RocksDB.open(options, "path/to/db", cfDescriptors, cfHandles)) {

                // 获取默认列族和自定义列族的句柄
                ColumnFamilyHandle defaultCfHandle = cfHandles.get(0);
                ColumnFamilyHandle cf1Handle = cfHandles.get(1);
                ColumnFamilyHandle cf2Handle = cfHandles.get(2);

                // 插入数据到不同列族
                db.put(cf1Handle, "key1".getBytes(), "value1".getBytes());
                db.put(cf2Handle, "key2".getBytes(), "value2".getBytes());

                // 从列族中读取数据
                byte[] value1 = db.get(cf1Handle, "key1".getBytes());
                byte[] value2 = db.get(cf2Handle, "key2".getBytes());

                System.out.println("cf1:key1 -> " + new String(value1));
                System.out.println("cf2:key2 -> " + new String(value2));

                // 关闭列族句柄
                for (ColumnFamilyHandle handle : cfHandles) {
                    handle.close();
                }
            }
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }
}

对列族的put/get/delete操作时,要指定列族

列族的管理

创建新列族

ColumnFamilyHandle newCfHandle = db.createColumnFamily(new ColumnFamilyDescriptor("new_cf".getBytes(), cfOptions));

删除

db.dropColumnFamily(cfHandle);
cfHandle.close(); // 关闭句柄

列出所有列族

List<byte[]> columnFamilies = RocksDB.listColumnFamilies(new Options(), "path/to/db");
for (byte[] cfName : columnFamilies) {
    System.out.println(new String(cfName));
}
  1. 列族的配置

每个列族可以有自己的配置选项,例如:

  • 压缩策略:setCompressionType(CompressionType.SNAPPY_COMPRESSION)
  • 内存大小:setWriteBufferSize(64 * 1024 * 1024)
  • 合并操作:optimizeUniversalStyleCompaction()

示例:

ColumnFamilyOptions cfOptions = new ColumnFamilyOptions()
    .setCompressionType(CompressionType.SNAPPY_COMPRESSION)
    .setWriteBufferSize(64 * 1024 * 1024);

http://www.kler.cn/a/574483.html

相关文章:

  • 计算机毕业设计SpringBoot+Vue.js客户关系管理系统CRM(源码+文档+PPT+讲解)
  • 实验一:在Windows 10/11下配置和管理TCP/IP
  • 用DeepSeek搭建一个免费的AI智能量化机器人
  • EtherNet/IP转Modbus解析基于网关模块的罗克韦尔PLC与Modbus上位机协议转换通讯案例
  • 第三十天:Scrapy 框架-分布式
  • 自注意力机制的演进-从Transformer架构到DeepSeek-R1模型的深度语义理解革新
  • Docker Desktop 4.38 安装与配置全流程指南(Windows平台)
  • 如何把GUI做的像Web一样美观:Python PyQt6特性介绍,如何结合QSS美化
  • 每日一题——杨辉三角
  • 机器学习(六)
  • 将数据库结构化数据整合到RAG问答中的方式
  • 大模型day01自然语言+大模型+环境
  • **SystemUI 超详细解析:架构、流程与核心实现**
  • lambda:groupingBy对数据做map转换
  • ​DeepSeek:如何通过自然语言生成HTML文件与原型图?
  • SQL语句执行顺序是什么?
  • php里面__call方法的妙用
  • golang并发编程如何学习
  • [MySQL初阶]MySQL(5)内置函数详解
  • React:Axios