RocksDB报错:Compression type Snappy is not linked with the binary.

♀尐吖头ヾ 提交于 2020-01-17 05:34:15

表现:通过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 <snappy.h>
          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 rocksdbjavastaticmake 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编译开关地址链接。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!