Allow configuration of universal style compaction
This commit is contained in:
parent
68c58009f2
commit
6ceb6514b5
6 changed files with 228 additions and 5 deletions
|
@ -625,6 +625,84 @@ Options object
|
||||||
| *Type:* ``bool``
|
| *Type:* ``bool``
|
||||||
| *Default:* ``True``
|
| *Default:* ``True``
|
||||||
|
|
||||||
|
.. py:attribute:: compaction_style
|
||||||
|
|
||||||
|
The compaction style. Could be set to ``"level"`` to use level-style
|
||||||
|
compaction. For universal-style compaction use ``"universal"``.
|
||||||
|
|
||||||
|
| *Type:* ``string``
|
||||||
|
| *Default:* ``level``
|
||||||
|
|
||||||
|
.. py:attribute:: compaction_options_universal
|
||||||
|
|
||||||
|
Options to use for universal-style compaction. They make only sense if
|
||||||
|
:py:attr:`rocksdb.Options.compaction_style` is set to ``"universal"``.
|
||||||
|
|
||||||
|
It is a dict with the following keys.
|
||||||
|
|
||||||
|
* ``size_ratio``:
|
||||||
|
Percentage flexibilty while comparing file size.
|
||||||
|
If the candidate file(s) size is 1% smaller than the next file's size,
|
||||||
|
then include next file into this candidate set.
|
||||||
|
Default: ``1``
|
||||||
|
|
||||||
|
* ``min_merge_width``:
|
||||||
|
The minimum number of files in a single compaction run.
|
||||||
|
Default: ``2``
|
||||||
|
|
||||||
|
* ``max_merge_width``:
|
||||||
|
The maximum number of files in a single compaction run.
|
||||||
|
Default: ``UINT_MAX``
|
||||||
|
|
||||||
|
* ``max_size_amplification_percent``:
|
||||||
|
The size amplification is defined as the amount (in percentage) of
|
||||||
|
additional storage needed to store a single byte of data in the database.
|
||||||
|
For example, a size amplification of 2% means that a database that
|
||||||
|
contains 100 bytes of user-data may occupy upto 102 bytes of
|
||||||
|
physical storage. By this definition, a fully compacted database has
|
||||||
|
a size amplification of 0%. Rocksdb uses the following heuristic
|
||||||
|
to calculate size amplification: it assumes that all files excluding
|
||||||
|
the earliest file contribute to the size amplification.
|
||||||
|
Default: ``200``, which means that a 100 byte database could require upto
|
||||||
|
300 bytes of storage.
|
||||||
|
|
||||||
|
* ``compression_size_percent``:
|
||||||
|
If this option is set to be -1 (the default value), all the output
|
||||||
|
files will follow compression type specified.
|
||||||
|
|
||||||
|
If this option is not negative, we will try to make sure compressed
|
||||||
|
size is just above this value. In normal cases, at least this
|
||||||
|
percentage of data will be compressed.
|
||||||
|
|
||||||
|
When we are compacting to a new file, here is the criteria whether
|
||||||
|
it needs to be compressed: assuming here are the list of files sorted
|
||||||
|
by generation time: ``A1...An B1...Bm C1...Ct``
|
||||||
|
where ``A1`` is the newest and ``Ct`` is the oldest, and we are going
|
||||||
|
to compact ``B1...Bm``, we calculate the total size of all the files
|
||||||
|
as total_size, as well as the total size of ``C1...Ct`` as
|
||||||
|
``total_C``, the compaction output file will be compressed if
|
||||||
|
``total_C / total_size < this percentage``.
|
||||||
|
Default: -1
|
||||||
|
|
||||||
|
* ``stop_style``:
|
||||||
|
The algorithm used to stop picking files into a single compaction.
|
||||||
|
Can be either ``"similar_size"`` or ``"total_size"``.
|
||||||
|
|
||||||
|
* ``similar_size``: Pick files of similar size.
|
||||||
|
* ``total_size``: Total size of picked files is greater than next file.
|
||||||
|
|
||||||
|
Default: ``"total_size"``
|
||||||
|
|
||||||
|
For setting options, just assign a dict with the fields to set.
|
||||||
|
It is allowed to omit keys in this dict. Missing keys are just not set
|
||||||
|
to the underlying options object.
|
||||||
|
|
||||||
|
This example just changes the stop_style and leaves the other options
|
||||||
|
untouched. ::
|
||||||
|
|
||||||
|
opts = rocksdb.Options()
|
||||||
|
opts.compaction_options_universal = {'stop_style': 'similar_size'}
|
||||||
|
|
||||||
.. py:attribute:: filter_deletes
|
.. py:attribute:: filter_deletes
|
||||||
|
|
||||||
Use KeyMayExist API to filter deletes when this is true.
|
Use KeyMayExist API to filter deletes when this is true.
|
||||||
|
|
|
@ -29,7 +29,7 @@ A more production ready open can look like this ::
|
||||||
It assings a cache of 2.5G, uses a bloom filter for faster lookups and keeps
|
It assings a cache of 2.5G, uses a bloom filter for faster lookups and keeps
|
||||||
more data (64 MB) in memory before writting a .sst file.
|
more data (64 MB) in memory before writting a .sst file.
|
||||||
|
|
||||||
About Bytes and Unicode
|
About Bytes And Unicode
|
||||||
========================
|
========================
|
||||||
|
|
||||||
RocksDB stores all data as uninterpreted *byte strings*.
|
RocksDB stores all data as uninterpreted *byte strings*.
|
||||||
|
@ -272,12 +272,12 @@ The two arguments are the db_dir and wal_dir, which are mostly the same. ::
|
||||||
backup.restore_latest_backup("test.db", "test.db")
|
backup.restore_latest_backup("test.db", "test.db")
|
||||||
|
|
||||||
|
|
||||||
Change Memtable or SST implementations
|
Change Memtable Or SST Implementations
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
As noted here :ref:`memtable_factories_label`, RocksDB offers different implementations for the memtable
|
As noted here :ref:`memtable_factories_label`, RocksDB offers different implementations for the memtable
|
||||||
representation. Per default :py:class:`rocksdb.SkipListMemtableFactory` is used,
|
representation. Per default :py:class:`rocksdb.SkipListMemtableFactory` is used,
|
||||||
but changeing it to a different one is veary easy.
|
but changing it to a different one is veary easy.
|
||||||
|
|
||||||
Here is an example for HashSkipList-MemtableFactory.
|
Here is an example for HashSkipList-MemtableFactory.
|
||||||
Keep in mind: To use the hashed based MemtableFactories you must set
|
Keep in mind: To use the hashed based MemtableFactories you must set
|
||||||
|
@ -325,3 +325,20 @@ Here is an example how to use one of the 'PlainTables'. ::
|
||||||
opts.create_if_missing = True
|
opts.create_if_missing = True
|
||||||
|
|
||||||
db = rocksdb.DB("test.db", opts)
|
db = rocksdb.DB("test.db", opts)
|
||||||
|
|
||||||
|
Change Compaction Style
|
||||||
|
=======================
|
||||||
|
|
||||||
|
RocksDB has a compaction algorithm called *universal*. This style typically
|
||||||
|
results in lower write amplification but higher space amplification than
|
||||||
|
Level Style Compaction. See here for more details,
|
||||||
|
https://github.com/facebook/rocksdb/wiki/Rocksdb-Architecture-Guide#multi-threaded-compactions
|
||||||
|
|
||||||
|
Here is an example to switch to *universal style compaction*. ::
|
||||||
|
|
||||||
|
opts = rocksdb.Options()
|
||||||
|
opts.compaction_style = "universal"
|
||||||
|
opts.compaction_options_universal = {"min_merge_width": 3}
|
||||||
|
|
||||||
|
See here for more options on *universal style compaction*,
|
||||||
|
:py:attr:`rocksdb.Options.compaction_options_universal`
|
||||||
|
|
|
@ -26,6 +26,15 @@ cimport backup
|
||||||
cimport env
|
cimport env
|
||||||
cimport table_factory
|
cimport table_factory
|
||||||
cimport memtablerep
|
cimport memtablerep
|
||||||
|
cimport universal_compaction
|
||||||
|
|
||||||
|
# Enums are the only exception for direct imports
|
||||||
|
# Their name als already unique enough
|
||||||
|
from universal_compaction cimport kCompactionStopStyleSimilarSize
|
||||||
|
from universal_compaction cimport kCompactionStopStyleTotalSize
|
||||||
|
|
||||||
|
from options cimport kCompactionStyleLevel
|
||||||
|
from options cimport kCompactionStyleUniversal
|
||||||
|
|
||||||
from slice_ cimport Slice
|
from slice_ cimport Slice
|
||||||
from status cimport Status
|
from status cimport Status
|
||||||
|
@ -1040,6 +1049,71 @@ cdef class Options(object):
|
||||||
def __set__(self, value):
|
def __set__(self, value):
|
||||||
self.opts.verify_checksums_in_compaction = value
|
self.opts.verify_checksums_in_compaction = value
|
||||||
|
|
||||||
|
property compaction_style:
|
||||||
|
def __get__(self):
|
||||||
|
if self.opts.compaction_style == kCompactionStyleLevel:
|
||||||
|
return 'level'
|
||||||
|
if self.opts.compaction_style == kCompactionStyleUniversal:
|
||||||
|
return 'universal'
|
||||||
|
raise Exception("Unknown compaction_style")
|
||||||
|
|
||||||
|
def __set__(self, str value):
|
||||||
|
if value == 'level':
|
||||||
|
self.opts.compaction_style = kCompactionStyleLevel
|
||||||
|
elif value == 'universal':
|
||||||
|
self.opts.compaction_style = kCompactionStyleUniversal
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown compaction style")
|
||||||
|
|
||||||
|
property compaction_options_universal:
|
||||||
|
def __get__(self):
|
||||||
|
cdef universal_compaction.CompactionOptionsUniversal uopts
|
||||||
|
cdef dict ret_ob = {}
|
||||||
|
|
||||||
|
uopts = self.opts.compaction_options_universal
|
||||||
|
|
||||||
|
ret_ob['size_ratio'] = uopts.size_ratio
|
||||||
|
ret_ob['min_merge_width'] = uopts.min_merge_width
|
||||||
|
ret_ob['max_merge_width'] = uopts.max_merge_width
|
||||||
|
ret_ob['max_size_amplification_percent'] = uopts.max_size_amplification_percent
|
||||||
|
ret_ob['compression_size_percent'] = uopts.compression_size_percent
|
||||||
|
|
||||||
|
if uopts.stop_style == kCompactionStopStyleSimilarSize:
|
||||||
|
ret_ob['stop_style'] = 'similar_size'
|
||||||
|
elif uopts.stop_style == kCompactionStopStyleTotalSize:
|
||||||
|
ret_ob['stop_style'] = 'total_size'
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown compaction style")
|
||||||
|
|
||||||
|
return ret_ob
|
||||||
|
|
||||||
|
def __set__(self, dict value):
|
||||||
|
cdef universal_compaction.CompactionOptionsUniversal* uopts
|
||||||
|
uopts = cython.address(self.opts.compaction_options_universal)
|
||||||
|
|
||||||
|
if 'size_ratio' in value:
|
||||||
|
uopts.size_ratio = value['size_ratio']
|
||||||
|
|
||||||
|
if 'min_merge_width' in value:
|
||||||
|
uopts.min_merge_width = value['min_merge_width']
|
||||||
|
|
||||||
|
if 'max_merge_width' in value:
|
||||||
|
uopts.max_merge_width = value['max_merge_width']
|
||||||
|
|
||||||
|
if 'max_size_amplification_percent' in value:
|
||||||
|
uopts.max_size_amplification_percent = value['max_size_amplification_percent']
|
||||||
|
|
||||||
|
if 'compression_size_percent' in value:
|
||||||
|
uopts.compression_size_percent = value['compression_size_percent']
|
||||||
|
|
||||||
|
if 'stop_style' in value:
|
||||||
|
if value['stop_style'] == 'similar_size':
|
||||||
|
uopts.stop_style = kCompactionStopStyleSimilarSize
|
||||||
|
elif value['stop_style'] == 'total_size':
|
||||||
|
uopts.stop_style = kCompactionStopStyleTotalSize
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown compaction style")
|
||||||
|
|
||||||
property filter_deletes:
|
property filter_deletes:
|
||||||
def __get__(self):
|
def __get__(self):
|
||||||
return self.opts.filter_deletes
|
return self.opts.filter_deletes
|
||||||
|
|
|
@ -13,8 +13,13 @@ from snapshot cimport Snapshot
|
||||||
from slice_transform cimport SliceTransform
|
from slice_transform cimport SliceTransform
|
||||||
from table_factory cimport TableFactory
|
from table_factory cimport TableFactory
|
||||||
from memtablerep cimport MemTableRepFactory
|
from memtablerep cimport MemTableRepFactory
|
||||||
|
from universal_compaction cimport CompactionOptionsUniversal
|
||||||
|
|
||||||
cdef extern from "rocksdb/options.h" namespace "rocksdb":
|
cdef extern from "rocksdb/options.h" namespace "rocksdb":
|
||||||
|
ctypedef enum CompactionStyle:
|
||||||
|
kCompactionStyleLevel
|
||||||
|
kCompactionStyleUniversal
|
||||||
|
|
||||||
ctypedef enum CompressionType:
|
ctypedef enum CompressionType:
|
||||||
kNoCompression
|
kNoCompression
|
||||||
kSnappyCompression
|
kSnappyCompression
|
||||||
|
@ -101,8 +106,8 @@ cdef extern from "rocksdb/options.h" namespace "rocksdb":
|
||||||
cpp_bool use_adaptive_mutex
|
cpp_bool use_adaptive_mutex
|
||||||
uint64_t bytes_per_sync
|
uint64_t bytes_per_sync
|
||||||
cpp_bool verify_checksums_in_compaction
|
cpp_bool verify_checksums_in_compaction
|
||||||
# TODO: CompactionStyle compaction_style
|
CompactionStyle compaction_style
|
||||||
# TODO: CompactionOptionsUniversal compaction_options_universal
|
CompactionOptionsUniversal compaction_options_universal
|
||||||
cpp_bool filter_deletes
|
cpp_bool filter_deletes
|
||||||
uint64_t max_sequential_skip_in_iterations
|
uint64_t max_sequential_skip_in_iterations
|
||||||
shared_ptr[MemTableRepFactory] memtable_factory
|
shared_ptr[MemTableRepFactory] memtable_factory
|
||||||
|
|
|
@ -69,3 +69,37 @@ class TestOptions(unittest.TestCase):
|
||||||
opts.table_factory = rocksdb.BlockBasedTableFactory()
|
opts.table_factory = rocksdb.BlockBasedTableFactory()
|
||||||
opts.table_factory = rocksdb.PlainTableFactory()
|
opts.table_factory = rocksdb.PlainTableFactory()
|
||||||
opts.table_factory = rocksdb.TotalOrderPlainTableFactory()
|
opts.table_factory = rocksdb.TotalOrderPlainTableFactory()
|
||||||
|
|
||||||
|
def test_compaction_style(self):
|
||||||
|
opts = rocksdb.Options()
|
||||||
|
self.assertEqual('level', opts.compaction_style)
|
||||||
|
|
||||||
|
opts.compaction_style = 'universal'
|
||||||
|
self.assertEqual('universal', opts.compaction_style)
|
||||||
|
|
||||||
|
opts.compaction_style = 'level'
|
||||||
|
self.assertEqual('level', opts.compaction_style)
|
||||||
|
|
||||||
|
with self.assertRaisesRegexp(Exception, 'Unknown compaction style'):
|
||||||
|
opts.compaction_style = 'foo'
|
||||||
|
|
||||||
|
def test_compaction_opts_universal(self):
|
||||||
|
opts = rocksdb.Options()
|
||||||
|
uopts = opts.compaction_options_universal
|
||||||
|
self.assertEqual(-1, uopts['compression_size_percent'])
|
||||||
|
self.assertEqual(200, uopts['max_size_amplification_percent'])
|
||||||
|
self.assertEqual('total_size', uopts['stop_style'])
|
||||||
|
self.assertEqual(1, uopts['size_ratio'])
|
||||||
|
self.assertEqual(2, uopts['min_merge_width'])
|
||||||
|
self.assertGreaterEqual(4294967295, uopts['max_merge_width'])
|
||||||
|
|
||||||
|
new_opts = {'stop_style': 'similar_size', 'max_merge_width': 30}
|
||||||
|
opts.compaction_options_universal = new_opts
|
||||||
|
uopts = opts.compaction_options_universal
|
||||||
|
|
||||||
|
self.assertEqual(-1, uopts['compression_size_percent'])
|
||||||
|
self.assertEqual(200, uopts['max_size_amplification_percent'])
|
||||||
|
self.assertEqual('similar_size', uopts['stop_style'])
|
||||||
|
self.assertEqual(1, uopts['size_ratio'])
|
||||||
|
self.assertEqual(2, uopts['min_merge_width'])
|
||||||
|
self.assertEqual(30, uopts['max_merge_width'])
|
||||||
|
|
15
rocksdb/universal_compaction.pxd
Normal file
15
rocksdb/universal_compaction.pxd
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
cdef extern from "rocksdb/universal_compaction.h" namespace "rocksdb":
|
||||||
|
|
||||||
|
ctypedef enum CompactionStopStyle:
|
||||||
|
kCompactionStopStyleSimilarSize
|
||||||
|
kCompactionStopStyleTotalSize
|
||||||
|
|
||||||
|
cdef cppclass CompactionOptionsUniversal:
|
||||||
|
CompactionOptionsUniversal()
|
||||||
|
|
||||||
|
unsigned int size_ratio
|
||||||
|
unsigned int min_merge_width
|
||||||
|
unsigned int max_merge_width
|
||||||
|
unsigned int max_size_amplification_percent
|
||||||
|
int compression_size_percent
|
||||||
|
CompactionStopStyle stop_style
|
Loading…
Reference in a new issue