Golang的Rocksdb开发环境搭建

1,096 阅读7分钟

Go-Rocksdb开发环境搭建

因为需要使用golang开发rocksdb,其中有不少繁琐的环境依赖,所以记录一下mac上的开发环境搭建,并提供dockerfile以便在window或者liunx环境下使用。

注意,当前环境:(不是这个环境可能不适用)

go: 1.21
rocksdb: 7.10.2
lunxGnu/grocksdb: 1.7.16
< macOs: 13 >

环境介绍

  • rocksdb是facebook开源的一个kv数据库,经常作为存储中心的前置引擎。由C++编写,支持通过 C 绑定嵌入到使用 C、C++、Rust、Go 和 Java 等多种语言编写的应用程序中。

  • rocksdb运行时依赖于多个软件包,需要在对应系统安装。

    You can link RocksDB with following compression libraries:
    
    zlib - a library for data compression.
    bzip2 - a library for data compression.
    lz4 - a library for extremely fast data compression.
    snappy - a library for fast data compression.
    zstandard - Fast real-time compression algorithm.
    All our tools depend on:
    
    gflags - a library that handles command line flags processing. You can compile rocksdb library even if you don't have gflags installed.
    
  • rocksdb的编译有多种选择,可以是编译静态库,也可以编译动态库,基于开发架构选择使用。

    make clean
    
    静态库编译,得到librocksdb.a文件
    make static_lib
    
    or
    
    动态库编译,得到librocksdb.dylib(mac)或者librocksdb.so(linux)文件
    make shared_lib
    
    or 
    
    cmake 编译
    mkdir ./build && cd ./build
    cmake ..
    make
    
    安装
    make install shared PREFIX="路径"
    
  • CGO环境,使用golang开发rocksdb,通过cgo与rocksdb通讯,需要配置对应的环境。go env的CGO_ENABLED='1'

    指定rocksdb的include,可以使用源代码项目中的,也可以install
    export CGO_CFLAGS="-I/Users/apple/workspace/rocksdb_env/include"
    
    指定链接库,rocksdb可以是动态库或者静态库,其他的是对应的依赖
    export CGO_LDFLAGS="-L/Users/apple/workspace/rocksdb_env -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd"
    
  • 指定动态库路径查找路径,程序会到usr/lib,/usr/local/lib等路径中查找,可以通过参数控制。

    liunx中
    export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
    
    mac中
    export DYLD_LIBRARY_PATH="/usr/local/lib:$DYLD_LIBRARY_PATH"
    
  • lunxGnu/grocksdb 包装了rocksdb的接口,方便golang开发者使用


mac安装

  • 通过brew安装rocksdb的依赖(注意版本,经过测试,macOs 14安装的版本并不适用,需要手动通过源码编译)

    brew install zlib bzip2 lz4 snappy zstd
    
    ----
    注意版本:
    librocksdb.dylib:
    @rpath/librocksdb.7.dylib (compatibility version 7.0.0, current version 7.10.2)
    /opt/homebrew/opt/gflags/lib/libgflags.2.2.dylib (compatibility version 2.2.0, current version 2.2.2)
    /opt/homebrew/opt/zstd/lib/libzstd.1.dylib (compatibility version 1.0.0, current version 1.5.5)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
    
    
  • 下载rocksdb源码: git clone github.com/facebook/ro… && git checkout tags/v7.10.2​

  • make编译

    • 这里需要注意gcc的版本,有可能执行make失败,自行解决 :)。

    • make执行不了就通过cmake编译,cmake编译需要注意一个坑:

      如果开发对应的rocksdb中使用了zstd压缩,指定了zstd的压缩算法,那么需要将zstd的依赖编译进rocksdb的动态链接库librocksdb.dylib中。若没有将zstd编译进来,编译go_rocksdb程序能够成功。但是会在运行go程序的时候报错: `Invalid argument: Compression type ZSTD is not linked with the binary`
      
      坑点:
      在rocksdb7.10.2的分支中,cmake对应的CMakeLists.txt文件却默认把zstd依赖关掉了,导致了cmake编译出来的动态库是不包含zstd依赖的。
      
      解决:
      把CMakeLists.txt中,option(WITH_ZSTD "build with zstd" OFF) 的OFF改成ON,就可以将zstd编译到动态库中,golang调用cgo的时候可以找到zstd。
      
  • 静态库与动态库的选择,两者二选一,只要设定好CGO_LDFLAGS路径即可。不过使用动态库的话,需要关注LD_LIBRARY_PATH(linux)参数。

  • make intall 安装

  • 检查,如果使用动态库的话,确保动态库依赖都装上了。

    otool -L librocksdb.dylib
    librocksdb.dylib:
    @rpath/librocksdb.7.dylib (compatibility version 7.0.0, current version 7.10.2)
    /opt/homebrew/opt/gflags/lib/libgflags.2.2.dylib (compatibility version 2.2.0, current version 2.2.2)
    /opt/homebrew/opt/zstd/lib/libzstd.1.dylib (compatibility version 1.0.0, current version 1.5.5)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
    
  • 编译与运行


Docker 开箱即用

如果不是在mac上,可以使用docker打包环境,在docker容器中开发。最好选择liunx docker环境。

  • 提供dockerfile,基于alpine liunx编译rocksdb与对应依赖,然后拷贝到golang1.21的alpine环境中使用,确保开箱即用。
  • 官方golang1.21-alpine基于alpine3.20, 版本相对较高,需要做对应的修改。
  • alpine3.20的国内软件源需要使用对应的版本。
  • 将开发目录挂载进入docker中,使用vscode远程连接进入docker中开发。(确保与主机桥接网络,避免内部依赖拉取不了)

Dockerfile

FROM alpine:3.20 AS builder

ARG ROCKSDB_VERSION=v7.10.2
ARG LIB_DIR=/usr/local/rocksdb_env

RUN echo -e http://mirrors.ustc.edu.cn/alpine/v3.20/main/ > /etc/apk/repositories

RUN apk update && \
    apk add --no-cache --update \
    build-base \
    autoconf \
    automake \
    libtool \
    pkgconfig \
    git \
    bash \
    linux-headers \
    perl

RUN apk add --no-cache --update \
    zlib-dev \
    snappy-dev \
    bzip2-dev \
    lz4-dev \
    zstd-dev \
    zstd \
    libcurl \
    curl-dev \
    libsodium-dev

RUN apk add --no-cache --update cmake

RUN cd /tmp && \
    git clone https://github.com/gflags/gflags.git && \
    cd gflags && \
    mkdir build && \
    cd build && \
    cmake -DBUILD_SHARED_LIBS=1 -DGFLAGS_INSTALL_SHARED_LIBS=1 .. && \
    make install && \
    cd /tmp && \
    rm -R /tmp/gflags/

RUN mkdir /usr/src && \
    cd /usr/src && \
    git clone --depth 1 --branch ${ROCKSDB_VERSION} https://github.com/facebook/rocksdb.git && \
    cd /usr/src/rocksdb

RUN gcc --version && \
    echo "rocksdb_version= ${ROCKSDB_VERSION}"

# - 编译rocksdb动态库,因为alpine liunx安装的软件都是默认动态库,所以依赖也都是动态库.
# - 因为alpine liunx 3.20中,gcc版本与rocksdb v7.10.2不同,
#   所以需要手动添加 cstdint 头文件,否则编译失败.
# - 因为alpine基于busybox,musl与gnu有的命令有区别,所以需要修改makfile中的install的选项。
RUN cd /usr/src/rocksdb && \
    sed -i '1i #include <cstdint>' table/block_based/data_block_hash_index.h && \
    sed -i '1i #include <cstdint>' util/string_util.h && \
    sed -i 's/install -C/install -c/g' Makefile

RUN cd /usr/src/rocksdb && \
    make clean && \
    # make shared_lib && \
    echo "rocksdb_lib= ${LIB_DIR}" && \
    make install-shared PREFIX=${LIB_DIR} && \
    cd ${LIB_DIR}/lib && \
    # rm -R /usr/src/rocksdb/ && \
    ls -al && \
    ldd ./librocksdb.so

# ----------------------------------------------------------

FROM golang:1.21-alpine

ARG ROCKSDB_VERSION=v7.10.2
ARG LIB_DIR=/usr/local/rocksdb_env

RUN mkdir -p ${LIB_DIR} &&\
    mkdir -p /app && \
    echo -e http://mirrors.ustc.edu.cn/alpine/v3.20/main/ > /etc/apk/repositories

# 复制编译好的 RocksDB 到最终镜像
COPY --from=builder /usr/local/lib /usr/local/lib
COPY --from=builder /usr/lib /usr/lib
COPY --from=builder ${LIB_DIR} ${LIB_DIR}
# COPY --from=builder /usr/local/include/rocksdb /usr/local/include/rocksdb

# 设置CGO环境变量
ENV CGO_CFLAGS="-I${LIB_DIR}/include" \
    CGO_LDFLAGS="-L${LIB_DIR}/lib -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd" \
    LD_LIBRARY_PATH="/usr/local/lib:${LIB_DIR}/lib:$LD_LIBRARY_PATH" \
    GOPROXY=https://goproxy.io,direct \
    CGO_ENABLED='1'

RUN apk add --no-cache --update \
    build-base \
    zlib \
    zlib-dev

# 设置工作目录
WORKDIR /app

EXPOSE 7379

使用

可以将对应的开发rocksdb目录挂载进入docker中,使用vscode远程连接进入docker中开发(桥接主机网络)。

构建镜像
docker build -t grdb:st -f ./Dockerfile .

运行
docker run --name grdb-st -v /tmp/app:/app grdb:st /bin/sh

远程连接并开发

验证环境

验证环境是否安装完成,可以使用rocksdb项目中的make验证,但是,使用golang开发的情况,还需要验证golang的CGO是否能够成功调用。

  • 首先进入容器,创建一个golang项目。

  • 安装grocksdb ,版本确定为1.7.16

    go get github.com/linxGnu/grocksdb@v1.7.16

  • 使用以下代码:

    package main
    
    import (
    "log"
    "github.com/linxGnu/grocksdb"
    )
    
    func main() {
    opts := grocksdb.NewDefaultOptions()
    opts.SetCreateIfMissing(true)
    db, err := grocksdb.OpenDb(opts, "./path/to/db")
    if err != nil {
    	log.Fatalf("Error opening db: %v", err)
    }
    defer db.Close()
    // 进行数据库操作...
    // 写入数据
    writeOpts := grocksdb.NewDefaultWriteOptions()
    defer writeOpts.Destroy()
    err = db.Put(writeOpts, []byte("key1"), []byte("value1"))
    if err != nil {
    	log.Fatalf("Error putting data: %v", err)
    }
    // 读取数据
    readOpts := grocksdb.NewDefaultReadOptions()
    defer readOpts.Destroy()
    value, err := db.Get(readOpts, []byte("key1"))
    if err != nil {
    	log.Fatalf("Error getting data: %v", err)
    }
    defer value.Free()
    log.Printf("Value: %s", value.Data())
    // // 批量写入
    // batch := grocksdb.NewWriteBatch()
    // defer batch.Destroy()
    // batch.Put([]byte("key1"), []byte("value1"))
    // batch.Put([]byte("key2"), []byte("value2"))
    // err = db.Write(writeOpts, batch)
    // if err != nil {
    // 	log.Fatalf("Error writing batch: %v", err)
    // }
    // // 迭代器使用
    // it := db.NewIterator(readOpts)
    // defer it.Close()
    // for it.SeekToFirst(); it.Valid(); it.Next() {
    // 	key := it.Key()
    // 	value := it.Value()
    // 	log.Printf("Key: %s, Value: %s\n", string(key.Data()), string(value.Data()))
    // }
    }
    
  • 执行mod tidy
  • 因为环境变量参数都已经设置好了,可以直接编译 go builid ./main
  • 因为环境已经在docker中设置好,可以直接运行 ./main
  • 如果正确输出,则环境正常

报错备忘记录

+

/go/pkg/mod/github.com/linx!gnu/grocksdb@v1.9.3/cache.go:25:12: could not determine kind of name for C.rocksdb_cache_create_hyper_clock
/go/pkg/mod/github.com/linx!gnu/grocksdb@v1.9.3/cache.go:31:12: could not determine kind of name for C.rocksdb_cache_create_hyper_clock_opts

go程序编译报错,grocksdb版本太高,对应的rocksdb7.10.2没有,需要将grocksdb将为1.7.16.

+

$DEBUG_LEVEL is 0
make: -c: No such file or directory
make: -c: No such file or directory
make: -c: No such file or directory
make: -c: No such file or directory
make: -c: No such file or directory
make: -c: No such file or directory
make: -c: No such file or directory
Makefile:245: make_config.mk: No such file or directory
make: *** No rule to make target 'make_config.mk'.  Stop.

rocksdb项目中执行make报错,未知原因,额外安装一个bash可以解决。

+

Reason: tried: '/System/Volumes/Preboot/Cryptexes/OS@rpath/librocksdb.7.dylib' (no such file), '/usr/local/lib/librocksdb.7.dylib' (no such file), '/usr/lib/librocksdb.7.dylib' (no such file, not in dyld cache)

编译rocksdb报错,使用了动态库,但是没有配置动态库路径参数。

mac: DYLD_LIBRARY_PATH

liunx: LD_LIBRARY_PATH

+

xxx no found.

由于alpine镜像使用的是musl libc而不是gnu libc,/lib64/ 是不存在的。但他们是兼容的.

​mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2​

+

  • make编译到一半,报错:note: 'uint8_t' is defined in header ''; did you forget to '#include '

    是gcc的版本对应不上,可以通过手动添加:sed -i '1i #include ' table/block_based/data_block_hash_index.h && sed -i '1i #include ' util/string_util.h ​修改。

  • make的install失败,因为alpine中-C命令需要改为-c。
    ​sed -i 's/install -C/install -c/g' Makefile​ (Fix 'install -c' flag)

+

运行编译好的go程序时报错:
​Invalid argument: Compression type ZSTD is not linked with the binary​

这个是因为编译的时候丢了zstd依赖,可能是cmake编译的,通过otool(mac上)或者ldd(liunx上)工具查看对应动态库的依赖。