表现:通过JNI打开RocksDB报错:snappy压缩库没有被链接:
org.rocksdb.RocksDBException: Compression type Snappy is not linked with the binary.
at org.rocksdb.RocksDB.open(Native Method) ~[rocksdbjni-6.6.0-fix-osx.jar:?]
at org.rocksdb.RocksDB.open(RocksDB.java:290) ~[rocksdbjni-6.6.0-fix-osx.jar:?]
at com.baidu.hugegraph.backend.store.rocksdb.RocksDBStdSessions.<init>(RocksDBStdSessions.java:130) ~[classes/:?]
at com.baidu.hugegraph.backend.store.rocksdb.RocksDBStore.openSessionPool(RocksDBStore.java:299) ~[classes/:?]
at com.baidu.hugegraph.backend.store.rocksdb.RocksDBStore.open(RocksDBStore.java:237) ~[classes/:?]
at com.baidu.hugegraph.backend.store.rocksdb.RocksDBStore.open(RocksDBStore.java:228) ~[classes/:?]
从官网下载的release包是没有问题的,由于这个是我Mac本地编译包,所以报错应该是本地环境导致的。看到这个错误信息直觉是想可能没有安装libsnappy
导致的,于是brew install snappy
安装了一下,再次启动RocksDB发现还是同样的错误,没想到这小问题还很顽固,于是决定分析下究竟什么原因。
1、找到了报错的代码行,
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
if (!cf_options.compression_per_level.empty()) {
for (size_t level = 0; level < cf_options.compression_per_level.size();
++level) {
if (!CompressionTypeSupported(cf_options.compression_per_level[level])) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression_per_level[level]) +
" is not linked with the binary."); // <======= 这里报错
}
}
} else {
if (!CompressionTypeSupported(cf_options.compression)) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression) +
" is not linked with the binary.");
}
}
...
}
报错代码地址可点击此Github链接。
事实上,在打开RockSD.open()的时候,会调用
ValidateOptions()
方法检查ColumnFamilyOptions配置项是否合法:Status ColumnFamilyData::ValidateOptions( const DBOptions& db_options, const ColumnFamilyOptions& cf_options) { Status s; s = CheckCompressionSupported(cf_options); if (s.ok() && db_options.allow_concurrent_memtable_write) { s = CheckConcurrentWritesSupported(cf_options); } if (s.ok()) { s = CheckCFPathsSupported(db_options, cf_options); } if (!s.ok()) { return s; } ... }
ValidateOptions()代码地址链接。
2、从报错代码行上面的if
语句看出当调用CompressionTypeSupported()
方法返回false
时会报这个错。
看看CompressionTypeSupported()
方法里面是怎么实现的:
inline bool CompressionTypeSupported(CompressionType compression_type) {
switch (compression_type) {
case kNoCompression:
return true;
case kSnappyCompression:
return Snappy_Supported();
case kZlibCompression:
return Zlib_Supported();
...
}
inline bool Snappy_Supported() {
#ifdef SNAPPY
return true;
#else
return false;
#endif
}
Snappy_Supported()代码地址链接。
为何CompressionTypeSupported()
方法返回false
?从上述代码看出该方法根据压缩类型switch-case调用了各自实现,snappy
压缩时调用Snappy_Supported()
方法,其逻辑很简单:在编译时如果定义了SNAPPY
则表示支持。所以这个错误与运行时无关,预编译的时候就决定了是否支持snappy
压缩。
3、那问题来了,为什么我本地编译的时候没有定义SNAPPY
呢?猜测应该是自动根据本地编译环境检测是否有相关依赖库,或者提供开关选项由用户指定是否开启。
查看为何没有启用SNAPPY
:在Makefile中,会调用build_tools/build_detect_platform
脚本生成make_config.mk
编译配置文件:
# detect what platform we're building on
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; export PORTABLE="$(PORTABLE)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
# this file is generated by the previous line to set build flags and sources
include make_config.mk
build_detect_platform检测脚本链接。
查看build_detect_platform脚本,找到了SNAPPY
检测代码:如果snappy已安装则输出-DSNAPPY
配置项:
if ! test $ROCKSDB_DISABLE_SNAPPY; then
# Test whether Snappy library is installed
# http://code.google.com/p/snappy/
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
#include
int main() {
}
EOF
if [ "$?" = 0 ]; then
COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY"
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lsnappy"
JAVA_LDFLAGS="$JAVA_LDFLAGS -lsnappy"
fi
fi
自动检测SNAPPY
代码地址链接。
4、最终解决方法:本地安装snappy库之后,重新编译RocksDB即可,验证OK。
brew install snappy
make rocksdbjava
5、其它需要注意的地方:
如何禁用压缩库编译:
看起来可以通过两个开关显示禁用:ROCKSDB_DISABLE_SNAPPY或ROCKSDB_JAVA_NO_COMPRESSION,待验证。
强制编译问题:
如果没有安装snappy库强制指定要编译的话
make OPT=-DSNAPPY rocksdbjava
,是无法编译通过的,会报错:"./util/compression.h:31:10: fatal error: ‘snappy.h’ file not found" 或者 “ld: library not found for -lsnappy”。
动态库找不到问题:
如果编译过了后,拷到本地无snappy动态库的机器执行会报错:“java.lang.NoClassDefFoundError: Could not initialize class org.rocksdb.Options”。
需要以静态库方式链接依赖(本身还是动态库):
make rocksdbjavastatic
或make rocksdbjavastaticrelease
。
依赖包括:JAVA_COMPRESSIONS = libz.a libbz2.a libsnappy.a liblz4.a libzstd.a。
RocksDB中编译snappy
指令详见libsnappy.a。
Windows平台注意:
在Windows平台上实际上是提供开关选项,在
CMakeLists
中有一个WITH_SNAPPY
开关控制是否编译snappy:if(WITH_SNAPPY) find_package(snappy REQUIRED) add_definitions(-DSNAPPY) list(APPEND THIRDPARTY_LIBS snappy::snappy) endif()
option(WITH_JEMALLOC "build with JeMalloc" OFF) option(WITH_SNAPPY "build with SNAPPY" OFF) option(WITH_LZ4 "build with lz4" OFF) option(WITH_ZLIB "build with zlib" OFF) option(WITH_ZSTD "build with zstd" OFF)
压缩相关的几个开关默认都是关闭的,如果需要打开请使用:
cmake -DWITH_SNAPPY=1
WITH_SNAPPY编译开关地址链接。