文章目录
  1. 1. RocksDB是什么?
  2. 2. 在Java工程中使用RocksDB
    1. 2.1. 初始化RocksDB
    2. 2.2. RocksDB读写
    3. 2.3. rocksDB的write操作
  3. 3. 解释一下rocksdb的flush操作
  4. 4. rocksDB的WAL(write ahead log)
  5. 5. 小结

项目中需要实现高效的IO操作,不仅支持查询、写入数据,还需要实现数据的持久化。选型最终选择了RocksDB,那么本文就来一睹RocksDB的芳容。

RocksDB是什么?

RocksDB是Facebook开发的一款高效的数据库软件,是采用C++编写而成的。

RocksDB是一款key-value型数据存储设施,具备四个特点,其具有四大特点。

高性能:RocksDB使用一套C++编写而成的高性能日志结构的数据库引擎。 它的Key和value支持任意大小的字节流。

可适配性:RocksDB适合于多种不同工作场景。从像MyRocks这样的数据存储引擎到应用数据缓存, 甚至是嵌入式工作场景,RocksDB都可以从容面对这些不同的数据工作需求。

为快速存储而优化:RocksDB为快速而又低延迟的存储设备(例如闪存或者高速硬盘)进行了特殊优化处理,将最大限度发挥闪存和RAM的高度率读写性能。

基础和高级的数据库操作:RocksDB提供了一些基础的操作,例如打开和关闭数据库。它对于合并、压缩、过滤等高级操作,也提供了支持。

如果对RocksDB感兴趣,可以去读它的源码,地址为:https://github.com/facebook/rocksdb

在Java工程中使用RocksDB

如何在Java工程中使用RocksDB呢?

首先建立一个maven工程,在pom.xml中引入RocksDB依赖:

<!-- https://mvnrepository.com/artifact/org.rocksdb/rocksdbjni -->
<dependency>
    <groupId>org.rocksdb</groupId>
    <artifactId>rocksdbjni</artifactId>
    <version>6.20.3</version>
</dependency>

接着我们通过一个demo尝试在Java中使用RocksDB。

初始化RocksDB

private RocksDB rocksDB;

private String path = "D:/rocksdb";

public RocksDBDemo() {
    RocksDB.loadLibrary();
    Options options = new Options();
    options.setCreateIfMissing(true);
    try {
        rocksDB = RocksDB.open(options, path);
    } catch (RocksDBException e) {
        e.printStackTrace();
    }
}

public RocksDB rocksDB() {
    return this.rocksDB;
}

首先指定一个路径用于创建RocksDB的log文件:rocksdb写入时,直接以append方式写到log文件以及memtable,随即返回,因此非常快速。

接着通过RocksDB.loadLibrary();导入库,然后设置Options,并打开rocksDB,此时成员变量rocksDB就可以拿来进行操作了。

RocksDB读写

RocksDBDemo rocksDBDemo = new RocksDBDemo();
RocksDB rocksDB = rocksDBDemo.rocksDB();
// 写入
rocksDB.put("name".getBytes(), "snowalker".getBytes());
// 读取
byte[] bytes = rocksDB.get(("name".getBytes()));
System.out.println("读取结果:" + new String(bytes));

// 遍历
RocksIterator iter = rocksDB.newIterator();
for (iter.seekToFirst();iter.isValid();iter.next()) {
    System.out.println("iter key: " + new String(iter.key()) + ",iter value: " +
            new String(iter.value()));
}

RocksDBDemo是我们的测试类名,首先获取rocksDB引用,然后调用put,get方法进行读写操作。

其中put方法签名为:

public void put(final byte[] key, final byte[] value)
    throws RocksDBException {
    put(nativeHandle_, key, 0, key.length, value, 0, value.length);
}

get方法签名为:

public byte[] get(final byte[] key) throws RocksDBException {
    return get(nativeHandle_, key, 0, key.length);
}

通过rocksDB.newIterator()可以获取迭代器,借助迭代器能够执行迭代操作,这里是读取了一下key与value。

rocksDB的write操作

rocksdb的一个WriteBatch是原子操作,要么全部成功,要么全部失败,

具体的实现原理是在整个log的写的过程中只会调用Write操作,最后会调用一次flush,所以如果中间发生机器crash,所有的都会失败,否则所有的都会成功。

看一段实际代码:

// write batch test
try (final WriteOptions writeOpt = new WriteOptions()) {
    for (int i = 0; i <= 10; ++i) {
        try (final WriteBatch batch = new WriteBatch()) {
            for (int j = 0; j <= 10; ++j) {
                batch.put(String.format("%d * %d%s", i, j, "--batch").getBytes(),
                        String.format("%d", i * j).getBytes());
            }
            rocksDB.write(writeOpt, batch);
        }
    }
}

这里实际上将乘法表写入了rocksDB。

运行代码,观察目录D:/rocksdb中出现了以下文件:

1.PNG

简单对这几种文件进行讲解:

我们可以从后缀看出:主要有这几种类型

  • sst文件
  • CURRENT文件
  • manifest文件
  • log文件
  • LOG文件和LOCK文件

其中

  1. sst文件存储的是落地的数据
  2. CURRENT文件存储的是当前最新的是哪个manifest文件
  3. manifest文件存储的是Version的变化
  4. log文件是rocksdb的write ahead log,就是在写db之前写的数据日志文件,类似binlog
  5. LOG文件是一些日志信息,是供调试用的
  6. LOCK是打开db锁,只允许同时有一个进程打开db

这里我们重点看一下log文件中的内容,由于写入的byte,可能有乱码。

2.PNG

从图中可以看到,实际上是顺序写入了我们在代码中设置的key-value。

解释一下rocksdb的flush操作

Flush是指将memtable的数据导入到sst中,变成持久化存储,从而不必担心数据丢失了。

1.首先在memtable的add的时候,
会检测是否memtable的大小达到了max write buffer,
如果是就将should_flush_置为true,
并会在WriteBatch的Handler里面调用CheckMemtableFull,
将当前column family加入flush_scheduler。

2.在Write的时候,调用ScheduleFlushes,
将需要flush的column family的memtable切换一个新的,
同时将原来的memtable加入cfd的imm中,
如果这个column family data的imm数量大于min_write_buffer_number_to_merge,
并启动一个新的线程调用BGWorkFlush

由于真正的Flush过程是在另一个线程完成的,所以这个地方并不会block写过程

rocksDB的WAL(write ahead log)

rocksdb的write ahead log(WAL)是指:
每次写操作,rocksdb会先写write ahead log,然后才会写db
write ahead log可以配置到单独的空间,并且可以配置WAL文件的单独的删除机制。

这种原因是为了保存WAL文件,达到特殊的目的,比如,其他sst文件放在不可靠存储里面,而WAL放到可靠存储里面。

对RocksDB 的写操作而言,每次都必写到两个地方:

  1. 基于内存的数据结构memtable(达到quota 后会flush 至SST file)。
  2. 预写日志-Write Ahead Log(WAL)。
    如果出现异常情况,WAL 可以用来完整恢复memtable 中的数据,恢复db 的原有的状态。

默认配置下,RocksDB 通过每次用户写之后flush WAL,来保证进程crash 后的一致性。

小结

本文我们主要对rocksDB做了一个了解和学习,一般来说,使用它的目的在于高性能的写入,实现数据的快速持久化。

业界不乏优秀的中间件底层依赖了rocksDB,如TiDB。

###



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

文章目录
  1. 1. RocksDB是什么?
  2. 2. 在Java工程中使用RocksDB
    1. 2.1. 初始化RocksDB
    2. 2.2. RocksDB读写
    3. 2.3. rocksDB的write操作
  3. 3. 解释一下rocksdb的flush操作
  4. 4. rocksDB的WAL(write ahead log)
  5. 5. 小结
Fork me on GitHub