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):
backend=rocksdbselects the RocksDB store provider.serializer=binaryis the serializer the shipped template uses for this backend. The built-in serializers arebinary,binaryscatterandtext.storeis 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:
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 bothrocksdb.data_pathandrocksdb.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_disksset, the tables named there are opened as separate RocksDB instances under the given paths instead of underrocksdb.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 option | default value | description |
|---|---|---|
| rocksdb.data_path | rocksdb-data/data | The 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_path | rocksdb-data/wal | The 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_level | INFO | The info log level of RocksDB. Allowed values: DEBUG, INFO, WARN, ERROR, FATAL, HEADER. |
Compaction and compression options
| config option | default value | description |
|---|---|---|
| rocksdb.num_levels | 7 | Set the number of levels for this database. Range: 1 to 2^31-1. |
| rocksdb.compaction_style | LEVEL | Set compaction style for RocksDB: LEVEL/UNIVERSAL/FIFO. |
| rocksdb.optimize_mode | true | Optimize for heavy workloads and big datasets. See “How the options are applied” below. |
| rocksdb.bulkload_mode | false | Switch 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_compression | none | The compression algorithm for the bottommost level of RocksDB, allowed values are none/snappy/z/bzip2/lz4/lz4hc/xpress/zstd. |
| rocksdb.compression | snappy | The compression algorithm for compressing blocks of RocksDB, allowed values are none/snappy/z/bzip2/lz4/lz4hc/xpress/zstd. |
Database level options
| config option | default value | description |
|---|---|---|
| rocksdb.max_background_jobs | 8 | Maximum number of concurrent background jobs, including flushes and compactions. Range: 1 to 2^31-1. |
| rocksdb.max_subcompactions | 4 | The value represents the maximum number of threads per compaction job. Range: 1 to 2^31-1. |
| rocksdb.delayed_write_rate | 16777216 (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 | -1 | The maximum number of open files that can be cached by RocksDB, -1 means no limit. |
| rocksdb.max_manifest_file_size | 104857600 (100 MB) | The max size of manifest file in bytes. |
| rocksdb.skip_stats_update_on_db_open | false | Whether 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_open | false | Whether to skip checking sizes of all sst files when opening the database. |
| rocksdb.max_file_opening_threads | 16 | The max number of threads used to open files. Range: 1 to 2^31-1. |
| rocksdb.max_total_wal_size | 0 | Total 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_sync | 0 | Allows 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_sync | 0 | Same as above for WAL files. 0 turns it off. |
| rocksdb.strict_bytes_per_sync | false | When 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_size | 0 | Total size of write buffers in bytes across all column families, 0 means no limit. |
| rocksdb.log_readahead_size | 0 | The number of bytes to prefetch when reading the log. 0 means the prefetching is disabled. |
| rocksdb.compaction_readahead_size | 0 | The 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_capacity | 0 | The capacity in bytes of global cache for table-level rows. 0 means the row_cache is disabled. |
| rocksdb.delete_obsolete_files_period | 21600 (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 option | default value | description |
|---|---|---|
| rocksdb.write_buffer_size | 134217728 (128 MB) | Amount of data in bytes to build up in memory. Minimum 1 MB. This is per column family. |
| rocksdb.max_write_buffer_number | 6 | The maximum number of write buffers that are built up in memory. Range: 1 to 2^31-1. |
| rocksdb.min_write_buffer_number_to_merge | 2 | The minimum number of write buffers that will be merged together. Range: 1 to 2^31-1. |
| rocksdb.max_write_buffer_number_to_maintain | 0 | The total maximum number of write buffers to maintain in memory for conflict checking when transactions are used. |
| rocksdb.memtable_bloom_size_ratio | 0.0 | If 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_filtering | false | Enable 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_size | 0 | The page size for huge page TLB for bloom in memtable. If <= 0, not allocate from huge page TLB but from malloc. |
| rocksdb.inplace_update_support | false | Allows 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 option | default value | description |
|---|---|---|
| rocksdb.level_compaction_dynamic_level_bytes | false | Whether 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_base | 536870912 (512 MB) | The upper-bound of the total size of level-1 files in bytes. Minimum 1 MB. |
| rocksdb.max_bytes_for_level_multiplier | 10.0 | The 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_base | 67108864 (64 MB) | The target file size for compaction in bytes. Minimum 1 MB. |
| rocksdb.target_file_size_multiplier | 1 | The size ratio between a level L file and a level (L+1) file. |
| rocksdb.level0_file_num_compaction_trigger | 2 | Number of files to trigger level-0 compaction. |
| rocksdb.level0_slowdown_writes_trigger | 20 | Soft limit on number of level-0 files for slowing down writes. |
| rocksdb.level0_stop_writes_trigger | 36 | Hard limit on number of level-0 files for stopping writes. |
| rocksdb.soft_pending_compaction_bytes_limit | 68719476736 (64 GB) | The soft limit to impose on pending compaction in bytes. Minimum 1 GB. |
| rocksdb.hard_pending_compaction_bytes_limit | 274877906944 (256 GB) | The hard limit to impose on pending compaction in bytes. Minimum 1 GB. |
File I/O options
| config option | default value | description |
|---|---|---|
| rocksdb.allow_mmap_writes | false | Allow the OS to mmap file for writing. |
| rocksdb.allow_mmap_reads | false | Allow the OS to mmap file for reading sst tables. |
| rocksdb.use_direct_reads | false | Enable the OS to use direct I/O for reading sst tables. |
| rocksdb.use_direct_io_for_flush_and_compaction | false | Enable the OS to use direct read/writes in flush and compaction. |
| rocksdb.use_fsync | false | If true, then every store to stable storage will issue a fsync. |
| rocksdb.atomic_flush | false | If 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 option | default value | description |
|---|---|---|
| rocksdb.format_version | 5 | The format version of BlockBasedTable, allowed values are 0~5. |
| rocksdb.index_type | kBinarySearch | The index type used to lookup between data blocks with the sst table, allowed values are [kBinarySearch, kHashSearch, kTwoLevelIndexSearch, kBinarySearchWithFirstKey]. |
| rocksdb.data_block_index_type | kDataBlockBinarySearch | The search type used to point lookup in data block with the sst table, allowed values are [kDataBlockBinarySearch, kDataBlockBinaryAndHash]. |
| rocksdb.data_block_hash_table_util_ratio | 0.75 | The 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_size | 4096 (4 KB) | Approximate size of user data packed per block, Note that it corresponds to uncompressed data. |
| rocksdb.block_size_deviation | 10 | The percentage of free space used to close a block. Range: 0 to 100. |
| rocksdb.block_restart_interval | 16 | The block restart interval for delta encoding in blocks. |
| rocksdb.block_cache_capacity | 8388608 (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 option | default value | description |
|---|---|---|
| rocksdb.bloom_filter_bits_per_key | -1 | The 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_mode | false | If bloom filter is enabled, set this option true to use block based filter rather than full filter. |
| rocksdb.bloom_filter_whole_key_filtering | true | If 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_blocks | true | Set this option true if we’d put index/filter blocks to the block cache. |
| rocksdb.pin_l0_filter_and_index_blocks_in_cache | true | Set this option true if we’d pin L0 index/filter blocks to the block cache. |
| rocksdb.optimize_filters_for_hits | true | If 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_indexes | false | If 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_filter | true | If 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_bytes | 0 | The 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=trueapplies 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=truedisables 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=0turns the block cache off completely rather than making it unbounded.rocksdb.prefix_extractor_n_bytesgreater than 0 installs a capped prefix extractor of that length.- Every column family uses the
uint64addmerge operator, which is what the counter table relies on. - The database is created if it is missing, and
avoid_unnecessary_blocking_ioandwrite_dbid_to_manifestare 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_capacitycreates 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 them,gandsstores of every graph, plus the instances opened forrocksdb.data_disks.rocksdb.write_buffer_sizetimesrocksdb.max_write_buffer_numberbounds the memtable memory of one column family.rocksdb.db_write_buffer_sizecaps 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.shforcesraft.mode=falsewhile it initializes the backend, so initialization never goes through raft.- The shipped
conf/graphs/hugegraph.propertiesmarks the raft options as deprecated. Distributed deployments of 1.7.0 and later use thehstorebackend with PD and Store instead. - The raft peer endpoints are served under
graphspaces/{graphspace}/graphs/{graph}/raft/, withlist_peers,get_leader,set_leader,transfer_leader,add_peerandremove_peer.bin/raft-tools.shwraps 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,containsandcontains_keyconditions, 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.