NutsDB is a transactional key/value store database written in Go. All transactions executed in NutsDB are fully serializable. Transactions are categorized into types based on their level of access. In other words, there are read-only and read-write transactions. Multiple read-only transactions are allowed at the same time, and they could, of course, read values from a bucket using a key or traverse the buckets to find a range of key-value pairs. However, only one read-write transaction is allowed at the same time and it could read, update and delete from the data storage.
The DBMS supports different data structures, such as list, set and sorted set.
NutsDB is developed mainly by a Chinese programmer named Jiajun Xu. The motivation is that the author wants a simpler, faster, and more persistent key/value store database system. The project started in December 2018. It is not derived or forked from any other database systems, but it does use Bitcask as its underlying the key/value store. The author further optimized it using B+ tree as index to compensate Bitcask's limitations in range and prefix scanning.
NutsDB supports key/value storage and all key/value pairs are stored in buckets, in which all keys must be unique. However, the same keys are allowed in different buckets.
NutsDB adopted Bitcask, which is a log-structure key/value storage system. All the updates and write operations are appended to the end of the log. It does not support random read and write. Rather, to delete a value, it simply append a new record marking the old value as deleted. To update, it will directly append the new value, and the system will use the newest value always, according to the timestamp. Therefore, the old values, even though still exist in the file, are simply discarded. An obvious drawback is that the log file will become huge after running for a while or the amount of data processed is huge, therefore causing the launch of NutsDB to be slow since it needs to process the log file first.
It also supports other data structures: list, set and sorted set. The APIs follows Redis commands.
NutsDB uses Bitcask as a key/value store. This model is fast and reliable, but it also introduces some shortcomings in terms of the performance. Traditional key/value store does not support range and prefix searching. To perform a scan, it needs to query the keys one by one, which is not desirable. Therefore, the author optimized it by incorporating B+ tree indexing to support easy scan and searching. With B+ tree, range query and prefix matching could be performed with ease.
Upon launch, there are three modes of indexing to read the log files to reconstruct the database, varying in the amount of memory required. The first mode, which caches indices and values entirely in memory at startup, is the default mode, which is the most memory consuming but the most efficient one. This mode is most suitable for situations when the memory is not an issue. The second mode, which only caches indices, is less memory consuming by storing values on disk and use the index to find the offsets of the values. This is most suitable when the number of values is much more than that of the keys. The last mode, which uses multilevel indexing, is the most memory efficient but the slowest one. since it does not requires too much memory but issues a lot of file seeks.
Since NutsDB adopted Bitcask, it uses logging to record every single operation that made changes to the database.
Insert Insertion of a data key/value pair is simply appending to the end of the logging file. If the log file results in a huge size (after running for a long time or a transaction that involves too many writes), it will start a new file but keep the old files.
Delete Deletion is also simply appending to the end of the logging file by marking that value to be deleted
.
Update Update is also simply appending, and the database will keep track of the time stamp, since only the latest value is the valid ones. In other words, the values with old timestamps, even though still present in the logging file, will be discarded.
Merge Logs Eventually, the log file will be too huge and NutsDB will initiate a merge
operation to merge all the log files. It will remove all the duplicates, delete the values that are marked as deleted, and keep only the newest value with the latest timestamp during updates.
For ordinary key/value queries, NutsDB used its own APIs to insert, delete and update key/value pairs in the buckets. Since all the operations are monitored and organized by transactions, the API calls are made by transactions, or Tx. Using the transaction, one could put (insert), get and delete from the database with ease using the given APIs. These three operations are the main ones that could be used to query from the database using key/value pairs.
NutsDB also supports Prefix Scan and Range Scan, and further user has the option to limit the number of entries being returned.
For manipulating data structures such as List, Set and OrderedSet, NutsDB follows the traditions in the Redis commands.
NutsDB has different modes of storage. In all modes, the log files will be used, which is on disk, to reconstruct database.
For the first mode, it is the pure in-memory, and it is the most efficient one. However, memory usage would be the bottleneck here, especially when you don't have enough.
The second mode is partially in-memory and partially disk-oriented. Under this mode, NutsDB will store the values on the disk and use the key to get the offset for the corresponding values. It is more memory efficient, but it is slower. This is the most suitable when the values significantly outnumber the keys.
The third mode adopted multi-level indexing on disk, so it is understandable that while it is the most memory efficient, it is the slowest.
Overall, the second and the third mode supports larger-than-memory databases, since they will be querying from the disk.
NutsDB support all kinds of Views using the View APIs, In fact, there are only two kinds of operations supported, View and Update. The first one is responsible for all Read-Only tasks and the latter Read-Write tasks. To get a View, one could simply use Get discussed in the Query Interfaces to do a key/value query. Range Scan or PrefixScan are also supported. Finally, to View the whole database, one could get all the key/value pairs from all the buckets with one simple API call.
https://github.com/xujiajun/nutsdb
Jiajun Xu
2018