ZoneTree
ZoneTree is a persistent, high-performance, transactional, and ACID-compliant ordered key-value database for .NET. It operates seamlessly both in-memory and on local/cloud storage, making it an ideal choice for a wide range of applications requiring efficient data management.
Install / Use
/learn @koculu/ZoneTreeREADME

ZoneTree
ZoneTree is a persistent, high-performance, transactional, and ACID-compliant ordered key-value database for .NET. It operates seamlessly both in-memory and on local/cloud storage, making it an ideal choice for a wide range of applications requiring efficient data management.
Table of Contents
- Why Choose ZoneTree?
- Performance
- Getting Started
- Maintaining the LSM Tree
- Handling Deletions
- Data Iteration
- Transaction Support
- Feature Highlights
- ZoneTree.FullTextSearch
- Documentation
- Contributing
- License
Why Choose ZoneTree?
-
Pure C# Implementation: ZoneTree is developed entirely in C#, ensuring seamless integration and deployment within .NET ecosystems without external dependencies.
-
Exceptional Performance: Demonstrates performance several times faster than Facebook's RocksDB and hundreds of times faster than SQLite. Optimized for both speed and efficiency.
-
Data Durability and Crash Resilience: Provides optional durability features that protect data against crashes and power outages, ensuring data integrity under all circumstances.
-
Transactional and ACID-Compliant: Supports both transactional and non-transactional access with full ACID guarantees, offering flexibility for various application requirements.
-
Embeddable Database Engine: Easily embed ZoneTree into applications, eliminating the overhead of maintaining or shipping separate database products.
-
Scalability: Capable of handling small to massive datasets, allowing the creation of both scalable and non-scalable databases tailored to specific needs.
Performance
ZoneTree sets new standards in database performance, showcasing remarkable speeds in data insertion, loading, and iteration operations.
Benchmark Results
- 100 Million integer key-value pairs inserted in 20 seconds using WAL mode = NONE.
- Loading database with 100 million integer key-value pairs takes 812 milliseconds.
- Iterating over 100 million key-value pairs completes in 24 seconds.
Detailed Benchmark for Various Modes:
| Insert Benchmarks | 1M | 2M | 3M | 10M | | ----------------------------------------- | ------------- | -------- | -------- | -------- | | int-int ZoneTree async-compressed WAL | 267 ms | 464 ms | 716 ms | 2693 ms | | int-int ZoneTree sync-compressed WAL | 834 ms | 1617 ms | 2546 ms | 8642 ms | | int-int ZoneTree sync WAL | 2742 ms | 5533 ms | 8242 ms | 27497 ms | | | | | | | | str-str ZoneTree async-compressed WAL | 892 ms | 1833 ms | 2711 ms | 9443 ms | | str-str ZoneTree sync-compressed WAL | 1752 ms | 3397 ms | 5070 ms | 19153 ms | | str-str ZoneTree sync WAL | 3488 ms | 7002 ms | 10483 ms | 38727 ms | | | | | | | | int-int RocksDb sync-compressed WAL | 8059 ms | 16188 ms | 23599 ms | 61947 ms | | str-str RocksDb sync-compressed WAL | 8215 ms | 16146 ms | 23760 ms | 72491 ms |
Benchmark Configuration:
DiskCompressionBlockSize = 10 * 1024 * 1024; // 10 MB
WALCompressionBlockSize = 32 * 1024 * 8; // 256 KB
DiskSegmentMode = DiskSegmentMode.SingleDiskSegment;
MutableSegmentMaxItemCount = 1_000_000;
ThresholdForMergeOperationStart = 2_000_000;
Additional Notes:
- ZoneTree has been tested successfully with up to 1 billion records on standard desktop computers, demonstrating stability and efficiency even with very large datasets.
Write-Ahead Log (WAL) Modes
ZoneTree offers four WAL modes to provide flexibility between performance and durability:
-
Sync Mode:
- Durability: Maximum.
- Performance: Slower write speed.
- Use Case: Ensures data is not lost in case of crashes or power cuts.
-
Sync-Compressed Mode:
- Durability: High, but slightly less than sync mode.
- Performance: Faster write speed due to compression.
- Use Case: Balances durability and performance; periodic jobs can persist decompressed tail records for added safety.
-
Async-Compressed Mode:
- Durability: Moderate.
- Performance: Very fast write speed; logs are written in a separate thread.
- Use Case: Suitable where immediate durability is less critical but performance is paramount.
-
None Mode:
- Durability: No immediate durability; relies on manual or automatic disk saves.
- Performance: Maximum possible.
- Use Case: Ideal for scenarios where performance is critical and data can be reconstructed or is not mission-critical.
Benchmark Environment
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
Processor: Intel Core i7-6850K CPU @ 3.60GHz (Skylake), 12 logical cores, 6 physical cores
Memory: 64 GB DDR4
Storage: Samsung SSD 850 EVO 1TB
Configuration: 1M mutable segment size, 2M readonly segments merge-threshold
Getting Started
ZoneTree is designed for ease of use, allowing developers to integrate and utilize its capabilities with minimal setup.
Basic Usage
using Tenray.ZoneTree;
using var zoneTree = new ZoneTreeFactory<int, string>()
.OpenOrCreate();
zoneTree.Upsert(39, "Hello ZoneTree");
Creating a Database
using Tenray.ZoneTree;
using Tenray.ZoneTree.Comparers;
using Tenray.ZoneTree.Serializers;
var dataPath = "data/mydatabase";
using var zoneTree = new ZoneTreeFactory<int, string>()
.SetComparer(new Int32ComparerAscending())
.SetDataDirectory(dataPath)
.SetKeySerializer(new Int32Serializer())
.SetValueSerializer(new Utf8StringSerializer())
.OpenOrCreate();
// Atomic (thread-safe) operation on single mutable-segment.
zoneTree.Upsert(39, "Hello ZoneTree!");
// Atomic operation across all segments.
zoneTree.TryAtomicAddOrUpdate(39, "a", (ref string x) =>
{
x += "b";
return true;
}, out var opIndex);
Maintaining the LSM Tree
Large-scale LSM Trees require periodic maintenance to ensure optimal performance and resource utilization. ZoneTree provides the IZoneTreeMaintenance interface to facilitate comprehensive maintenance tasks.
Example Usage:
using Tenray.ZoneTree;
using Tenray.ZoneTree.Maintenance;
var dataPath = "data/mydatabase";
using var zoneTree = new ZoneTreeFactory<int, string>()
.SetComparer(new Int32ComparerAscending())
.SetDataDirectory(dataPath)
.SetKeySerializer(new Int32Serializer())
.SetValueSerializer(new Utf8StringSerializer())
.OpenOrCreate();
using var maintainer = zoneTree.CreateMaintainer();
maintainer.EnableJobForCleaningInactiveCaches = true;
// Perform read/write operations.
zoneTree.Upsert(39, "Hello ZoneTree!");
// Wait for background maintenance tasks to complete.
maintainer.WaitForBackgroundThreads();
Note: For smaller datasets, maintenance tasks may not be necessary. The default maintainer allows developers to focus on core business logic without delving into LSM tree intricacies.
Handling Deletions
In Log-Structured Merge (LSM) trees, deletions are managed by upserting a key/value pair with a deletion marker. Actual data removal occurs during the compaction stage.
By default, ZoneTree assumes that default values indicate deletion. This behavior can be customized by defining specific deletion flags or disabling deletions entirely using the DisableDeletion method.
Using an Integer Deletion Flag
In this example, -1 is used as the deletion marker for integer values:
using Tenray.ZoneTree;
using var zoneTree = new ZoneTreeFactory<int, int>()
.SetIsDeletedDelegate((in int key, in int value) => value == -1)
.SetMarkValueDeletedDelegate((ref int value) => value = -1)
.OpenOrCreate();
// Deleting a key by setting its value to -1
zoneTree.Upsert(42, -1);
Using a Custom Struct for Deletion
For more control, define a custom structure to represent values and their deletion status:
using System.Runtime.InteropServices;
using Tenray.ZoneTree;
[StructLayout(LayoutKind.Sequential)]
struct MyDeletableV
