什么是 RocksDB(以及它在流媒体中的作用)?

2025-06-07

什么是 RocksDB(以及它在流媒体中的作用)?

图片描述

RocksDB 是一款高性能数据库,是科技行业中一颗隐藏的瑰宝,却常常被开发者忽视。我迫不及待地想深入研究这款强大工具的复杂性和应用。谁知道呢?它或许会成为你下一个项目的基石。

介绍

RocksDB 源自 Facebook,是一款强大的存储引擎,专为跨多种存储介质的服务器工作负载而设计,最初侧重于高速存储,尤其是闪存存储。它是一个 C++ 库,可将键和值存储为任意大小的字节流。它支持点查找和范围扫描,并提供各种 ACID 保证。

RocksDB 在可定制性和自适应性之间取得了完美的平衡。它拥有高度可调的配置设置,可以进行微调以适应各种生产环境,包括 SSD、硬盘、ramfs 或远程存储。它支持多种压缩算法,并提供强大的生产支持和调试工具。同时,我们努力限制可调参数的数量,以确保令人满意的开箱即用性能,并在适当的情况下实现自适应算法。

RocksDB 融合了开源 leveldb 项目的大量代码以及 Apache HBase 的理念。其初始代码库是从开源 leveldb 分支而来。此外,它还基于 Facebook 在 RocksDB 诞生之前开发的代码和概念。

概述

RocksDB 的基本设计原则是针对快速存储和服务器工作负载进行性能优化。它旨在促进高效的点查找和范围扫描。其可配置性使其能够支持高随机读取工作负载、高更新工作负载或两者的混合。RocksDB 的架构设计使其能够轻松调整权衡,以适应不同的工作负载和硬件配置。

RocksDB 是一个存储引擎库,提供键值存储接口,其中键和值可以表示为任意字节流。它将所有数据按排序顺序排列,典型的操作包括Get(key)NewIterator()Put(key, val)Delete(key)SingleDelete(key)

RocksDB 本身并不支持 SQL。虽然它没有关系数据模型,也不支持 SQL 查询,但它可以与其他提供类似 SQL 查询功能的系统或框架结合使用。例如,MyRocks 将 RocksDB 与 MySQL 结合使用。然而,需要注意的是,RocksDB 本身并不原生支持 SQL。它也不直接支持二级索引,但用户可以使用列族在内部或外部构建自己的二级索引。

主要特点有:

  • 专为想要在本地或远程存储系统上存储多达几 TB 数据的应用服务器而设计。
  • 针对在快速存储(闪存设备或内存)上存储中小型键值进行了优化
  • 它在多核处理器上运行良好

对于读写工作负载,RocksDB 应该由单个进程打开。RocksDB 能够支持多进程只读操作,而无需对数据库进行任何写入操作。这可以通过调用 DB::OpenForReadOnly() 方法打开数据库来实现。

RocksDB 最初是一个开源项目,采用 BSD 三条款许可证发布。然而,2017 年 7 月,RocksDB 的许可证更改为双重许可证模式,同时包含 Apache 2.0 和 GPLv2。这一变化意义重大,因为它使 RocksDB 能够集成到 Apache 软件基金会旗下的项目中,而该基金会此前已将 BSD+Patents 许可证条款列入黑名单。双重许可证信息可在 GitHub 上 RocksDB 代码库的根目录中找到。如需了解最新更新,您可以访问 RocksDB 官方 GitHub 代码库。

应用

RocksDB 广泛应用于 Apache Flink 等流处理框架,作为快速高效的状态存储来维护流式应用程序的状态。由于其高效的内存利用率和快速的读写操作,RocksDB 也被用于 Web 应用程序中的缓存和会话存储。此外,RocksDB 还可作为 TiKV 等需要高性能和高持久性的数据库系统的存储引擎,并因其较小的内存占用和轻量级设计而广泛应用于物联网设备或边缘计算等嵌入式系统。

什么是流媒体

流式处理和批处理是两种不同的数据处理方法,每种方法都有各自的用例和优势。

流式处理是一种数据处理方法,其中每个数据项在到达后都会被单独实时处理。它就像一条水流,数据持续流动。当您需要处理实时数据(例如实时分析、实时监控系统或实时推荐)时,此方法尤其有用。流式处理的优势在于它能够提供实时或近乎实时的洞察。然而,它的实现可能更为复杂,并且需要强大的基础设施来处理持续的数据流。

另一方面,批处理是一种在一段时间内收集数据并一次性处理的方法。这就像往桶里注满水,然后一次性处理所有水。这种方法通常用于不需要实时处理数据的情况,例如日报、历史数据分析或大型 ETL(提取、转换、加载)作业。批处理的优势在于它更容易实现,并且更具成本效益,因为它可以优化资源。然而,它无法提供实时洞察,而且速度可能较慢,因为您需要等待批处理完成才能处理数据。

总而言之,流式处理和批处理之间的选择取决于您的具体用例和需求。如果您需要实时洞察,那么流式处理是最佳选择。如果您要处理大量数据且不需要实时处理,那么批处理可能是更高效的选择。作为一名数据工程师,了解这些差异对于根据您的数据处理需求做出最佳决策至关重要。

图片描述

流式传输. Apache Flink

处理流的应用程序通常具有状态特性,这意味着它们会保留已处理事件的信息,以影响未来事件的处理。在 Flink 中,这些保留的信息(或状态)存储在本地已配置的状态后端中。为了防止故障期间数据丢失,状态后端会定期拍摄其内容的快照,并将其存储在预先配置的持久存储中。
在 Flink 的三个内置状态后端中,RocksDB 状态后端(也称为 RocksDBStateBackend)是其中之一。

图片描述

org/apache/flink/contrib/streaming/state您可以在包中找到使用示例( https://github.com/apache/flink/tree/9fe8d7bf870987bf43bad63078e2590a38e4faf6/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state)。

流媒体。Kafka Streams 和 KSQL

Kafka Streams 和 KSQL 使用 RocksDB 作为其默认的有状态操作存储引擎。在 KSQL 中,RocksDB 用于将物化视图本地存储在其磁盘上。RocksDB 是一个嵌入式键/值存储,在每个 KSQL 服务器中以进程形式运行。您无需启动、管理或与其交互。这使得 KSQL 能够高效可靠地执行有状态操作。

您可以在 RocksDBStore 类( https://github.com/a0x8o/kafka/blob/master/streams/src/main/java/org/apache/kafka/streams/state/internals/RocksDBStore.java )中找到使用示例

蟑螂数据库

CockroachDB 是由 Cockroach Labs 开发的分布式 SQL 数据库系统。它旨在实现高弹性和一致性,正如其名称“蟑螂”一样。该系统以其可扩展性、高可用性和多功能性而闻名。它可以通过添加更多节点来处理不断增加的负载,确保数据始终可访问,并且可以在各种环境中运行。

CockroachDB 的关键特性之一是它与 PostgreSQL 兼容。它支持 PostgreSQL 的有线协议和大部分 PostgreSQL 语法。这意味着基于 PostgreSQL 构建的现有应用程序通常可以迁移到 CockroachDB,而无需更改应用程序代码。然而,需要注意的是,CockroachDB 不支持某些 PostgreSQL 功能,或者其行为方式与 PostgreSQL 不同,因为并非所有功能都能在分布式系统中轻松实现。

图片描述

CockroachDB 使用 RocksDB 作为其底层存储引擎。CockroachDB 的架构分为两层:SQL 层和存储层。SQL 层位于事务性和强一致性的分布式键值存储之上。

在这个键值存储中,键值范围被划分并存储在 RocksDB 中。这使得 CockroachDB 能够充分利用 RocksDB 高效的存储机制,同时提供 SQL 接口的优势。存储在 RocksDB 中的数据会在整个集群中复制,从而增强系统的弹性。这种设计选择使 CockroachDB 能够处理大规模分布式存储,同时保持高性能和强一致性。

您可以在 CockroachDB 的博客https://www.cockroachlabs.com/blog/cockroachdb-on-rocksd/上找到有关 RocksDB 在 CockroachDB 中的使用的详细信息

TiKV

TiKV,全称“Ti Key-Value”,是一个开源、分布式、事务型键值数据库。它旨在处理海量数据,同时确保强一致性和可靠性。与传统的 NoSQL 系统不同,TiKV 既提供经典的键值 API,也提供符合 ACID 要求的事务 API。

TiKV 以 Rust 语言构建,并基于 Raft 共识算法,最初由 PingCAP 创建,旨在补充 TiDB——一个兼容 MySQL 协议的分布式 HTAP(混合事务和分析处理)数据库。TiKV 的设计灵感源自 Google 的一些优秀分布式系统,例如 BigTable、Spanner 和 Percolator,以及近年来学术界的一些最新成果,例如 Raft 共识算法。

图片描述

TiKV 使用 RocksDB 作为其主要存储层。这种设计选择使 TiKV 能够处理大规模分布式存储,同时保持高性能和强一致性。

在 TiKV 的架构中,数据以键值对的形式存储。键被划分为不同的范围,每个范围的键都存储在 RocksDB 中。这使得 TiKV 能够充分利用 RocksDB 高效的存储机制。

TiKV 也使用 Raft 一致性算法进行数据复制。对于每个写请求,TiKV 首先将请求写入 Raft 日志。日志提交后,TiKV 应用 Raft 日志并将数据写入 RocksDB。这确保了集群中多个副本之间的数据一致性。

此外,TiKV 还使用了 RocksDB 的一项名为前缀布隆过滤器 (PBF) 的功能。PBF 可以过滤掉那些不可能包含与给定行键相同前缀的键的数据。这在 TiKV 的多版本并发控制 (MVCC) 模型中尤为有用,因为同一行的多个版本共享相同的前缀。此功能可以提高读取操作的效率。

综上所述,RocksDB 在 TiKV 提供可靠、高性能的分布式存储解决方案中起着至关重要的作用。

Apache Kvrocks

图片描述

Apache Kvrocks 是一个分布式键值 NoSQL 数据库,它使用 RocksDB 作为其存储引擎,并设计为与 Redis 协议兼容。

Kvrocks 具有以下主要特点:

  • 兼容 Redis:用户可以通过任何 Redis 客户端访问 Apache Kvrocks。
  • 命名空间:类似于 Redis SELECT,但每个命名空间都配备有令牌。
  • 复制:像 MySQL 一样使用 binlog 进行异步复制。
  • 高可用性:支持 Redis sentinel 在主服务器或从服务器发生故障时进行故障转移。
  • 集群:集中管理,但可通过任何 Redis 集群客户端访问。

您可以在其官方文档中找到详细信息https://kvrocks.apache.org/community/data-structure-on-rocksdb/

在应用程序中使用

RocksDB 是一个嵌入式数据库,这意味着它旨在直接集成到您的应用程序中。与独立数据库系统不同,像 RocksDB 这样的嵌入式数据库作为应用程序的组成部分运行,允许在应用程序内部高效地管理和存储数据。由于数据库操作与应用程序的功能紧密相关,这可以提高性能并简化数据处理。然而,这也意味着应用程序负责直接管理数据库。

C++

在 C++ 中,您可以使用以下方式操作数据库:

// Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#include <cstdio>
#include <string>

#include "rocksdb/db.h"
#include "rocksdb/options.h"
#include "rocksdb/slice.h"

using ROCKSDB_NAMESPACE::DB;
using ROCKSDB_NAMESPACE::Options;
using ROCKSDB_NAMESPACE::PinnableSlice;
using ROCKSDB_NAMESPACE::ReadOptions;
using ROCKSDB_NAMESPACE::Status;
using ROCKSDB_NAMESPACE::WriteBatch;
using ROCKSDB_NAMESPACE::WriteOptions;

#if defined(OS_WIN)
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_simple_example";
#else
std::string kDBPath = "/tmp/rocksdb_simple_example";
#endif

int main() {
  DB* db;
  Options options;
  // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  options.IncreaseParallelism();
  options.OptimizeLevelStyleCompaction();
  // create the DB if it's not already present
  options.create_if_missing = true;

  // open DB
  Status s = DB::Open(options, kDBPath, &db);
  assert(s.ok());

  // Put key-value
  s = db->Put(WriteOptions(), "key1", "value");
  assert(s.ok());
  std::string value;
  // get value
  s = db->Get(ReadOptions(), "key1", &value);
  assert(s.ok());
  assert(value == "value");

  // atomically apply a set of updates
  {
    WriteBatch batch;
    batch.Delete("key1");
    batch.Put("key2", value);
    s = db->Write(WriteOptions(), &batch);
  }

  s = db->Get(ReadOptions(), "key1", &value);
  assert(s.IsNotFound());

  db->Get(ReadOptions(), "key2", &value);
  assert(value == "value");

  {
    PinnableSlice pinnable_val;
    db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);
    assert(pinnable_val == "value");
  }

  {
    std::string string_val;
    // If it cannot pin the value, it copies the value to its internal buffer.
    // The intenral buffer could be set during construction.
    PinnableSlice pinnable_val(&string_val);
    db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);
    assert(pinnable_val == "value");
    // If the value is not pinned, the internal buffer must have the value.
    assert(pinnable_val.IsPinned() || string_val == "value");
  }

  PinnableSlice pinnable_val;
  s = db->Get(ReadOptions(), db->DefaultColumnFamily(), "key1", &pinnable_val);
  assert(s.IsNotFound());
  // Reset PinnableSlice after each use and before each reuse
  pinnable_val.Reset();
  db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);
  assert(pinnable_val == "value");
  pinnable_val.Reset();
  // The Slice pointed by pinnable_val is not valid after this point

  delete db;

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

您可以在 github 的官方 wiki 中找到详细信息https://github.com/facebook/rocksdb/wiki/Basic-Operations

Java

RocksJava 是一项旨在为 RocksDB 开发高性能且用户友好的 Java 驱动程序的计划。

<dependency>
  <groupId>org.rocksdb</groupId>
  <artifactId>rocksdbjni</artifactId>
  <version>6.6.4</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

您可以使用以下方式打开数据库

import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import org.rocksdb.Options;
...
  // a static method that loads the RocksDB C++ library.
  RocksDB.loadLibrary();

  // the Options class contains a set of configurable DB options
  // that determines the behaviour of the database.
  try (final Options options = new Options().setCreateIfMissing(true)) {

    // a factory method that returns a RocksDB instance
    try (final RocksDB db = RocksDB.open(options, "path/to/db")) {

        // do something
    }
  } catch (RocksDBException e) {
    // do some error handling
    ...
  }
...
Enter fullscreen mode Exit fullscreen mode

并做一些读写操作:

byte[] key1;
byte[] key2;
// some initialization for key1 and key2

try {
  final byte[] value = db.get(key1);
  if (value != null) {  // value == null if key1 does not exist in db.
    db.put(key2, value);
  }
  db.delete(key1);
} catch (RocksDBException e) {
  // error handling
}
Enter fullscreen mode Exit fullscreen mode

您可以在官方文档中找到详细信息https://github.com/facebook/rocksdb/wiki/RocksJava-Basics

结论

总而言之,RocksDB 凭借其高性能和高效的键值存储,是管理磁盘持久状态的绝佳解决方案。它尤其适合需要快速、低延迟访问磁盘数据的应用程序。但是,如果您的需求扩展到更大规模的分布式状态管理或约束管理,那么像 MySQL 这样更抽象的数据库可能更适合。MySQL 是一个关系数据库,它提供事务支持、高级查询功能以及处理复杂数据关系的稳健性等特性,使其更适合这些更复杂的场景。因此,在 RocksDB 和 MySQL(或任何其他数据库)之间进行选择,应该取决于您应用程序的具体需求。


作者是 Mark Andreev,SWE@Conundrum.AI

文章来源:https://dev.to/mrkandreev/what-is-rocksdb-and-its-role-in-streaming-3bla
PREV
教你如何将网站性能提升 21%!AWS GenAI 上线啦!
NEXT
MERN 堆栈 TODO 应用程序 [后端]