RocksDB報錯:Compression type Snappy is not linked with the binary.

表現:通過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編譯開關地址鏈接。

發佈了16 篇原創文章 · 獲贊 21 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章