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:
<dependency>
<groupId>org.rocksdb</groupId>
<artifactId>rocksdbjni</artifactId>
<version>5.5.1</version>
</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绑定:
$ 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类的本机实现。 运行单元测试:
$ make jtest
[只在Facebook内部]在Facebook开发服务器上:
$ ROCKSDB_NO_FBCODE=1 make jtest
清理:
$ make jclean
例子:
如果您想直接跳到代码中,我们在这里提供了一些示例。(https://github.com/facebook/rocksdb/tree/master/java/samples/src/main/java)
内存管理
RocksJava API中使用的许多Java对象将由Java对象拥有的所有权的C++对象支持。由于C++不像Java那样自动垃圾回收,所以我们必须显式的释放C++对象使用的内存。
管理C++对象的RocksJava中的任何Java对象都讲继承自org.rocksdb.AbstractNativeReference是设计用来帮助管理和清理所有C++对象的。为此使用了两种机制:
(1) AbstractNativeReference#close()
当用户使用完RocksJava对象后,应该显式地调用此方法。如果分配了C++对象,但还没有释放,则在第一次调用此方法时将释放这些对象。
为了方便使用,这个方法覆盖了java.lang.AutoCloseable#close(), 这使得它可以与ARM(自动资源管理)之类的结构一起使用,比如Java SE 7的try-with-resources语句。
(2) AbstractNativeReference#finalize()
当对象的所有强引用都已过期,且对象刚被垃圾回收之前,Java的终结器线程调用此方法。
最终,它委托AbstractNativeReference#close()。然而,用户不应该依赖于此,而应该把它看作是最后努力的故障保险。
它肯定会确保在收集Java对象时清理所拥有的C++对象。它不但是帮助管理的记忆RocksJava作为一个整体,在堆上分配的内存的C++本机C++对象支持Java的Java对象实际上是无形的GC过程,因此JVM不能正确计算GC的内存压力。
当使用RocksJava对象时,用户应该始终显式地调用AbstractNativeReference#close()。
Opening a Database 打开数据库
rocksdb数据库有一个对应于文件系统目录的名称。数据库的所有内容都存储在这个目录中。下面的例子展示了如何打开一个数据库,在必要时创建它:
import org.rocksdb.RocksDB;
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
...
}
...
提示: 您可能注意到了上面的RocksDBException类。这个异常类扩展java.lang.Exception。 Exception并将状态类封装在C++ rocksdb中,后者描述了rocksdb的任何内部错误。
Reads and Writes 读和写
数据库提供put、remove和get方法来修改/查询数据库。例如,下面的代码将存储在key1下的值移动到key2。
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.remove(key1);
} catch (RocksDBException e) {
// error handling
}
提示: 您还可以使用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中的列族时,必须遵守一个非常特定的销毁顺序,以便数据库正确释放并关闭,这个代码示例说明了顺序:
import org.rocksdb.RocksDB;
import org.rocksdb.Options;
...
// a static method that loads the RocksDB C++ library.
RocksDB.loadLibrary();
try (final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions().optimizeUniversalStyleCompaction()) {
// list of column family descriptors, first entry must always be default column family
final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
new ColumnFamilyDescriptor("my-first-columnfamily".getBytes(), cfOpts)
);
// a list which will hold the handles for the column families once the db is opened
final List<ColumnFamilyHandle> columnFamilyHandleList =
new ArrayList<>();
try (final DBOptions options = new DBOptions()
.setCreateIfMissing(true)
.setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options,
"path/to/do", cfDescriptors,
columnFamilyHandleList)) {
try {
// do something
} finally {
// NOTE frees the column family handles before freeing the db
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
} // frees the db and the db options
}
} // frees the column family options
...