天天看點

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 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編譯開關位址連結。