RocksJava 基础

RocksJava是一个为RocksDB构建高性能但易于使用的Java驱动程序项目。

RocksJava有3层结构: 1、组织中的Java类,组成RocksJava API的rocksdb包,Java用户只直接与该层交互。 2、用C++编写的JNI代码,它提供了 Java API和RocksDB之间的链接。 3、RocksDB本身是用C++编写的,并编译成JNI层使用的本机库。

(我们努力使用RocksJava API与RocksDB的C++ API保持同步,但它经常落后。我们高度鼓励社区的贡献… 因此,如果您发现自己需要某个API,而该API是C++的,但还不是Java的,请随时向我们发送Pull Request.)

在本页中,您将学习RocksDB Java APIs的基础知识

开始

您可以使用我们发布的预构建Maven构件,也可以自己从其源代码构建RocksJava

Maven

我们还将 RocksJava构件发布到Maven Central,如果您只是想依赖于jar而不是自己构建它,您可以这样做: https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.rocksdb%22.

我们发布了一个类Jar (rocksdbjni-X.X.X.jar) ,它包含所有支持平台的本机库和Java类文件,以及更小的特定于平台的Jar(如rocksdbjni-X.X.X-linux64.jar)

从支持Maven样式依赖关系的构建系统中使用RocksJava的最简单方法是在RocksJava上添加一个依赖关系。

例如,如果您使用Maven:

  1. <dependency>
  2. <groupId>org.rocksdb</groupId>
  3. <artifactId>rocksdbjni</artifactId>
  4. <version>5.5.1</version>
  5. </dependency>

注意 微软Windows用户: 如果您在微软Windows上使用Maven中央编译构件,那么它们是使用Microsoft Visual Studio 2015编译的, 如果您没有安装“Microsoft Visual c++ 2015 Redistributable”,那么您需要从https://www.microsoft.com/en-us/download/details.aspx?id=48145,或者从源代码构建自己的二进制文件。

从源代码编译

要构建RocksJava,首先需要设置JAVA_HOME环境变量,以指向安装Java SDK的位置(必须是JDK 1.7),您还必须具备平台编译RocksDB本机库的先决条件,请参见INSTALL.md。 一旦正确设置了JAVA_HOME,并且安装了先决条件,只需运行make rocksdbjava就可以为RocksDB构建Java绑定:

  1. $ make -j8 rocksdbjava

这将生成rocksdbjni.jar 和 librocksdbjni.so(或 librocksdbjni.jnilib 在MacOS),在rocksdb根目录下的java/target目录中。 具体的说,,rocksdbjni.jar 包含为RocksDB定义Java API的Java类,而librocksdbjni则包含定义Java API的Java类。因此包括C++ rocksdb库和在rocksdbjni.jar中定义的Java类的本机实现。 运行单元测试:

  1. $ make jtest

[只在Facebook内部]在Facebook开发服务器上:

  1. $ ROCKSDB_NO_FBCODE=1 make jtest

清理:

  1. $ make jclean

例子:

  1. 如果您想直接跳到代码中,我们在这里提供了一些示例。(https://github.com/facebook/rocksdb/tree/master/java/samples/src/main/java)

内存管理

  1. RocksJava API中使用的许多Java对象将由Java对象拥有的所有权的C++对象支持。由于C++不像Java那样自动垃圾回收,所以我们必须显式的释放C++对象使用的内存。
  2. 管理C++对象的RocksJava中的任何Java对象都讲继承自org.rocksdb.AbstractNativeReference是设计用来帮助管理和清理所有C++对象的。为此使用了两种机制:
  3. (1) AbstractNativeReference#close()
  4. 当用户使用完RocksJava对象后,应该显式地调用此方法。如果分配了C++对象,但还没有释放,则在第一次调用此方法时将释放这些对象。
  5. 为了方便使用,这个方法覆盖了java.lang.AutoCloseable#close(), 这使得它可以与ARM(自动资源管理)之类的结构一起使用,比如Java SE 7的try-with-resources语句。
  6. (2) AbstractNativeReference#finalize()
  7. 当对象的所有强引用都已过期,且对象刚被垃圾回收之前,Java的终结器线程调用此方法。
  8. 最终,它委托AbstractNativeReference#close()。然而,用户不应该依赖于此,而应该把它看作是最后努力的故障保险。
  9. 它肯定会确保在收集Java对象时清理所拥有的C++对象。它不但是帮助管理的记忆RocksJava作为一个整体,在堆上分配的内存的C++本机C++对象支持JavaJava对象实际上是无形的GC过程,因此JVM不能正确计算GC的内存压力。
  10. 当使用RocksJava对象时,用户应该始终显式地调用AbstractNativeReference#close()。

Opening a Database 打开数据库

rocksdb数据库有一个对应于文件系统目录的名称。数据库的所有内容都存储在这个目录中。下面的例子展示了如何打开一个数据库,在必要时创建它:

  1. import org.rocksdb.RocksDB;
  2. import org.rocksdb.Options;
  3. ...
  4. // a static method that loads the RocksDB C++ library.
  5. RocksDB.loadLibrary();
  6. // the Options class contains a set of configurable DB options
  7. // that determines the behaviour of the database.
  8. try (final Options options = new Options().setCreateIfMissing(true)) {
  9. // a factory method that returns a RocksDB instance
  10. try (final RocksDB db = RocksDB.open(options, "path/to/db")) {
  11. // do something
  12. }
  13. } catch (RocksDBException e) {
  14. // do some error handling
  15. ...
  16. }
  17. ...

提示: 您可能注意到了上面的RocksDBException类。这个异常类扩展java.lang.Exception。 Exception并将状态类封装在C++ rocksdb中,后者描述了rocksdb的任何内部错误。

Reads and Writes 读和写

数据库提供put、remove和get方法来修改/查询数据库。例如,下面的代码将存储在key1下的值移动到key2。

  1. byte[] key1;
  2. byte[] key2;
  3. // some initialization for key1 and key2
  4. try {
  5. final byte[] value = db.get(key1);
  6. if (value != null) { // value == null if key1 does not exist in db.
  7. db.put(key2, value);
  8. }
  9. db.remove(key1);
  10. } catch (RocksDBException e) {
  11. // error handling
  12. }

提示: 您还可以使用WriteOptions和ReadOptions调用它们的多态方法RocksDB来控制put和get行为。 RocksDB.put(WriteOptions opt, byte[] key, byte[] value) 和 RocksDB.get(ReadOptions opt, byte[] key).

提示: 为了避免在RocksDB.get()中创建字节数组,还可以使用它的参数化方法 int RocksDB.get(byte[] key, byte[] value) 或 int RocksDB.get(ReadOptions opt, byte[] key, byte[] value), 其中输出值将被填充到预先分配的输出缓冲区值中,其int返回值将指示与输入键关联的值的实际长度。当返回值大于value.length,这表示输出缓冲区的大小不够。

打开包含列族的数据库

一个rocksdb数据库可能有多个列族,列族允许您将类似的键/值组合在一起,并独立于其他列族对它们执行操作。

如果您以前使用过RocksDB,但没有显式地使用列族,那么您可能会惊讶的发现,实际上所有的操作都是在列族上进行的,即默认列族。

重要的要注意,在使用RocksJava中的列族时,必须遵守一个非常特定的销毁顺序,以便数据库正确释放并关闭,这个代码示例说明了顺序:

  1. import org.rocksdb.RocksDB;
  2. import org.rocksdb.Options;
  3. ...
  4. // a static method that loads the RocksDB C++ library.
  5. RocksDB.loadLibrary();
  6. try (final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions().optimizeUniversalStyleCompaction()) {
  7. // list of column family descriptors, first entry must always be default column family
  8. final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
  9. new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
  10. new ColumnFamilyDescriptor("my-first-columnfamily".getBytes(), cfOpts)
  11. );
  12. // a list which will hold the handles for the column families once the db is opened
  13. final List<ColumnFamilyHandle> columnFamilyHandleList =
  14. new ArrayList<>();
  15. try (final DBOptions options = new DBOptions()
  16. .setCreateIfMissing(true)
  17. .setCreateMissingColumnFamilies(true);
  18. final RocksDB db = RocksDB.open(options,
  19. "path/to/do", cfDescriptors,
  20. columnFamilyHandleList)) {
  21. try {
  22. // do something
  23. } finally {
  24. // NOTE frees the column family handles before freeing the db
  25. for (final ColumnFamilyHandle columnFamilyHandle :
  26. columnFamilyHandleList) {
  27. columnFamilyHandle.close();
  28. }
  29. } // frees the db and the db options
  30. }
  31. } // frees the column family options
  32. ...