All Projects → VictorScherbakov → DataTanker

VictorScherbakov / DataTanker

Licence: MIT license
Embedded persistent key-value store for .NET. Pure C# code.

Programming Languages

C#
18002 projects

Projects that are alternatives of or similar to DataTanker

Keyvast
KeyVast - A key value store
Stars: ✭ 33 (-37.74%)
Mutual labels:  nosql, key-value
docs
Source code of the ArangoDB online documentation
Stars: ✭ 18 (-66.04%)
Mutual labels:  nosql, key-value
Ejdb
🏂 EJDB 2.0 — Embeddable JSON Database engine C library. Simple XPath like query language (JQL). Websockets / Android / iOS / React Native / Flutter / Java / Dart / Node.js bindings. Docker image.
Stars: ✭ 1,187 (+2139.62%)
Mutual labels:  nosql, key-value
Dynomite
A generic dynamo implementation for different k-v storage engines
Stars: ✭ 3,830 (+7126.42%)
Mutual labels:  nosql, key-value
Nuster
A high performance HTTP proxy cache server and RESTful NoSQL cache server based on HAProxy
Stars: ✭ 1,825 (+3343.4%)
Mutual labels:  nosql, key-value
Libmdbx
One of the fastest embeddable key-value ACID database without WAL. libmdbx surpasses the legendary LMDB in terms of reliability, features and performance.
Stars: ✭ 729 (+1275.47%)
Mutual labels:  nosql, key-value
Unqlite
An Embedded NoSQL, Transactional Database Engine
Stars: ✭ 1,583 (+2886.79%)
Mutual labels:  nosql, key-value
keyval-resource
a resource that passes key values between jobs
Stars: ✭ 39 (-26.42%)
Mutual labels:  key-value, keyvalue
Arangodb
🥑 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.
Stars: ✭ 11,880 (+22315.09%)
Mutual labels:  nosql, key-value
Ardb
A redis protocol compatible nosql, it support multiple storage engines as backend like Google's LevelDB, Facebook's RocksDB, OpenLDAP's LMDB, PerconaFT, WiredTiger, ForestDB.
Stars: ✭ 1,707 (+3120.75%)
Mutual labels:  nosql, key-value
Bitnami Docker Redis
Bitnami Redis Docker Image
Stars: ✭ 317 (+498.11%)
Mutual labels:  nosql, key-value
simpledbm
SimpleDBM is an Open Source Multi-Threaded Embeddable Transactional Database Engine in Java.
Stars: ✭ 51 (-3.77%)
Mutual labels:  nosql, btree
elara
Elara DB is an easy to use, lightweight key-value database that can also be used as a fast in-memory cache. Manipulate data structures in-memory, encrypt database files and export data. 🎯
Stars: ✭ 93 (+75.47%)
Mutual labels:  nosql, key-value
Xodus
Transactional schema-less embedded database used by JetBrains YouTrack and JetBrains Hub.
Stars: ✭ 864 (+1530.19%)
Mutual labels:  nosql, key-value
quitsies
A persisted drop-in replacement for Memcached, respecting the rules of quitsies.
Stars: ✭ 16 (-69.81%)
Mutual labels:  nosql, key-value
Cog
A Persistent Embedded Graph Database for Python
Stars: ✭ 90 (+69.81%)
Mutual labels:  nosql, key-value
radix
Golang radix tree implementation
Stars: ✭ 30 (-43.4%)
Mutual labels:  radix-tree, radix
Gkvdb
[mirror] Go语言开发的基于DRH(Deep-Re-Hash)深度哈希分区算法的高性能高可用Key-Value嵌入式事务数据库。基于纯Go语言实现,具有优异的跨平台性,良好的高可用及文件IO复用设计,高效的底层数据库文件操作性能,支持原子操作、批量操作、事务操作、多表操作、多表事务、随机遍历等特性。
Stars: ✭ 109 (+105.66%)
Mutual labels:  nosql, key-value
Hive
Lightweight and blazing fast key-value database written in pure Dart.
Stars: ✭ 2,681 (+4958.49%)
Mutual labels:  nosql, key-value
Iowow
The skiplist based persistent key/value storage engine
Stars: ✭ 206 (+288.68%)
Mutual labels:  nosql, key-value

DataTanker NuGet Pre Release AppVeyor license

Embedded persistent key-value store for .NET. Pure C# code, B+Tree and RadixTree features, MIT License.

Features

  • simple and lightweight
  • fast enough
  • variable length values
  • concurrent access
  • atomic operations
  • user-defined serialization routines

Performance

I did not perform detailed comparison with competitors. The benchmarks provided by competing developers are usually biased and misleading. However, I provide some performance values. To ensure reliability you can run the Performance.exe test util.

  • sequential insert and read 1 000 000 integer keys with 20-byte values - 16sec
  • insert 100 000 integer keys with large values (from 200 to 5000 bytes) - 12sec
  • perform 1 000 000 random operations on 100 000 key set - 8sec

Feedback

Any feedback is welcome. Feel free to ask a question, send a bug report or feature request.

Usage

    [Serializable]
    class Employee
    {
        public int Id { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }

        public decimal Salary { get; set; }

        public override string ToString()
        {
            return $"Id: {Id}, Name: {FirstName} {LastName}, Salary {Salary}";
        }
    }

    class Program
    {
        public static void SaveEmployee(StorageFactory factory, Employee emp)
        {
            // create storage with integer keys and Employee values
            using (var storage = factory.CreateBPlusTreeStorage<int, Employee>(BPlusTreeStorageSettings.Default(sizeof(int))))
            {
                storage.OpenOrCreate(Directory.GetCurrentDirectory());
                storage.Set(emp.Id, emp);
            }
        }

        public static Employee ReadEmployee(StorageFactory factory, int id)
        {
            using (var storage = factory.CreateBPlusTreeStorage<int, Employee>(BPlusTreeStorageSettings.Default(sizeof(int))))
            {
                storage.OpenOrCreate(Directory.GetCurrentDirectory());
                return storage.Get(id);
            }
        }

        static void Main(string[] args)
        {
            var emp1 = new Employee { Id = 1, FirstName = "John", LastName = "Smith", Salary = new decimal(100000) };
            var emp2 = new Employee { Id = 2, FirstName = "Steve", LastName = "Barret", Salary = new decimal(150000) };

            var factory = new StorageFactory();
            SaveEmployee(factory, emp1);
            SaveEmployee(factory, emp2);

            var emp3 = ReadEmployee(factory, 1);
            var emp4 = ReadEmployee(factory, 2);

            Console.WriteLine(emp3);
            Console.WriteLine(emp4);
        }
    }

Where should I use it?

In cases where the data storage is required, but the file system is not suitable, and large DBMSs have too much overhead. For example: query caches, message queues, large amount of temporary data etc.

Access methods

Now DataTanker support two access methods: B+Tree and RadixTree. B+Tree have a good fill factor and demonstrates best performance on small sized keys without hierarchy. RadixTree is well suited for storing long hierarchical keys like filepaths.

Keys

Existing key always corresponds to a single value. Integer keys have a built-in serializers, so you don't have to worry about it. Here is a list of types having built-in serializers for keys: Int32, Int64, UInt32, UInt64, Double, Single, DateTime, String

To use any other type just implement serialize/deserialize routines for it. Other features depend on the access method.

B+Tree storage

Binary representation of key has size limit, which depends on selected page size. Each index page must have a place for at least three keys in binary format. This requirement follows from the B+Tree properties. Keys must implement the IComparable interface.

RadixTree storage

Key has no reasonable size limit. Implementation of IComparable and IEquatable interfaces are not required. However, key set is ordered by comparing byte sequences of serialized keys. So, care must be taken to the serialization.

Values

Values ​​have no restrictions on type. The size of value have no reasonable limit. In most cases large values will be limited by the drive size.

The values use BinaryFormatter serialization by default. In this case, classes should be marked with [Serializable] attribute. However, serialization using BinaryFormatter implies too much overhead in time and disc space. You can easily provide your own serializer based on BSON, Protobuf or other appropriate proto. All you have to do is just implement two methods like this:

    public class ProtobufSerializer<T> : ISerializer<T>
    {
        public T Deserialize(byte[] bytes)
        {
            using (var ms = new MemoryStream(bytes))
            {
                var result = Serializer.Deserialize(typeof(T), ms);
                return (T)result;
            }
        }

        public byte[] Serialize(T obj)
        {
            using (var ms = new MemoryStream())
            {
                Serializer.Serialize(ms, obj);
                return ms.ToArray();
            }
        }
    }

What about ACID?

  • atomicity - any single operation is atomic
  • consistency - any operation transfer storage to a consistent state
  • isolation - all single operations are isolated from each other
  • durability - durability of updates is achieved by calling storage.Flush() method

However, transactions in the sense of unit-of-work are not supported. Thus, we can not produce long series of changes, and then rollback or commit them.

Create storage

var factory = new StorageFactory();
var storage = factory.CreateBPlusTreeStorage<int, string>( 
                    BitConverter.GetBytes,               // key serialization
                    p => BitConverter.ToInt32(p, 0),     // key deserialization
                    p => Encoding.UTF8.GetBytes(p),      // value serialization
                    p => Encoding.UTF8.GetString(p),     // value deserialization
                    BPlusTreeStorageSettings.Default(sizeof(int)));

Here we provide types for keys and values, serialization methods and storage settings. Please note that storage instance implements IDisposible interface. We should use storage instance in using block or call Dispose() in appropriate time to successfully shutdown.

To create storage on disk or open an already existing storage use one of

  • storage.OpenExisting(string path)
  • storage.CreateNew(string path)
  • storage.OpenOrCreate(string path)

On-disk storage files:

  • info - xml-file containing common information about storage
  • pagemap - the mapping of virtual page addresses to on-disk offsets
  • storage - the main storage file containing keys, values and index data

Operations

Storage instance returned by the CreateBPlusTreeStorage() method supports following operations:

  • Get(TKey key) - gets the value by its key
  • Set(TKey key, TValue value) - inserts or updates key value pair
  • Remove(TKey key) - removes key-value pair by key
  • Exists(TKey key) - cheks if key-value pair exists
  • GetRawDataLength(TKey key) - retrieves the length (in bytes) of binary representation of the value referenced by the specified key
  • GetRawDataSegment(TKey key, long startIndex, long endIndex) - retrieves a segment of binary representation of the value referenced by the specified key
  • Min() - gets the minimal key
  • Max() - gets the maximal key
  • PreviousTo(TKey key) - gets the key previous to the specified key, the existence of the specified key is not required
  • NextTo(TKey key) - gets the key next to the specified key, the existence of the specified key is not required
  • MinValue() - gets a value corresponing to the minimal key
  • MaxValue() - gets the value corresponing to the maximal key
  • Count() - computes the count of key-value pairs
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].