1

1. 诞生

RocksDB 是一个高性能的键值存储引擎,由 Facebook 开发并开源,起源于 Google 的 LevelDB。

发展历程
  • 2012 年:Facebook 开始在 LevelDB 的基础上开发 RocksDB。
  • 2013 年:RocksDB 首次开源,成为 GitHub 上的一个项目。
  • 持续改进:自开源以来,RocksDB 得到了社区的广泛关注和贡献,Facebook 也持续改进和优化 RocksDB,以满足不断变化的需求。
LevelDB

LevelDB 是由 Google 开发的,它是一个基于磁盘的键值存储库,使用 LSM-Tree(Log-Structured Merge Tree)数据结构。LevelDB 设计简洁、性能优秀,适用于嵌入式系统,但在某些高负载情况下,性能和功能不符合 Facebook 的需求。

Facebook 的需求

在 2012 年,Facebook 需要一个能够处理大量数据、高并发读写请求的存储引擎。LevelDB 虽然性能优异,但在面对 Facebook 的海量数据和高并发请求时,暴露出了一些瓶颈和问题,如:

  • 写入放大(Write Amplification):LevelDB 在高写入负载下的性能瓶颈。
  • 读放大(Read Amplification):在某些查询模式下,读取性能不佳。
  • 缺乏多线程支持:LevelDB 的单线程设计难以充分利用多核处理器的性能。
RocksDB 的诞生

为了满足自身的需求,Facebook 在 LevelDB 的基础上进行了大量优化和扩展,最终开发出了 RocksDB。以下是一些关键的改进:

  • 多线程支持:RocksDB 通过引入多线程机制来提升并发性能,充分利用多核处理器的优势。例如,多线程 Compaction 和多线程写入操作。
  • 更好的写入性能:提升写入性能是 RocksDB 的核心目标之一,通过优化 LSM-Tree 和引入多种写入缓冲机制来减少写入放大。
  • 更高效的压缩:提供多种压缩算法和多级别压缩策略,用户可以根据具体需求选择合适的压缩方式,进一步优化存储效率。
  • 丰富的配置选项:RocksDB 提供了大量的配置选项,允许用户根据具体场景进行微调和优化,例如内存管理、缓存大小、写入缓冲区大小等。
  • 列族支持:引入了列族(Column Families)的概念,允许用户将数据按逻辑分组,提供更灵活的数据组织方式。
  • 更强的持久化机制:通过改进 Write-Ahead Log (WAL) 和快照机制,RocksDB 提供了更强的数据持久化和恢复能力。

2. 应用场景

2.1. 特性

  • 高性能:RocksDB 是为快速读写操作优化的,特别适用于需要低延迟和高吞吐量的应用场景,如实时分析、在线事务处理等。
  • 可嵌入:它是一个嵌入式数据库,这意味着它可以直接嵌入到应用程序中,而不需要运行一个独立的服务器进程。这使得它非常适合于需要紧密集成数据库存储的应用程序。
  • 灵活的配置:RocksDB 提供了多种配置选项,允许开发者根据具体需求调整读写性能、内存使用、磁盘使用等。
  • 支持多种存储引擎:除了默认的磁盘存储引擎外,RocksDB 还支持基于内存的存储引擎,以满足不同的性能需求。
  • 丰富的功能:包括快照、事务、列族、压缩、合并操作等,这些功能使得 RocksDB 能够应对各种复杂的应用场景。
  • 横向扩展:虽然 RocksDB 本身是一个嵌入式数据库,但它可以通过集成到分布式系统(如 Apache Cassandra、TiDB 等)中实现横向扩展。

2.2. 应用场景

RocksDB 常用在以下场景中:

  • 缓存存储:由于其快速的读写性能,RocksDB 经常被用作缓存存储解决方案。
  • 实时数据分析:适用于需要低延迟数据写入和查询的实时分析系统。
  • 嵌入式设备:因其嵌入式特性,RocksDB 常用于需要本地数据存储的嵌入式设备中。
  • 分布式系统:作为存储引擎集成到分布式数据库和文件系统中。
1. 日志存储和处理

在日志存储和处理系统中,RocksDB 可以处理高频率的写入操作,并提供高效的数据持久化和查询能力。

特点和优势:

  • 高效写入:RocksDB 的 LSM-Tree 结构和写缓冲机制可以处理高频率的写入操作,减少写入放大。
  • 持久化:通过 WAL 日志和快照机制,确保数据在系统崩溃时也能恢复。
  • 压缩:支持多种压缩算法,减少存储空间消耗。

示例:

  • Kafka 使用 RocksDB 作为内部的状态存储引擎,用于存储日志和偏移量信息。
2. 缓存系统

RocksDB 可以作为持久化缓存层,结合内存缓存和磁盘存储的优势。

特点和优势:

  • 持久化缓存:数据可以永久存储在磁盘上,减少数据丢失的风险。
  • 内存管理:灵活的内存管理策略,可以根据内存大小调整缓存策略。
  • 高性能读取:通过内存中的 memtable 和 block cache 提供高效的读取性能。

示例:

  • Facebook 的 McDipper:一个分布式缓存系统,使用 RocksDB 作为持久化存储。
3. 大数据和分布式系统

在大数据处理和分布式系统中,RocksDB 可以作为高效的存储引擎,支持高并发读写操作。

特点和优势:

  • 多线程支持:充分利用多核处理器的优势,提升并发性能。
  • 灵活配置:提供大量的配置选项,可以根据具体场景进行优化。
  • 事务支持:基本的事务支持,确保数据一致性。

示例:

  • Apache Flink:一个流处理框架,使用 RocksDB 作为状态存储引擎。
  • CockroachDB:一个分布式 SQL 数据库,使用 RocksDB 作为底层存储引擎。
4. 物联网和边缘计算

在资源受限的物联网设备和边缘计算环境中,RocksDB 提供高效的数据存储和处理能力。

特点和优势:

  • 小内存占用:适合内存有限的设备。
  • 高效写入和读取:适合频繁的传感器数据收集和查询。
  • 嵌入式:无需独立服务,可以直接嵌入到应用程序中。

示例:
物联网网关设备:收集传感器数据并进行本地存储和处理。

5. NoSQL 数据库

许多 NoSQL 数据库使用 RocksDB 作为存储引擎,提供高效的键值存储和查询能力。

特点和优势:

  • 高并发支持:多线程写入和读取,提升并发性能。
  • 灵活的键值存储:支持多种数据类型和复杂查询。
  • 持久化和压缩:确保数据安全并优化存储空间。

示例:

  • TiDB:一个分布式 SQL 数据库,使用 RocksDB 作为底层存储。
  • ScyllaDB:一个高性能的 NoSQL 数据库,也使用 RocksDB 作为存储引擎。

3.配置调优

3.1. 配置调优

RocksDB 提供了许多可配置的选项,用于优化数据库的性能、内存使用和磁盘 I/O。以下是一些关键的调优配置选项,涵盖了内存管理、Compaction 策略、WAL(Write-Ahead Log)日志、Block 缓存、数据压缩和并发控制等方面:

3.1.1. 内存管理

MemTable 配置:

  • setWriteBufferSize: 控制 MemTable 的大小,以平衡写入性能和 Compaction 频率。
  • setMaxWriteBufferNumber: 设置同时存在的 MemTable 数量,以处理并发写入。
  • setMinWriteBufferNumberToMerge: 设置合并到磁盘前需要的最小 MemTable 数量。
    Table Format 配置:
  • BlockBasedTableConfig: 配置 SSTable 文件的 Block 大小,以优化读取性能。

3.1.2. Compaction 策略

Level Compaction
  • setLevelCompactionDynamicLevelBytes: 动态调整每层的大小,以平衡 Compaction 频率和读写放大。
  • setMaxBytesForLevelBase 和 setMaxBytesForLevelMultiplier: 设置各级别的目标大小和倍数,控制 Compaction 触发条件。
  • setCompactionPri: 设置 Compaction 优先级,如 BySize、ByCompensatedSize 等。
Universal Compaction
  • setCompactionStyle: 设置为 CompactionStyle.UNIVERSAL,适用于写密集型应用。
  • setUniversalCompactionOptions: 配置 Universal Compaction 的相关参数,如合并策略、阈值等。

3.1.3. WAL 配置

  • setWalDir: 设置 WAL 日志的存储路径。
  • setWalTtlSeconds 和 setWalSizeLimitMB: 控制 WAL 日志的保留时间和大小,以平衡数据安全和磁盘使用。

3.1.4. Block Cache

  • LRUCache: 创建和配置 Block Cache,用于缓存 SSTable 中的数据块,提高读性能。
  • setBlockSize: 设置 Block 大小,影响 Block Cache 的利用率。
  • setPinL0FilterAndIndexBlocksInCache: 是否将 Level 0 的过滤器和索引块保留在 Block Cache 中。

3.1.5. 数据压缩

  • setCompression: 设置数据压缩算法,如 NoCompression、Snappy、Zlib、LZ4 或 Zstd,以节省磁盘空间。
  • setCompressionPerLevel: 针对不同级别设置不同的压缩算法。

3.1.6. 并发控制

  • setMaxBackgroundJobs: 设置后台任务的最大并发数,包括 Compaction 和刷写操作。
  • setMaxBackgroundCompactions: 设置最大 Compaction 线程数。
  • setMaxBackgroundFlushes: 设置最大刷写线程数。

3.1.7. 其他优化

  • useFsync: 是否在写入时使用 fsync,以确保数据的持久性。
  • setTableFormatConfig: 配置 SSTable 的其他属性,如过滤器类型、索引间隔等。

3.1.8. 示例代码

import org.rocksdb.*;

public class RocksDBConfig {

    static {
        RocksDB.loadLibrary();
    }

    public RocksDB createRocksDB() throws RocksDBException {
        Options options = new Options()
                // 内存管理
                .setWriteBufferSize(128 * 1024 * 1024) // MemTable 大小,可以根据写入速率调整
                .setMaxWriteBufferNumber(4) // 最大 MemTable 数量,用于并发写入
                .setMinWriteBufferNumberToMerge(2) // 合并 MemTable 的最小数量
                .setTargetFileSizeBase(256 * 1024 * 1024) // 每个 SSTable 文件的目标大小,根据磁盘 I/O 能力调整
                .setMaxBytesForLevelBase(1024 * 1024 * 1024) // Level 1 的目标大小,根据磁盘空间调整
                .setMaxBytesForLevelMultiplier(10) // 每层的目标大小是前一层的倍数,控制 Compaction 触发条件
                .setLevelCompactionDynamicLevelBytes(true) // 动态调整每层大小
                .setCompactionPri(CompactionPriority.ByCompensatedSize) // 根据补偿大小优先级排序

                // Compaction 策略
                .setMaxBackgroundCompactions(4) // 最大后台 Compaction 线程数,根据 CPU 核心数调整
                .setMaxBackgroundFlushes(2) // 最大后台刷写线程数,根据 CPU 核心数调整

                // WAL 配置
                .setWalDir("/path/to/wal") // WAL 日志路径
                .setWalTtlSeconds(60 * 60 * 24) // WAL 日志保留时间,根据数据安全性需求调整
                .setWalSizeLimitMB(2048) // WAL 日志大小限制,根据磁盘空间调整
                .setUseFsync(false) // 是否在写入时使用 fsync,平衡性能和数据安全

                // Block Cache
                .setBlockBasedTableConfig(new BlockBasedTableConfig()
                        .setBlockSize(16 * 1024) // Block 大小,根据数据特性调整
                        .setPinL0FilterAndIndexBlocksInCache(true) // Pin Level 0 的过滤器和索引块
                        .setCacheIndexAndFilterBlocks(true) // 缓存索引和过滤器块,提高读性能
                        .setBlockCache(new LRUCache(512 * 1024 * 1024))) // Block Cache 大小,根据内存资源调整

                // 数据压缩
                .setCompression(CompressionType.LZ4Compression) // 使用 LZ4 压缩算法,也可以选择其他算法
                .setCompressionPerLevel(Arrays.asList(
                        CompressionType.LZ4Compression, // Level 0
                        CompressionType.LZ4Compression, // Level 1
                        CompressionType.ZSTD, // Higher levels
                        CompressionType.ZSTD,
                        CompressionType.ZSTD)) // 自定义不同级别的压缩算法

                // 并发控制
                .setMaxTotalWalSize(2 * 1024 * 1024 * 1024) // WAL 文件总大小限制
                .setMaxOpenFiles(-1) // 允许打开的最大文件数,-1 表示无限制
                .setMaxSequentialSkipIn_iterations(8) // 优化迭代器性能

                // 其他优化
                .setTableFormatConfig(new BlockBasedTableConfig().setFilterPolicy(new BloomFilter(10, false))); // 使用布隆过滤器减少读操作

        String dbPath = "/path/to/rocksdb";
        return RocksDB.open(options, dbPath);
    }
}

3.2. MemTable、Block Cache

功能
  • MemTable:数据首先被写入 MemTable,然后再根据一定的策略刷写到磁盘生成 SSTable 文件。主要用于存储新写入的数据,临时存储在内存中并定期刷写到磁盘。优化写性能。
  • Block Cache:主要用于缓存从磁盘读取的数据块,减少后续读取相同数据块的磁盘 I/O。优化读性能。
操作路径
  • MemTable:写操作首先写入 MemTable,读操作首先检查 MemTable。
  • Block Cache:读操作在MemTable之后查不到之后会读 Block Cache,如果命中则返回缓存数据;否则从磁盘读取并缓存。
持久性
  • MemTable:数据最终会被持久化到磁盘生成 SSTable 文件。
  • Block Cache:缓存的数据块不具备持久性,缓存失效或系统重启后需要重新加载。
配置
  • MemTable:通过 setWriteBufferSize 和 setMaxWriteBufferNumber 配置大小和数量。
  • Block Cache:通过 LRUCache 配置大小。
配置建议

MemTable:

  • 在写密集型场景中,适当增加 MemTable 大小和数量,可以提高写入性能和减少刷写频率。
  • 在内存有限的情况下,配置适当的 MemTable 大小,确保内存使用的合理性,避免内存不足。

Block Cache:

  • 在读密集型场景中,适当增加 Block Cache 大小,可以提高读缓存命中率和读性能。
  • 在内存有限的情况下,配置适当的 Block Cache 大小,确保内存使用的合理性,避免内存不足。

3.3. 读顺序

在 RocksDB 中,读操作的流程涉及多个步骤,主要目的是尽可能快速地找到所需的数据。具体步骤如下:

1.查询 MemTable

首先,RocksDB 会在 MemTable 中查找数据。MemTable 存储的是最近的写操作数据,由于在内存中查询速度非常快,所以这是第一步。
如果在 MemTable 中找不到数据,RocksDB 接着会在 Immutable MemTable 中查找。Immutable MemTable 是已经写满并准备刷到磁盘但尚未完成的 MemTable。

2.查询 Block Cache

如果在 MemTable 和 Immutable MemTable 中都没有找到数据,RocksDB 会查找 Block Cache。Block Cache 存储的是从 SSTable 文件中读取的数据块(block),用于加速读操作。
Block Cache 是基于 LRU(Least Recently Used)策略管理的,旨在保留最近使用的数据块,以提高缓存命中率。

3.查询 SSTable 文件

如果数据不在 Block Cache 中,RocksDB 会从磁盘上的 SSTable 文件中查找。首先会查找每层的索引和过滤器(如布隆过滤器)来确定数据可能存在的文件和位置。
从 SSTable 文件中读取数据块时,RocksDB 会将这些数据块加载到 Block Cache 中,以便后续访问更快。

4.合并数据

如果在多个位置找到数据版本(例如在 MemTable 和 SSTable 中同时存在),RocksDB 会根据数据的序列号(sequence number)和操作类型(如 PUT 或 DELETE)来确定最终返回的数据。

4. spring使用

4.1. 配置类

RocksDB 是一个嵌入式数据库,这意味着它不需要单独的服务器进程。一般情况下,你只需要指定数据存储目录和一些配置选项。

pom
<dependency>
    <groupId>org.rocksdb</groupId>
    <artifactId>rocksdbjni</artifactId>
    <version>6.20.3</version> <!-- 请根据最新版本进行更新 -->
</dependency>

调优选项解释

MemTable 配置
  • setWriteBufferSize: 设置每个 MemTable 的大小。例如,这里设置为 64MB。
  • setMaxWriteBufferNumber: 设置同时存在的最大 MemTable 数量,这里设置为 3。
  • setMinWriteBufferNumberToMerge: 设置合并 MemTable 的最小数量。
Block Cache 配置
  • 使用 LRUCache 进行 Block Cache 的配置,这里设置为 128MB。
Compaction 策略
  • setLevelCompactionDynamicLevelBytes: 启用动态调整每层大小的 Compaction 策略。
  • setMaxBackgroundCompactions: 设置最大后台 Compaction 线程数。
  • setMaxBackgroundFlushes: 设置最大后台刷写线程数。
WAL 配置
  • setWalDir: 设置 WAL 文件的存储路径,以确保数据的持久性。

通过这些配置,您可以根据业务需求和系统资源情况,合理调整 RocksDB 的参数,提升其在 Spring 项目中的性能。

import org.rocksdb.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RocksDBConfig {

    static {
        RocksDB.loadLibrary();
    }

    @Bean
    public RocksDB rocksDB() throws RocksDBException {
        // 配置 RocksDB 选项
        Options options = new Options();
        options.setCreateIfMissing(true);

        // 配置 MemTable
        options.setWriteBufferSize(64 * 1024 * 1024); // 64MB
        options.setMaxWriteBufferNumber(3); // 3个 MemTable
        options.setMinWriteBufferNumberToMerge(1); // 最少1个 MemTable 合并

        // 配置 Block Cache
        Cache blockCache = new LRUCache(128 * 1024 * 1024); // 128MB Block Cache
        BlockBasedTableConfig tableConfig = new BlockBasedTableConfig();
        tableConfig.setBlockCache(blockCache);
        options.setTableFormatConfig(tableConfig);

        // 配置 Compaction 策略
        options.setLevelCompactionDynamicLevelBytes(true); // 动态调整每层大小
        options.setMaxBackgroundCompactions(4); // 最大后台 Compaction 线程数
        options.setMaxBackgroundFlushes(2); // 最大后台刷写线程数

        // 配置 WAL
        options.setWalDir("/path/to/wal"); // 指定 WAL 目录

        // 指定数据存储目录
        String dbPath = "/path/to/rocksdb";

        // 打开 RocksDB 数据库
        return RocksDB.open(options, dbPath);
    }
}

4.1. 操作类

你可以通过注入 RocksDB bean 来在 Spring 中使用 RocksDB 进行数据操作。以下是一个简单的示例,展示了如何进行基本的增删改查操作:

import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RocksDBService {

    private final RocksDB rocksDB;

    @Autowired
    public RocksDBService(RocksDB rocksDB) {
        this.rocksDB = rocksDB;
    }

    // 存储数据
    public void put(String key, String value) {
        try {
            rocksDB.put(key.getBytes(), value.getBytes());
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }

    // 获取数据
    public String get(String key) {
        try {
            byte[] value = rocksDB.get(key.getBytes());
            return value != null ? new String(value) : null;
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 删除数据
    public void delete(String key) {
        try {
            rocksDB.delete(key.getBytes());
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }
}

KerryWu
641 声望159 粉丝

保持饥饿


« 上一篇
Etcd常用场景
下一篇 »
学会使用BitSet

引用和评论

0 条评论