你好,RocksDB

2022-04-15 00:00:00 数据 操作 文件 的是 写入

项目中需要实现高效的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依赖:

    <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中出现了以下文件:

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

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

  • 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,可能有乱码。

从图中可以看到,实际上是顺序写入了我们在代码中设置的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。刚兴趣的朋友可以去TiDB官网阅读相关内容。

来源 https://www.modb.pro/db/58495

相关文章