Skip to content

Configuring the RocksDB Backend

Overview

RocksDB is an embedded LSM-tree key-value store. With the rocksdb backend, HugeGraph-Server keeps all graph data in RocksDB instances that live inside the server process, so there is no separate storage service to deploy. This is the backend used by the shipped conf/graphs/hugegraph.properties.

Since version 1.7.0 the server accepts only memory, rocksdb, hbase and hstore as the backend. The rocksdb backend stores data on the local disks of one server: it does not support shared storage, so a graph cannot be served by several servers over the same data directory. For a distributed deployment use the hstore backend with PD and Store.

The RocksDB JNI library is pinned to version 8.10.2 by hugegraph-rocksdb/pom.xml, so the on-disk format and the option semantics are those of RocksDB 8.10.

The backend driver version reported by this store is 1.11, and it is written into the meta table of the system store when the graph is initialized.

Selecting the backend

Set the backend and the serializer in the graph properties file (conf/graphs/<graph>.properties):

gremlin.graph=org.apache.hugegraph.HugeFactory

backend=rocksdb
serializer=binary

store=hugegraph

# rocksdb backend config
#rocksdb.data_path=/path/to/disk
#rocksdb.wal_path=/path/to/disk
  • backend=rocksdb selects the RocksDB store provider.
  • serializer=binary is the serializer the shipped template uses for this backend. The built-in serializers are binary, binaryscatter and text.
  • store is the database namespace of the graph, and it is also part of the graph name that the provider passes down to the store.

Run bin/init-store.sh once before the first start to create the stores, then start the server. Both bin/init-store.sh and bin/hugegraph-server.sh load the RocksDB library, so the data directories are created on the machine that runs them.

The distribution registers the option space and the store provider for each backend listed in the packaged backend.properties, whose value comes from the hugegraph.backends build property. A default build registers rocksdb, hbase, hstore; building with -Drocksdb-only activates the rocksdb-only profile and produces a distribution that registers only rocksdb. A backend that is not registered fails at startup with Not exists BackendStoreProvider.

The provider registration also adds a second name, rocksdbsst, for the store that writes SST files instead of a live database. That name is not in the list of allowed backends, so backend=rocksdbsst is rejected with backend is illegal: rocksdbsst. To load SST files into a normal rocksdb graph, use rocksdb.sst_path as described below.

Data directory layout

Two directories matter: rocksdb.data_path (default rocksdb-data/data) and rocksdb.wal_path (default rocksdb-data/wal). Relative paths resolve against the working directory of the server, which is the installation directory.

Each graph opens three stores: m for schema, g for graph data, and s for the system store. The store name is appended to both configured paths, so a default single-graph installation looks like this:

rocksdb-data/
  data/
    m/    # schema store: property keys, vertex/edge/index labels, counters
    g/    # graph store: vertices, edges, index tables, olap tables
    s/    # system store: tasks, server info, backend meta (driver version)
  wal/
    m/
    g/
    s/

Every backend table becomes a RocksDB column family inside the store it belongs to, named <database>+<table>, where the database is derived from the graph name. Column families of existing data directories are always reopened, so tables created by an older version stay readable.

Other points to keep in mind:

  • Two graphs must not share a data path. When a graph is created by cloning an existing configuration through the API, the provider appends _<newGraph> to both rocksdb.data_path and rocksdb.wal_path. Deleting such a graph deletes both directories.
  • Snapshots are created beside the data directory: the last two segments of the data path are rewritten with a prefix, so with the default paths the snapshot of the graph store goes to rocksdb-data/<prefix>_data/g. Resuming a snapshot closes the instance, deletes the data directory and moves the snapshot into its place.
  • With rocksdb.data_disks set, the tables named there are opened as separate RocksDB instances under the given paths instead of under rocksdb.data_path. The server opens up to 8 instances in parallel, waits at most 600 seconds for the open to finish and 30 seconds for sessions to close.

Path and log options

config optiondefault valuedescription
rocksdb.data_pathrocksdb-data/dataThe path for storing data of RocksDB. Must not be empty.
rocksdb.data_disks[]The optimized disks for storing data of RocksDB. The format of each element: STORE/TABLE: /path/disk. Allowed keys are [g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, g/olap]. A disk path must differ from rocksdb.data_path.
rocksdb.wal_pathrocksdb-data/walThe path for storing WAL of RocksDB. Must not be empty.
rocksdb.sst_path(empty)The path for ingesting SST file into RocksDB. Empty disables ingestion.
rocksdb.log_levelINFOThe info log level of RocksDB. Allowed values: DEBUG, INFO, WARN, ERROR, FATAL, HEADER.

Compaction and compression options

config optiondefault valuedescription
rocksdb.num_levels7Set the number of levels for this database. Range: 1 to 2^31-1.
rocksdb.compaction_styleLEVELSet compaction style for RocksDB: LEVEL/UNIVERSAL/FIFO.
rocksdb.optimize_modetrueOptimize for heavy workloads and big datasets. See “How the options are applied” below.
rocksdb.bulkload_modefalseSwitch to the mode to bulk load data into RocksDB.
rocksdb.compression_per_level[none, none, snappy, snappy, snappy, snappy, snappy]The compression algorithms for different levels of RocksDB, allowed values are none/snappy/z/bzip2/lz4/lz4hc/xpress/zstd. The list must be empty or hold exactly rocksdb.num_levels elements.
rocksdb.bottommost_compressionnoneThe compression algorithm for the bottommost level of RocksDB, allowed values are none/snappy/z/bzip2/lz4/lz4hc/xpress/zstd.
rocksdb.compressionsnappyThe compression algorithm for compressing blocks of RocksDB, allowed values are none/snappy/z/bzip2/lz4/lz4hc/xpress/zstd.

Database level options

config optiondefault valuedescription
rocksdb.max_background_jobs8Maximum number of concurrent background jobs, including flushes and compactions. Range: 1 to 2^31-1.
rocksdb.max_subcompactions4The value represents the maximum number of threads per compaction job. Range: 1 to 2^31-1.
rocksdb.delayed_write_rate16777216 (16 MB/s)The rate limit in bytes/s of user write requests when need to slow down if the compaction gets behind.
rocksdb.max_open_files-1The maximum number of open files that can be cached by RocksDB, -1 means no limit.
rocksdb.max_manifest_file_size104857600 (100 MB)The max size of manifest file in bytes.
rocksdb.skip_stats_update_on_db_openfalseWhether to skip statistics update when opening the database, setting this flag true allows us to not update statistics.
rocksdb.skip_check_sst_size_on_db_openfalseWhether to skip checking sizes of all sst files when opening the database.
rocksdb.max_file_opening_threads16The max number of threads used to open files. Range: 1 to 2^31-1.
rocksdb.max_total_wal_size0Total size of WAL files in bytes. Once WALs exceed this size, we will start forcing the flush of column families related, 0 means no limit.
rocksdb.bytes_per_sync0Allows OS to incrementally sync SST files to disk while they are being written, asynchronously in the background. Issue one request for every bytes_per_sync written. 0 turns it off.
rocksdb.wal_bytes_per_sync0Same as above for WAL files. 0 turns it off.
rocksdb.strict_bytes_per_syncfalseWhen true, guarantees SST/WAL files have at most bytes_per_sync/wal_bytes_per_sync bytes submitted for writeback at any given time. This can be used to handle cases where processing speed exceeds I/O speed.
rocksdb.db_write_buffer_size0Total size of write buffers in bytes across all column families, 0 means no limit.
rocksdb.log_readahead_size0The number of bytes to prefetch when reading the log. 0 means the prefetching is disabled.
rocksdb.compaction_readahead_size0The number of bytes to perform bigger reads when doing compaction. If running RocksDB on spinning disks, you should set this to at least 2MB. 0 means the prefetching is disabled.
rocksdb.row_cache_capacity0The capacity in bytes of global cache for table-level rows. 0 means the row_cache is disabled.
rocksdb.delete_obsolete_files_period21600 (6 hours)The periodicity in seconds when obsolete files get deleted, 0 means always do full purge. The value is converted to microseconds before it reaches RocksDB.

Memtable options

config optiondefault valuedescription
rocksdb.write_buffer_size134217728 (128 MB)Amount of data in bytes to build up in memory. Minimum 1 MB. This is per column family.
rocksdb.max_write_buffer_number6The maximum number of write buffers that are built up in memory. Range: 1 to 2^31-1.
rocksdb.min_write_buffer_number_to_merge2The minimum number of write buffers that will be merged together. Range: 1 to 2^31-1.
rocksdb.max_write_buffer_number_to_maintain0The total maximum number of write buffers to maintain in memory for conflict checking when transactions are used.
rocksdb.memtable_bloom_size_ratio0.0If prefix-extractor is set and memtable_bloom_size_ratio is not 0, or if memtable_whole_key_filtering is set true, create bloom filter for memtable with the size of write_buffer_size * memtable_bloom_size_ratio. A value larger than 0.25 is reduced to 0.25. Range: 0.0 to 1.0.
rocksdb.memtable_whole_key_filteringfalseEnable whole key bloom filter in memtable, it can potentially reduce CPU usage for point-look-ups. Note this will only take effect if memtable_bloom_size_ratio > 0.
rocksdb.memtable_huge_page_size0The page size for huge page TLB for bloom in memtable. If <= 0, not allocate from huge page TLB but from malloc.
rocksdb.inplace_update_supportfalseAllows thread-safe inplace updates if a put key exists in current memtable and sizeof new value is smaller.

Level sizing and write stall options

config optiondefault valuedescription
rocksdb.level_compaction_dynamic_level_bytesfalseWhether to enable level_compaction_dynamic_level_bytes, if it’s enabled we give max_bytes_for_level_multiplier a priority against max_bytes_for_level_base, the bytes of base level is dynamic for a more predictable LSM tree, it is useful to limit worse case space amplification. Turning this feature on/off for an existing DB can cause unexpected LSM tree structure so it’s not recommended.
rocksdb.max_bytes_for_level_base536870912 (512 MB)The upper-bound of the total size of level-1 files in bytes. Minimum 1 MB.
rocksdb.max_bytes_for_level_multiplier10.0The ratio between the total size of level (L+1) files and the total size of level L files for all L. Minimum 1.0.
rocksdb.target_file_size_base67108864 (64 MB)The target file size for compaction in bytes. Minimum 1 MB.
rocksdb.target_file_size_multiplier1The size ratio between a level L file and a level (L+1) file.
rocksdb.level0_file_num_compaction_trigger2Number of files to trigger level-0 compaction.
rocksdb.level0_slowdown_writes_trigger20Soft limit on number of level-0 files for slowing down writes.
rocksdb.level0_stop_writes_trigger36Hard limit on number of level-0 files for stopping writes.
rocksdb.soft_pending_compaction_bytes_limit68719476736 (64 GB)The soft limit to impose on pending compaction in bytes. Minimum 1 GB.
rocksdb.hard_pending_compaction_bytes_limit274877906944 (256 GB)The hard limit to impose on pending compaction in bytes. Minimum 1 GB.

File I/O options

config optiondefault valuedescription
rocksdb.allow_mmap_writesfalseAllow the OS to mmap file for writing.
rocksdb.allow_mmap_readsfalseAllow the OS to mmap file for reading sst tables.
rocksdb.use_direct_readsfalseEnable the OS to use direct I/O for reading sst tables.
rocksdb.use_direct_io_for_flush_and_compactionfalseEnable the OS to use direct read/writes in flush and compaction.
rocksdb.use_fsyncfalseIf true, then every store to stable storage will issue a fsync.
rocksdb.atomic_flushfalseIf true, flushing multiple column families and committing their results atomically to MANIFEST. Note that it’s not necessary to set atomic_flush=true if WAL is always enabled.

SST table format and block cache options

config optiondefault valuedescription
rocksdb.format_version5The format version of BlockBasedTable, allowed values are 0~5.
rocksdb.index_typekBinarySearchThe index type used to lookup between data blocks with the sst table, allowed values are [kBinarySearch, kHashSearch, kTwoLevelIndexSearch, kBinarySearchWithFirstKey].
rocksdb.data_block_index_typekDataBlockBinarySearchThe search type used to point lookup in data block with the sst table, allowed values are [kDataBlockBinarySearch, kDataBlockBinaryAndHash].
rocksdb.data_block_hash_table_util_ratio0.75The hash table utilization ratio value of entries/buckets. It is valid only when data_block_index_type=kDataBlockBinaryAndHash. Range: 0.0 to 1.0.
rocksdb.block_size4096 (4 KB)Approximate size of user data packed per block, Note that it corresponds to uncompressed data.
rocksdb.block_size_deviation10The percentage of free space used to close a block. Range: 0 to 100.
rocksdb.block_restart_interval16The block restart interval for delta encoding in blocks.
rocksdb.block_cache_capacity8388608 (8 MB)The amount of block cache in bytes that will be used by RocksDB, 0 means no block cache. A separate cache of this size is created for each column family.

Bloom filter options

The options in this group are read only when rocksdb.bloom_filter_bits_per_key is 0 or greater. With the default value of -1 there is no bloom filter and none of the other options in this table take effect, including the index and filter block caching ones.

config optiondefault valuedescription
rocksdb.bloom_filter_bits_per_key-1The bits per key in bloom filter, a good value is 10, which yields a filter with ~ 1% false positive rate. Set bloom_filter_bits_per_key > 0 to enable bloom filter, -1 means no bloom filter (0~0.5 round down to no filter).
rocksdb.bloom_filter_block_based_modefalseIf bloom filter is enabled, set this option true to use block based filter rather than full filter.
rocksdb.bloom_filter_whole_key_filteringtrueIf bloom filter is enabled, set this option true to place whole keys in the bloom filter, else place the prefix of keys when prefix-extractor is set.
rocksdb.cache_index_and_filter_blockstrueSet this option true if we’d put index/filter blocks to the block cache.
rocksdb.pin_l0_filter_and_index_blocks_in_cachetrueSet this option true if we’d pin L0 index/filter blocks to the block cache.
rocksdb.optimize_filters_for_hitstrueIf bloom filter is enabled, this flag allows us to not store filters for the last level. set this option true to optimize the filters mainly for cases where keys are found rather than also optimize for keys missed. This one is applied even when the filter is disabled.
rocksdb.partition_filters_and_indexesfalseIf bloom filter is enabled, set this option true to use partitioned full filters and indexes for each sst file. This option is incompatible with block-based filters. Enabling it also forces the index type to kTwoLevelIndexSearch and sets the metadata block size to rocksdb.block_size.
rocksdb.pin_top_level_index_and_filtertrueIf partition_filters_and_indexes is set true, set this option true if we’d pin top-level index of partitioned filter and index blocks to the block cache.
rocksdb.prefix_extractor_n_bytes0The prefix-extractor uses the first N bytes of a key as its prefix, it will use the full key when a key is shorter than the N. 0 means unset prefix-extractor.

How the options are applied

The server builds the RocksDB option objects once per store and per column family, so a change to any of the options above takes effect on the next server start.

  • rocksdb.optimize_mode=true applies presets before the values in the tables above: at the database level it raises parallelism to half of the available processors (at least one), allows concurrent memtable writes and enables the write thread adaptive yield; at the column family level it calls the RocksDB level-style and universal-style compaction presets. The explicit options are applied afterwards, so any value you set in the properties file wins over the preset.
  • rocksdb.bulkload_mode=true disables automatic compaction, raises the three level-0 triggers to the maximum integer and the two pending compaction limits to the maximum long value. Turn it off and restart after the load, otherwise compaction never runs.
  • rocksdb.block_cache_capacity=0 turns the block cache off completely rather than making it unbounded.
  • rocksdb.prefix_extractor_n_bytes greater than 0 installs a capped prefix extractor of that length.
  • Every column family uses the uint64add merge operator, which is what the counter table relies on.
  • The database is created if it is missing, and avoid_unnecessary_blocking_io and write_dbid_to_manifest are always on.

Memory notes

The caches and write buffers of RocksDB are native allocations, so they are not part of the JVM heap sizing in bin/hugegraph-server.sh. The GET /metrics/backend endpoint reports what the store uses: the memory number is the sum of the block cache usage, the pinned block cache usage, the estimated table reader memory (index and filter blocks) and the size of all memtables, taken from the RocksDB properties of every open column family.

Two option values multiply with the number of column families:

  • rocksdb.block_cache_capacity creates one cache instance per column family, so the total block cache of a server is roughly this value times the number of open tables across the m, g and s stores of every graph, plus the instances opened for rocksdb.data_disks.
  • rocksdb.write_buffer_size times rocksdb.max_write_buffer_number bounds the memtable memory of one column family. rocksdb.db_write_buffer_size caps the total across all column families of one store, and its default of 0 means there is no such cap.

rocksdb.row_cache_capacity is different: it is one cache per store, and 0 disables it.

Ingesting SST files

Setting rocksdb.sst_path turns on ingestion. When a store is opened, and again whenever tables are created, the server walks <sst_path>/<column family>/, collects every non-empty *.sst file below it and ingests those files into the matching column family. The files are moved rather than copied, so the source directory is consumed by the ingestion.

Raft mode

The RocksDB backend can still run behind the raft state machine: with raft.mode=true the store provider of any local backend is wrapped by the raft provider. The wrapper rejects backends with shared storage, so rocksdb is accepted while hbase is not. Under raft mode a RocksDB session writes with the WAL disabled and without sync, because the state machine can restore from a snapshot plus the raft log, and snapshots are supported by this backend.

Notes for anyone using it:

  • bin/init-store.sh forces raft.mode=false while it initializes the backend, so initialization never goes through raft.
  • The shipped conf/graphs/hugegraph.properties marks the raft options as deprecated. Distributed deployments of 1.7.0 and later use the hstore backend with PD and Store instead.
  • The raft peer endpoints are served under graphspaces/{graphspace}/graphs/{graph}/raft/, with list_peers, get_leader, set_leader, transfer_leader, add_peer and remove_peer. bin/raft-tools.sh wraps the same operations, but it still builds URLs without the graphspace segment, so the path has to be adjusted for a 1.7.0 server.
  • The remaining raft.* options are listed in the Server Complete Configuration Manual.

Backend capabilities

The feature flags of this backend affect what the server can push down to the store:

  • Scans by key prefix and by key range, paged queries, range conditions and order-by are supported.
  • There is no index inside RocksDB, so querying schema by name, querying by label and deleting edges by label are done by the server instead of the store.
  • Transactions are supported through RocksDB write batches.
  • Snapshots are supported, which is what raft mode and backup rely on.
  • Shared storage is not supported, so one data directory belongs to one server.
  • Olap properties are supported, and their tables are created as extra column families.
  • The store does not expire data by itself, so the server filters out elements whose TTL has passed when it reads them.
  • in, contains and contains_key conditions, aggregate properties and vertex or edge property updates in place are not supported at the store level.

Platform note for riscv64

On Linux riscv64 the RocksDB JNI library needs libatomic.so.1. bin/util.sh looks for it and adds it to LD_PRELOAD before bin/hugegraph-server.sh, bin/init-store.sh and bin/dump-store.sh start the JVM. If it is missing, those scripts stop with RISC-V RocksDB requires libatomic.so.1; install libatomic1, and installing the libatomic1 package fixes it.