[](https://crates.io/crates/wedb_embed)
[](https://docs.rs/wedb_embed)
---
<a id="en"></a>
# wedb_embed : Embedded database engine providing Redis-like APIs, built on [fjall](https://github.com/fjall-rs/fjall)
- [wedb_embed : Embedded database engine providing Redis-like APIs, built on [fjall](https://github.com/fjall-rs/fjall)](#wedb_embed-embedded-database-engine-providing-redis-like-apis-built-on-fjallhttpsgithubcomfjall-rsfjall)
- [Motivation](#motivation)
- [Core Architecture & Encoding Design](#core-architecture-encoding-design)
- [1. Default DB Prefix Optimization & 1-Byte Tag](#1-default-db-prefix-optimization-1-byte-tag)
- [2. OPPV Order-Preserving Prefix Varint (`oppv_u64`)](#2-oppv-order-preserving-prefix-varint-oppv_u64)
- [3. Numerical Tenant ID Mapping & Renaming](#3-numerical-tenant-id-mapping-renaming)
- [4. Streaming Iterators](#4-streaming-iterators)
- [Usage](#usage)
- [1. Database Initialization & Configuration (`WeDb` & `Conf`)](#1-database-initialization-configuration-wedb-conf)
- [2. Multi-Tenant & Multi-DB Isolation (`Namespace`)](#2-multi-tenant-multi-db-isolation-namespace)
- [3. Key-Value & String Operations (`KvOps`)](#3-key-value-string-operations-kvops)
- [4. Hash Map (`HashOps`)](#4-hash-map-hashops)
- [5. List (`ListOps`)](#5-list-listops)
- [6. Set (`SetOps`)](#6-set-setops)
- [7. Sorted Set (`ZSetOps`)](#7-sorted-set-zsetops)
- [8. Bitmap, Bloom Filter, TimeSeries & Geo (`BitmapOps`, `BloomOps`, `TsOps`, `GeoOps`)](#8-bitmap-bloom-filter-timeseries-geo-bitmapops-bloomops-tsops-geoops)
- [Tech Stack](#tech-stack)
- [Benchmark & Performance](#benchmark-performance)
- [Environment: Ubuntu CI (GitHub Actions Runner)](#environment-ubuntu-ci-github-actions-runner)
- [WeDb (Embedded Engine) vs Redis (v8.10.1 Standalone Server) Performance Comparison](#wedb-embedded-engine-vs-redis-v8101-standalone-server-performance-comparison)
- [Regression Benchmark](#regression-benchmark)
- [Environment: macOS (Apple M2 Max)](#environment-macos-apple-m2-max)
- [WeDb (Embedded Engine) vs Redis (v8.10.1 Standalone Server) Performance Comparison](#wedb-embedded-engine-vs-redis-v8101-standalone-server-performance-comparison)
- [Regression Benchmark](#regression-benchmark)
Embedded multi-model database engine in Rust, providing Redis-compatible data structures and APIs, built on [fjall](https://github.com/fjall-rs/fjall) LSM-Tree for disk-backed persistence.
---
## Motivation
In standalone services and embedded application scenarios, running an external Redis daemon introduces process management overhead and IPC / socket communication round trips.
`wedb_embed` provides rich Redis-compatible APIs directly as an in-process Rust library:
- **In-Process Direct Calls**: Eliminates network and IPC latency by reading and writing within the host application process.
- **Rich Redis Data Models**: Supports String, Hash, List, Set, ZSet, Bitmap, JSON, Bloom/Cuckoo Filter, TimeSeries, Geo, HyperLogLog, TDigest, SortedInt, Stream (all 15 composite types).
- **Multi-Tenant & Multi-DB Isolation**: Full $2^{64}$ tenant and $2^{64}$ database capacity with atomic renaming and physical isolation.
- **Compact Binary Encoding**: Default namespace 0-byte prefix bypass, composite types encoded with 1-Byte `KeyTag` and OPPV order-preserving varints.
- **Disk Persistence & Crash Consistency**: Built on [fjall](https://github.com/fjall-rs/fjall) LSM-Tree and cross-keyspace atomic write batches for crash-safe operations.
---
## Core Architecture & Encoding Design
```mermaid
graph TD
Client["Application Code"] --> WeDb["WeDb Instance"]
WeDb --> NS["Namespace Handle<br/>(Zero-Heap Struct)"]
subgraph KeyComposer["Key Composer & Fast Encoding"]
Tag["1-Byte Fast KeyTag (#[repr(u8)])"]
OPPV["OPPV Order-Preserving Varint (1~9B)"]
DefaultBypass["Default NS 0-Prefix Bypass"]
Slot["CRC16 Slot & {hashtag}"]
end
subgraph Engine["Storage Engine Layer"]
Batch["Atomic WriteBatch (Cross-Keyspace WAL)"]
Catalog["Catalog Compact Index"]
end
subgraph Storage["Fjall LSM Partitions"]
DataKS["Data Keyspace<br/>(Payloads & Subkeys / 64KB Blocks)"]
MetaKS["Meta Keyspace<br/>(Indices, TTL & Metadata / 4KB Blocks)"]
end
WeDb --> KeyComposer
NS --> KeyComposer
KeyComposer --> Engine
Engine --> DataKS
Engine --> MetaKS
```
### 1. Default DB Prefix Optimization & 1-Byte Tag
- **Default Namespace**: Keys are stored as raw bytes `user_key` with 0-byte prefix overhead.
- **1-Byte Fast Tag (`KeyTag`)**: Composite metadata and subkey prefixes use `#[repr(u8)] KeyTag` encoding (`\x00\x01:key`), avoiding redundant string prefixes.
### 2. OPPV Order-Preserving Prefix Varint (`oppv_u64`)
- Database index and tenant numerical IDs use **OPPV (Order-Preserving Prefix Varint)** encoding:
- Databases $0 \sim 127$: Encoded in 1 byte.
- Preserves lexicographical byte order equal to numeric value order: $\forall a < b \implies \text{encode}(a) < \text{encode}(b)$.
### 3. Numerical Tenant ID Mapping & Renaming
- Tenant names map to `ns_id: u64` via global metadata.
- **Tenant Renaming (`rename_namespace`)**: Metadata mapping updated without rewriting underlying data.
### 4. Streaming Iterators
- **`WeDb::iter(&self) -> Namespaces`**: Iterates distinct tenants via Jump-Seek.
- **`Namespace::iter(&self) -> Dbs`**: Decodes OPPV catalog entries.
---
## Usage
### 1. Database Initialization & Configuration (`WeDb` & `Conf`)
```rust
use wedb_embed::{Conf, PersistMode, Result, WeDb};
fn main() -> Result<()> {
// 1. Open with custom configuration
let mut conf = Conf::new("./data/db_demo");
conf.db.flush_workers = 2; // Background flush worker threads
let db = WeDb::open_with_conf(&conf)?;
// 2. Persistence and active expiration tasks
db.persist(PersistMode::SyncAll)?; // Synchronously persist WAL and dirty pages
db.active_expire_cycle(100)?; // Trigger active key expiration sampling
Ok(())
}
```
### 2. Multi-Tenant & Multi-DB Isolation (`Namespace`)
```rust
use wedb_embed::{KvOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/multitenant_demo")?;
// Tenant-isolated handle (lightweight zero-copy struct)
let ns_apple = db.namespace("tenant_apple");
let ns_google = db.namespace("tenant_google");
ns_apple.set(b"config:theme", b"dark", &[])?;
ns_google.set(b"config:theme", b"light", &[])?;
// Physically isolated lookups
assert_eq!(ns_apple.get(b"config:theme")?.unwrap(), b"dark");
assert_eq!(ns_google.get(b"config:theme")?.unwrap(), b"light");
// Multi-DB selection (e.g. SELECT 1)
let db1 = db.select_db(1);
db1.set(b"db1_key", b"value1", &[])?;
// Iterate active databases under tenant
for db_idx in &ns_apple {
println!("Active DB: {}", db_idx);
}
// Tenant renaming
db.rename_namespace("tenant_apple", "tenant_apple_v2")?;
// Keyspace management
let key_count = ns_apple.key_count()?;
let keys = ns_apple.keys(b"config:*")?;
let exists_count = ns_apple.exists(&[b"config:theme".as_slice()])?;
ns_apple.del(&[b"config:theme".as_slice()])?;
ns_apple.clear()?; // Cascade clear all tenant data
Ok(())
}
```
### 3. Key-Value & String Operations (`KvOps`)
```rust
use wedb_embed::{KvOps, Result, WeDb, string::Set};
fn main() -> Result<()> {
let db = WeDb::open("./data/kv_demo")?;
// Basic read, write, and TTL expiration
db.set(b"site", b"webc.site", &[])?;
let val = db.get(b"site")?;
db.set_ex(b"temp_token", b"xyz", 3600)?;
// Conditional write (NX / XX)
db.set(b"lock", b"1", &[Set::Nx])?;
// Numerical increment & decrement
db.incr(b"counter")?;
db.incrby(b"counter", 10)?;
db.decrby(b"counter", 5)?;
// Batch & string operations
db.mset(&[(b"k1", b"v1"), (b"k2", b"v2")])?;
let values = db.mget(&[b"k1".as_slice(), b"k2".as_slice()])?;
let len = db.strlen(b"site")?;
let old_val = db.getset(b"site", b"new_site")?;
let del_val = db.getdel(b"site")?;
Ok(())
}
```
### 4. Hash Map (`HashOps`)
```rust
use wedb_embed::{HashOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/hash_demo")?;
// Field set & get
db.hset(b"user:100", b"name", b"Alice")?;
db.hset(b"user:100", b"age", b"20")?;
let name = db.hget(b"user:100", b"name")?;
// Batch field operations
db.hmset(b"user:100", &[(b"city", b"Beijing"), (b"role", b"Admin")])?;
let fields = db.hmget(b"user:100", &[b"name".as_slice(), b"city".as_slice()])?;
// Counter increment and introspection
db.hincrby(b"user:100", b"age", 1)?;
let exists = db.hexists(b"user:100", b"name")?;
let len = db.hlen(b"user:100")?;
let all = db.hgetall(b"user:100")?;
let keys = db.hkeys(b"user:100")?;
let vals = db.hvals(b"user:100")?;
db.hdel(b"user:100", &[b"role".as_slice()])?;
Ok(())
}
```
### 5. List (`ListOps`)
```rust
use wedb_embed::{ListOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/list_demo")?;
// Push and pop
db.lpush(b"tasks", &[b"task1".as_slice(), b"task2".as_slice()])?;
db.rpush(b"tasks", &[b"task3".as_slice()])?;
let first = db.lpop(b"tasks", 1)?;
let last = db.rpop(b"tasks", 1)?;
// Range query and trim
let len = db.llen(b"tasks")?;
let items = db.lrange(b"tasks", 0, -1)?;
db.ltrim(b"tasks", 0, 10)?;
db.lset(b"tasks", 0, b"updated_task")?;
Ok(())
}
```
### 6. Set (`SetOps`)
```rust
use wedb_embed::{Result, SetOps, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/set_demo")?;
db.sadd(b"tags", &[b"rust".as_slice(), b"database".as_slice(), b"lsm".as_slice()])?;
let is_member = db.sismember(b"tags", b"rust")?;
let count = db.scard(b"tags")?;
let members = db.smembers(b"tags")?;
db.srem(b"tags", &[b"lsm".as_slice()])?;
let popped = db.spop(b"tags", 1)?;
Ok(())
}
```
### 7. Sorted Set (`ZSetOps`)
```rust
use wedb_embed::{Result, WeDb, ZSetOps};
fn main() -> Result<()> {
let db = WeDb::open("./data/zset_demo")?;
// Add members with scores
db.zadd(b"leaderboard", &[(100.0, b"player1".as_slice()), (200.0, b"player2".as_slice())], &[])?;
db.zincrby(b"leaderboard", 50.0, b"player1")?;
// Rank and score lookups
let score = db.zscore(b"leaderboard", b"player1")?;
let rank = db.zrank(b"leaderboard", b"player1")?;
let rev_rank = db.zrevrank(b"leaderboard", b"player1")?;
// Range queries
let top = db.zrange(b"leaderboard", 0, 10, &[])?;
let count = db.zcount(b"leaderboard", 100.0, 300.0)?;
let card = db.zcard(b"leaderboard")?;
db.zrem(b"leaderboard", &[b"player1".as_slice()])?;
Ok(())
}
```
### 8. Bitmap, Bloom Filter, TimeSeries & Geo (`BitmapOps`, `BloomOps`, `TsOps`, `GeoOps`)
```rust
use wedb_embed::{BitmapOps, BloomOps, GeoOps, Result, TsOps, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/advanced_demo")?;
// Bitmap
db.setbit(b"online_users", 1001, true)?;
let is_online = db.getbit(b"online_users", 1001)?;
let online_count = db.bitcount(b"online_users", 0, -1)?;
// Bloom Filter
db.bf_add(b"filter", b"item_1")?;
let exists = db.bf_exists(b"filter", b"item_1")?;
// TimeSeries
db.ts_add(b"metrics:cpu", 1700000000000, 42.5, &[])?;
let latest = db.ts_get(b"metrics:cpu")?;
// Geo
db.geoadd(b"cities", &[(116.4074, 39.9042, b"Beijing".as_slice()), (121.4737, 31.2304, b"Shanghai".as_slice())])?;
let dist = db.geodist(b"cities", b"Beijing", b"Shanghai", None)?;
Ok(())
}
```
---
## Tech Stack
- **Core Language**: Rust Edition 2024
- **Storage Engine**: `fjall` (LSM-Tree Engine)
- **JSON Engine**: `sonic-rs` (SIMD JSON Parser)
- **Hash Algorithm**: `rapidhash` (Non-Cryptographic Hash)
- **Serialization**: `bitcode` (Binary Codec)
- **Compact String**: `hipstr`
- **Time Utilities**: `coarsetime`, `ts_`
- **Error Handling**: `thiserror`
---
## Benchmark & Performance
### Environment: Ubuntu CI (GitHub Actions Runner)
- **CPU**: `Intel(R) Xeon(R) 6973P-C (4 Cores)`
- **Memory**: `15.6 GB`
- **Platform**: `Ubuntu 24.04.4 LTS (Linux 6.17.0-1022-azure) (x86_64)`
- **Compiler**: `rustc 1.98.0 (88d9e12ae 2026-08-18)`
#### WeDb (Embedded Engine) vs Redis (v8.10.1 Standalone Server) Performance Comparison
> **Benchmark Notes**:
> - WeDb: In-process direct call, LSM disk persistence (WAL journal)
> - Redis: Standalone service, AOF persistence enabled (appendfsync everysec), Unix Domain Socket
| Command | WeDb P95 Latency | WeDb Est. Throughput | Redis P95 Latency | Redis Est. Throughput | WeDb Speedup |
| :--- | :--- | :--- | :--- | :--- | :--- |
| SET | 6.009 µs | 1.197 M ops/s | 63.3 µs | 0.021 M ops/s | $\color{#1a7f37}\text{57.7x}$ |
| GET | 267.8 ns | 4.013 M ops/s | 55.29 µs | 0.021 M ops/s | $\color{#1a7f37}\text{192.5x}$ |
| MSET | 12.44 µs | 0.249 M ops/s | 69.21 µs | 0.031 M ops/s | $\color{#1a7f37}\text{8.0x}$ |
| MGET | 3.295 µs | 0.527 M ops/s | 49.88 µs | 0.021 M ops/s | $\color{#1a7f37}\text{25.1x}$ |
| INCRBY | 8.867 µs | 0.987 M ops/s | 79.99 µs | 0.021 M ops/s | $\color{#1a7f37}\text{47.9x}$ |
| HSET | 11.27 µs | 0.663 M ops/s | 55.14 µs | 0.021 M ops/s | $\color{#1a7f37}\text{32.3x}$ |
| HGET | 1.425 µs | 1.767 M ops/s | 63.48 µs | 0.021 M ops/s | $\color{#1a7f37}\text{84.8x}$ |
| LPUSH | 12.19 µs | 0.316 M ops/s | 81.02 µs | 0.020 M ops/s | $\color{#1a7f37}\text{15.5x}$ |
| LPOP | 3.307 µs | 0.509 M ops/s | 51.65 µs | 0.020 M ops/s | $\color{#1a7f37}\text{25.0x}$ |
| SADD | 9.665 µs | 0.682 M ops/s | 69.82 µs | 0.039 M ops/s | $\color{#1a7f37}\text{17.4x}$ |
| SISMEMBER | 579.3 ns | 1.865 M ops/s | 33.42 µs | 0.052 M ops/s | $\color{#1a7f37}\text{35.6x}$ |
| ZADD | 12.26 µs | 0.527 M ops/s | 95.59 µs | 0.021 M ops/s | $\color{#1a7f37}\text{25.2x}$ |
| ZRANGE | 9.29 µs | 0.375 M ops/s | 34.76 µs | 0.043 M ops/s | $\color{#1a7f37}\text{8.8x}$ |
| DEL | 8.569 µs | 0.737 M ops/s | 56.91 µs | 0.021 M ops/s | $\color{#1a7f37}\text{35.9x}$ |
| EXISTS | 3.642 µs | 2.391 M ops/s | 53.85 µs | 0.020 M ops/s | $\color{#1a7f37}\text{117.8x}$ |
#### Regression Benchmark
| Command | P95 Latency | Est. Throughput | vs Baseline (%) |
| :--- | :--- | :--- | :--- |
| BITCOUNT | 2.912 µs | 2.100 M ops/s | $\color{#0969da}\text{-10%}$ |
| BITPOS | 763.5 ns | 3.066 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| GETBIT | 1.908 µs | 2.270 M ops/s | $\color{#0969da}\text{-11%}$ |
| SETBIT | 11.4 µs | 0.657 M ops/s | $\color{#0969da}\text{-41%}$ |
| BF.ADD | 106.3 µs | 0.021 M ops/s | $\color{#0969da}\text{-27%}$ |
| BF.EXISTS | 563.1 ns | 2.372 M ops/s | $\color{#0969da}\text{-19%}$ |
| BF.INFO | 186 ns | 5.659 M ops/s | $\color{#1a7f37}\text{-4%}$ |
| CF.ADD | 21.1 µs | 0.303 M ops/s | $\color{#0969da}\text{-42%}$ |
| CF.DEL | 19.47 µs | 0.159 M ops/s | $\color{#0969da}\text{-35%}$ |
| CF.EXISTS | 665.8 ns | 1.694 M ops/s | $\color{#0969da}\text{-20%}$ |
| BATCH_COMMIT | 9.836 µs | 0.265 M ops/s | $\color{#0969da}\text{-30%}$ |
| DEL | 18.39 µs | 0.507 M ops/s | $\color{#0969da}\text{-48%}$ |
| EXISTS | 2.533 µs | 3.549 M ops/s | $\color{#0969da}\text{-22%}$ |
| NAMESPACE | 7.039 µs | 0.983 M ops/s | $\color{#0969da}\text{-48%}$ |
| GEOADD | 13.16 µs | 0.532 M ops/s | $\color{#0969da}\text{-37%}$ |
| GEODIST | 3.196 µs | 1.977 M ops/s | $\color{#1a7f37}\text{-1%}$ |
| GEOHASH | 1.26 µs | 2.851 M ops/s | $\color{#1a7f37}\text{+1%}$ |
| GEOPOS | 1.728 µs | 3.425 M ops/s | $\color{#1a7f37}\text{+6%}$ |
| HDEL | 35 µs | 0.084 M ops/s | $\color{#0969da}\text{-20%}$ |
| HEXISTS | 420.3 ns | 2.495 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| HGET | 1.56 µs | 1.480 M ops/s | $\color{#0969da}\text{-12%}$ |
| HGETALL | 30.8 µs | 0.044 M ops/s | $\color{#1a7f37}\text{-9%}$ |
| HINCRBY | 10.64 µs | 0.861 M ops/s | $\color{#0969da}\text{-44%}$ |
| HKEYS | 25.58 µs | 0.054 M ops/s | $\color{#0969da}\text{-14%}$ |
| HLEN | 1.96 µs | 3.364 M ops/s | $\color{#0969da}\text{-11%}$ |
| HMGET | 3.538 µs | 0.515 M ops/s | $\color{#1a7f37}\text{-5%}$ |
| HSET | 9.949 µs | 0.613 M ops/s | $\color{#0969da}\text{-41%}$ |
| HVALS | 27.25 µs | 0.054 M ops/s | $\color{#0969da}\text{-24%}$ |
| PFADD | 28.24 µs | 0.129 M ops/s | $\color{#0969da}\text{-41%}$ |
| PFCOUNT | 45.74 µs | 0.036 M ops/s | $\color{#0969da}\text{-20%}$ |
| PFMERGE | 34.24 µs | 0.071 M ops/s | $\color{#0969da}\text{-35%}$ |
| JSON.ARRLEN | 4.744 µs | 2.376 M ops/s | $\color{#0969da}\text{-29%}$ |
| JSON.DEL | 13.38 µs | 0.441 M ops/s | $\color{#0969da}\text{-47%}$ |
| JSON.GET | 5.82 µs | 2.071 M ops/s | $\color{#0969da}\text{-33%}$ |
| JSON.NUMINCRBY | 14.19 µs | 0.756 M ops/s | $\color{#0969da}\text{-48%}$ |
| JSON.SET | 7.076 µs | 0.625 M ops/s | $\color{#0969da}\text{-45%}$ |
| JSON.TYPE | 3.341 µs | 2.795 M ops/s | $\color{#0969da}\text{-29%}$ |
| LINDEX | 1.531 µs | 2.005 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| LLEN | 695.5 ns | 3.602 M ops/s | $\color{#1a7f37}\text{-8%}$ |
| LPOP | 4.442 µs | 0.496 M ops/s | $\color{#0969da}\text{-39%}$ |
| LPUSH | 26.37 µs | 0.733 M ops/s | $\color{#0969da}\text{-43%}$ |
| LRANGE | 36.37 µs | 0.046 M ops/s | $\color{#0969da}\text{-16%}$ |
| LREM | 32.33 µs | 0.086 M ops/s | $\color{#0969da}\text{-19%}$ |
| LSET | 4.271 µs | 0.770 M ops/s | $\color{#0969da}\text{-45%}$ |
| LTRIM | 116.4 µs | 0.010 M ops/s | $\color{#0969da}\text{-42%}$ |
| RPOP | 4.115 µs | 0.515 M ops/s | $\color{#0969da}\text{-39%}$ |
| RPUSH | 10.63 µs | 0.728 M ops/s | $\color{#0969da}\text{-42%}$ |
| SADD | 12.4 µs | 0.647 M ops/s | $\color{#0969da}\text{-42%}$ |
| SCARD | 786 ns | 3.723 M ops/s | $\color{#1a7f37}\text{-10%}$ |
| SISMEMBER | 1.603 µs | 1.558 M ops/s | $\color{#0969da}\text{-16%}$ |
| SMEMBERS | 31.29 µs | 0.053 M ops/s | $\color{#0969da}\text{-16%}$ |
| SPOP | 1.877 ms | 0.001 M ops/s | $\color{#0969da}\text{-15%}$ |
| SRANDMEMBER | 32.24 µs | 0.053 M ops/s | $\color{#0969da}\text{-18%}$ |
| SREM | 24.01 µs | 0.083 M ops/s | $\color{#0969da}\text{-19%}$ |
| SI.ADD | 10.56 µs | 0.672 M ops/s | $\color{#0969da}\text{-41%}$ |
| SI.CARD | 1.556 µs | 3.609 M ops/s | $\color{#1a7f37}\text{-2%}$ |
| SI.EXISTS | 1.658 µs | 1.913 M ops/s | $\color{#1a7f37}\text{-9%}$ |
| SI.RANGE | 17.17 µs | 0.118 M ops/s | $\color{#0969da}\text{-16%}$ |
| SI.REM | 27.07 µs | 0.081 M ops/s | $\color{#0969da}\text{-19%}$ |
| APPEND | 20.18 µs | 0.567 M ops/s | $\color{#0969da}\text{-51%}$ |
| DECRBY | 3.075 µs | 1.141 M ops/s | $\color{#0969da}\text{-53%}$ |
| GET | 1.396 µs | 1.900 M ops/s | $\color{#0969da}\text{-11%}$ |
| GETDEL | 8.726 µs | 0.573 M ops/s | $\color{#0969da}\text{-52%}$ |
| GETRANGE | 190.8 ns | 5.488 M ops/s | $\color{#1a7f37}\text{-6%}$ |
| INCRBY | 8.12 µs | 1.163 M ops/s | $\color{#0969da}\text{-52%}$ |
| MGET | 10.18 µs | 0.523 M ops/s | $\color{#0969da}\text{-13%}$ |
| MSET | 25.02 µs | 0.229 M ops/s | $\color{#0969da}\text{-28%}$ |
| SET | 8.494 µs | 1.239 M ops/s | $\color{#0969da}\text{-53%}$ |
| SETRANGE | 9.97 µs | 0.560 M ops/s | $\color{#0969da}\text{-51%}$ |
| STRLEN | 334.8 ns | 6.112 M ops/s | $\color{#1a7f37}\text{+1%}$ |
| XADD | 21.5 µs | 0.699 M ops/s | $\color{#0969da}\text{-43%}$ |
| XDEL | 28.17 µs | 0.311 M ops/s | $\color{#0969da}\text{-40%}$ |
| XLEN | 212.2 ns | 4.914 M ops/s | $\color{#1a7f37}\text{-5%}$ |
| XRANGE | 22.76 µs | 0.078 M ops/s | $\color{#0969da}\text{-19%}$ |
| XREAD | 21.6 µs | 0.078 M ops/s | $\color{#0969da}\text{-18%}$ |
| TDIGEST.ADD | 6.174 µs | 0.534 M ops/s | $\color{#0969da}\text{-42%}$ |
| TDIGEST.BYRANK | 8.605 µs | 2.104 M ops/s | $\color{#0969da}\text{-32%}$ |
| TDIGEST.CDF | 10.59 µs | 1.067 M ops/s | $\color{#0969da}\text{-30%}$ |
| TDIGEST.QUANTILE | 9.608 µs | 2.562 M ops/s | $\color{#0969da}\text{-31%}$ |
| TS.ADD | 45.21 µs | 0.120 M ops/s | $\color{#0969da}\text{-20%}$ |
| TS.GET | 3.319 µs | 1.648 M ops/s | $\color{#0969da}\text{-23%}$ |
| TS.INCRBY | 42.69 µs | 0.063 M ops/s | $\color{#0969da}\text{-24%}$ |
| TS.RANGE | 52.34 µs | 0.023 M ops/s | $\color{#0969da}\text{-11%}$ |
| ZADD | 16.56 µs | 0.508 M ops/s | $\color{#0969da}\text{-37%}$ |
| ZCARD | 1.098 µs | 6.536 M ops/s | $\color{#1a7f37}\text{-10%}$ |
| ZCOUNT | 77.73 µs | 0.016 M ops/s | $\color{#0969da}\text{-24%}$ |
| ZINCRBY | 13.43 µs | 0.117 M ops/s | $\color{#1a7f37}\text{-9%}$ |
| ZPOPMIN | 28.23 µs | 0.071 M ops/s | $\color{#1a7f37}\text{-7%}$ |
| ZRANGE | 9.684 µs | 0.346 M ops/s | $\color{#0969da}\text{-19%}$ |
| ZRANK | 31.76 µs | 0.094 M ops/s | $\color{#1a7f37}\text{-7%}$ |
| ZREM | 42.02 µs | 0.065 M ops/s | $\color{#1a7f37}\text{+1%}$ |
| ZREVRANGE | 9.264 µs | 0.241 M ops/s | $\color{#0969da}\text{-20%}$ |
| ZSCORE | 590.1 ns | 2.031 M ops/s | $\color{#0969da}\text{-16%}$ |
---
### Environment: macOS (Apple M2 Max)
- **CPU**: `Apple M2 Max (12 Cores)`
- **Memory**: `64.0 GB`
- **Platform**: `macOS 26.5.1 (Darwin 25.5.0) (aarch64)`
- **Compiler**: `rustc 1.98.0 (88d9e12ae 2026-08-18)`
#### WeDb (Embedded Engine) vs Redis (v8.10.1 Standalone Server) Performance Comparison
> **Benchmark Notes**:
> - WeDb: In-process direct call, LSM disk persistence (WAL journal)
> - Redis: Standalone service, AOF persistence enabled (appendfsync everysec), Unix Domain Socket
| Command | WeDb P95 Latency | WeDb Est. Throughput | Redis P95 Latency | Redis Est. Throughput | WeDb Speedup |
| :--- | :--- | :--- | :--- | :--- | :--- |
| SET | 29.66 µs | 0.500 M ops/s | 531.2 µs | 0.008 M ops/s | $\color{#1a7f37}\text{61.9x}$ |
| GET | 6.999 µs | 0.632 M ops/s | 76.74 µs | 0.016 M ops/s | $\color{#1a7f37}\text{39.8x}$ |
| MSET | 152.2 µs | 0.052 M ops/s | 561.2 µs | 0.008 M ops/s | $\color{#1a7f37}\text{6.8x}$ |
| MGET | 22.37 µs | 0.286 M ops/s | 328.5 µs | 0.008 M ops/s | $\color{#1a7f37}\text{34.6x}$ |
| INCRBY | 136.6 µs | 0.251 M ops/s | 1.354 ms | 0.005 M ops/s | $\color{#1a7f37}\text{50.0x}$ |
| HSET | 48.29 µs | 0.255 M ops/s | 535.7 µs | 0.009 M ops/s | $\color{#1a7f37}\text{27.9x}$ |
| HGET | 8.04 µs | 0.414 M ops/s | 89.16 µs | 0.016 M ops/s | $\color{#1a7f37}\text{25.6x}$ |
| LPUSH | 141.9 µs | 0.053 M ops/s | 464.4 µs | 0.008 M ops/s | $\color{#1a7f37}\text{6.6x}$ |
| LPOP | 40.24 µs | 0.073 M ops/s | 153.5 µs | 0.014 M ops/s | $\color{#1a7f37}\text{5.0x}$ |
| SADD | 37.58 µs | 0.304 M ops/s | 544.4 µs | 0.009 M ops/s | $\color{#1a7f37}\text{34.2x}$ |
| SISMEMBER | 4.583 µs | 0.775 M ops/s | 85.2 µs | 0.013 M ops/s | $\color{#1a7f37}\text{60.4x}$ |
| ZADD | 52.37 µs | 0.289 M ops/s | 241.8 µs | 0.015 M ops/s | $\color{#1a7f37}\text{19.0x}$ |
| ZRANGE | 33.37 µs | 0.262 M ops/s | 113.1 µs | 0.013 M ops/s | $\color{#1a7f37}\text{20.9x}$ |
| DEL | 16.33 µs | 0.429 M ops/s | 164.2 µs | 0.019 M ops/s | $\color{#1a7f37}\text{22.4x}$ |
| EXISTS | 12.33 µs | 1.171 M ops/s | 50.87 µs | 0.023 M ops/s | $\color{#1a7f37}\text{51.2x}$ |
#### Regression Benchmark
| Command | P95 Latency | Est. Throughput | vs Baseline (%) |
| :--- | :--- | :--- | :--- |
| BITCOUNT | 8.083 µs | 2.181 M ops/s | $\color{#cf222e}\text{+22%}$ |
| BITPOS | 397.9 ns | 3.102 M ops/s | $\color{#0969da}\text{-48%}$ |
| GETBIT | 1.187 µs | 1.951 M ops/s | $\color{#cf222e}\text{+32%}$ |
| SETBIT | 79.33 µs | 0.436 M ops/s | $\color{#cf222e}\text{+34%}$ |
| BF.ADD | 433.3 µs | 0.011 M ops/s | $\color{#cf222e}\text{+88%}$ |
| BF.EXISTS | 845.8 ns | 2.261 M ops/s | $\color{#1a7f37}\text{+10%}$ |
| BF.INFO | 340.6 ns | 3.887 M ops/s | $\color{#cf222e}\text{+80%}$ |
| CF.ADD | 65.49 µs | 0.107 M ops/s | $\color{#cf222e}\text{+145%}$ |
| CF.DEL | 67.79 µs | 0.123 M ops/s | $\color{#1a7f37}\text{+13%}$ |
| CF.EXISTS | 869.4 ns | 1.456 M ops/s | $\color{#cf222e}\text{+25%}$ |
| BATCH_COMMIT | 67.24 µs | 0.222 M ops/s | $\color{#cf222e}\text{+20%}$ |
| DEL | 85.12 µs | 0.304 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| EXISTS | 5.666 µs | 2.999 M ops/s | $\color{#1a7f37}\text{-0%}$ |
| NAMESPACE | 69.91 µs | 0.522 M ops/s | $\color{#cf222e}\text{+35%}$ |
| GEOADD | 77.12 µs | 0.421 M ops/s | $\color{#1a7f37}\text{+12%}$ |
| GEODIST | 559.4 ns | 2.222 M ops/s | $\color{#cf222e}\text{+16%}$ |
| GEOHASH | 413.6 ns | 2.766 M ops/s | $\color{#0969da}\text{-57%}$ |
| GEOPOS | 366.7 ns | 3.344 M ops/s | $\color{#cf222e}\text{+25%}$ |
| HDEL | 80.33 µs | 0.100 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| HEXISTS | 411 ns | 2.936 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| HGET | 817.2 ns | 1.392 M ops/s | $\color{#1a7f37}\text{+15%}$ |
| HGETALL | 37.58 µs | 0.076 M ops/s | $\color{#1a7f37}\text{-8%}$ |
| HINCRBY | 68.16 µs | 0.649 M ops/s | $\color{#1a7f37}\text{-5%}$ |
| HKEYS | 20.74 µs | 0.080 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| HLEN | 317.2 ns | 3.967 M ops/s | $\color{#1a7f37}\text{-7%}$ |
| HMGET | 10.79 µs | 0.471 M ops/s | $\color{#1a7f37}\text{+15%}$ |
| HSET | 56.91 µs | 0.453 M ops/s | $\color{#1a7f37}\text{0%}$ |
| HVALS | 22.16 µs | 0.074 M ops/s | $\color{#0969da}\text{-11%}$ |
| PFADD | 68.2 µs | 0.097 M ops/s | $\color{#cf222e}\text{+16%}$ |
| PFCOUNT | 41.79 µs | 0.030 M ops/s | $\color{#1a7f37}\text{-8%}$ |
| PFMERGE | 51.37 µs | 0.059 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| JSON.ARRLEN | 17.91 µs | 0.924 M ops/s | $\color{#cf222e}\text{+100%}$ |
| JSON.DEL | 93.7 µs | 0.267 M ops/s | $\color{#1a7f37}\text{+5%}$ |
| JSON.GET | 15.66 µs | 1.503 M ops/s | $\color{#cf222e}\text{+23%}$ |
| JSON.NUMINCRBY | 17.79 µs | 0.558 M ops/s | $\color{#1a7f37}\text{0%}$ |
| JSON.SET | 56.29 µs | 0.407 M ops/s | $\color{#cf222e}\text{+20%}$ |
| JSON.TYPE | 5.208 µs | 2.002 M ops/s | $\color{#cf222e}\text{+20%}$ |
| LINDEX | 533.3 ns | 2.302 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| LLEN | 237.8 ns | 4.398 M ops/s | $\color{#1a7f37}\text{+13%}$ |
| LPOP | 6.457 µs | 0.429 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| LPUSH | 55.37 µs | 0.632 M ops/s | $\color{#1a7f37}\text{-0%}$ |
| LRANGE | 31.2 µs | 0.043 M ops/s | $\color{#cf222e}\text{+22%}$ |
| LREM | 129.3 µs | 0.109 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| LSET | 4.541 µs | 0.546 M ops/s | $\color{#cf222e}\text{+300%}$ |
| LTRIM | 194.2 µs | 0.007 M ops/s | $\color{#1a7f37}\text{+4%}$ |
| RPOP | 5.29 µs | 0.471 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| RPUSH | 75.45 µs | 0.522 M ops/s | $\color{#0969da}\text{-51%}$ |
| SADD | 76.66 µs | 0.453 M ops/s | $\color{#cf222e}\text{+20%}$ |
| SCARD | 353.7 ns | 3.434 M ops/s | $\color{#cf222e}\text{+29%}$ |
| SISMEMBER | 666.2 ns | 1.657 M ops/s | $\color{#0969da}\text{-15%}$ |
| SMEMBERS | 20.83 µs | 0.084 M ops/s | $\color{#1a7f37}\text{-0%}$ |
| SPOP | 1.262 ms | 0.001 M ops/s | $\color{#1a7f37}\text{-0%}$ |
| SRANDMEMBER | 115.5 µs | 0.078 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| SREM | 121.5 µs | 0.111 M ops/s | $\color{#1a7f37}\text{-1%}$ |
| SI.ADD | 142.5 µs | 0.534 M ops/s | $\color{#1a7f37}\text{+12%}$ |
| SI.CARD | 236.5 ns | 5.621 M ops/s | $\color{#1a7f37}\text{+1%}$ |
| SI.EXISTS | 647.9 ns | 2.095 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| SI.RANGE | 15.91 µs | 0.182 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| SI.REM | 91.49 µs | 0.101 M ops/s | $\color{#1a7f37}\text{+8%}$ |
| APPEND | 88.83 µs | 0.400 M ops/s | $\color{#0969da}\text{-56%}$ |
| DECRBY | 62.74 µs | 0.858 M ops/s | $\color{#1a7f37}\text{-10%}$ |
| GET | 666.1 ns | 2.344 M ops/s | $\color{#1a7f37}\text{+6%}$ |
| GETDEL | 442.2 µs | 0.173 M ops/s | $\color{#cf222e}\text{+128%}$ |
| GETRANGE | 114.1 ns | 9.891 M ops/s | $\color{#1a7f37}\text{+4%}$ |
| INCRBY | 97.58 µs | 0.829 M ops/s | $\color{#1a7f37}\text{-3%}$ |
| MGET | 4.874 µs | 0.600 M ops/s | $\color{#1a7f37}\text{+11%}$ |
| MSET | 139.3 µs | 0.202 M ops/s | $\color{#1a7f37}\text{+15%}$ |
| SET | 116.7 µs | 0.751 M ops/s | $\color{#1a7f37}\text{+14%}$ |
| SETRANGE | 158.5 µs | 0.358 M ops/s | $\color{#0969da}\text{-51%}$ |
| STRLEN | 96.56 ns | 11.972 M ops/s | $\color{#1a7f37}\text{-1%}$ |
| XADD | 49.99 µs | 0.572 M ops/s | $\color{#1a7f37}\text{+2%}$ |
| XDEL | 55.87 µs | 0.229 M ops/s | $\color{#cf222e}\text{+23%}$ |
| XLEN | 163.6 ns | 6.262 M ops/s | $\color{#1a7f37}\text{+12%}$ |
| XRANGE | 34.04 µs | 0.091 M ops/s | $\color{#1a7f37}\text{+9%}$ |
| XREAD | 28.37 µs | 0.086 M ops/s | $\color{#1a7f37}\text{+12%}$ |
| TDIGEST.ADD | 17.54 µs | 0.308 M ops/s | $\color{#1a7f37}\text{+7%}$ |
| TDIGEST.BYRANK | 11.83 µs | 1.413 M ops/s | $\color{#1a7f37}\text{+13%}$ |
| TDIGEST.CDF | 17.91 µs | 0.961 M ops/s | $\color{#cf222e}\text{+19%}$ |
| TDIGEST.QUANTILE | 16.08 µs | 1.847 M ops/s | $\color{#1a7f37}\text{-7%}$ |
| TS.ADD | 165 µs | 0.142 M ops/s | $\color{#1a7f37}\text{+1%}$ |
| TS.GET | 697.5 ns | 1.763 M ops/s | $\color{#1a7f37}\text{+7%}$ |
| TS.INCRBY | 27.2 µs | 0.069 M ops/s | $\color{#1a7f37}\text{+7%}$ |
| TS.RANGE | 28.58 µs | 0.038 M ops/s | $\color{#1a7f37}\text{+13%}$ |
| ZADD | 141.1 µs | 0.381 M ops/s | $\color{#cf222e}\text{+19%}$ |
| ZCARD | 174 ns | 7.485 M ops/s | $\color{#cf222e}\text{+24%}$ |
| ZCOUNT | 60.24 µs | 0.021 M ops/s | $\color{#cf222e}\text{+21%}$ |
| ZINCRBY | 106.8 µs | 0.125 M ops/s | $\color{#cf222e}\text{+22%}$ |
| ZPOPMIN | 14.83 µs | 0.109 M ops/s | $\color{#1a7f37}\text{+7%}$ |
| ZRANGE | 11.29 µs | 0.375 M ops/s | $\color{#cf222e}\text{+20%}$ |
| ZRANK | 10.29 µs | 0.155 M ops/s | $\color{#1a7f37}\text{+5%}$ |
| ZREM | 172.6 µs | 0.113 M ops/s | $\color{#1a7f37}\text{+3%}$ |
| ZREVRANGE | 12.49 µs | 0.253 M ops/s | $\color{#1a7f37}\text{+10%}$ |
| ZSCORE | 765.1 ns | 1.568 M ops/s | $\color{#cf222e}\text{+29%}$ |
---
<a id="zh"></a>
# wedb_embed : 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 [fjall](https://github.com/fjall-rs/fjall) 开发
- [wedb_embed : 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 [fjall](https://github.com/fjall-rs/fjall) 开发](#wedb_embed-嵌入式数据库引擎提供类似-redis-的接口底层基于-fjallhttpsgithubcomfjall-rsfjall-开发)
- [项目初衷](#项目初衷)
- [核心架构与编码设计](#核心架构与编码设计)
- [1. 默认库前缀优化与 1-Byte 标签](#1-默认库前缀优化与-1-byte-标签)
- [2. OPPV 保序变长整型编码 (`oppv_u64`)](#2-oppv-保序变长整型编码-oppv_u64)
- [3. 多租户数字 ID 映射与重命名](#3-多租户数字-id-映射与重命名)
- [4. 流式迭代器](#4-流式迭代器)
- [使用演示](#使用演示)
- [1. 数据库打开与配置 (`WeDb` & `Conf`)](#1-数据库打开与配置-wedb-conf)
- [2. 多租户与分库隔离 (`Namespace`)](#2-多租户与分库隔离-namespace)
- [3. 键值与字符串 (`KvOps`)](#3-键值与字符串-kvops)
- [4. 哈希字典 (`HashOps`)](#4-哈希字典-hashops)
- [5. 列表 (`ListOps`)](#5-列表-listops)
- [6. 集合 (`SetOps`)](#6-集合-setops)
- [7. 有序集合 (`ZSetOps`)](#7-有序集合-zsetops)
- [8. 位图、布隆过滤、时序与地理位置 (`BitmapOps`, `BloomOps`, `TsOps`, `GeoOps`)](#8-位图布隆过滤时序与地理位置-bitmapops-bloomops-tsops-geoops)
- [技术堆栈](#技术堆栈)
- [性能基准与回归测试](#性能基准与回归测试)
- [运行环境:Ubuntu CI (GitHub Actions Runner)](#运行环境ubuntu-ci-github-actions-runner)
- [WeDb (嵌入式引擎) vs Redis (v8.10.1 独立服务) 性能对比](#wedb-嵌入式引擎-vs-redis-v8101-独立服务-性能对比)
- [性能回归测试](#性能回归测试)
- [运行环境:macOS (Apple M2 Max)](#运行环境macos-apple-m2-max)
- [WeDb (嵌入式引擎) vs Redis (v8.10.1 独立服务) 性能对比](#wedb-嵌入式引擎-vs-redis-v8101-独立服务-性能对比)
- [性能回归测试](#性能回归测试)
基于 Rust 开发的嵌入式多模型数据库引擎,提供与 Redis 兼容的数据结构与操作接口,底层采用 [fjall](https://github.com/fjall-rs/fjall) LSM-Tree 进行磁盘持久化存储。
---
## 项目初衷
在许多单机服务或嵌入式应用场景中,部署和维护独立的 Redis 守护进程会增加运维复杂度,并且跨进程通信(IPC / Socket 网络与 RESP 编解码)会带来额外的延迟开销。
在单机或嵌入式应用中,部署独立 Redis 进程会增加运维复杂度,且跨进程通信(IPC/Socket 与 RESP 编解码)会引入额外延迟。
`wedb_embed` 作为 Rust 原生库嵌入应用程序:
- **进程内调用**:无网络与 IPC 开销,直接在内存与磁盘间进行读写。
- **丰富的数据模型**:支持 String、Hash、List、Set、ZSet、Bitmap、JSON、Bloom/Cuckoo Filter、TimeSeries、Geo、HyperLogLog、TDigest、SortedInt、Stream 等 15 种数据结构。
- **多租户与分库隔离**:原生支持 $2^{64}$ 租户与 $2^{64}$ 分库,具备原子改名与物理隔离能力。
- **紧凑二进制编码**:默认库采用 0 字节前缀,复合结构使用 1-Byte `KeyTag` 与 OPPV 变长保序编码。
- **持久化一致性**:基于 [fjall](https://github.com/fjall-rs/fjall) LSM-Tree 存储引擎与跨 Keyspace 原子 WriteBatch,保障数据安全。
---
## 核心架构与编码设计
```mermaid
graph TD
Client["Application Code"] --> WeDb["WeDb Instance"]
WeDb --> NS["Namespace Handle<br/>(Zero-Heap Struct)"]
subgraph KeyComposer["Key Composer & Fast Encoding"]
Tag["1-Byte Fast KeyTag (#[repr(u8)])"]
OPPV["OPPV Order-Preserving Varint (1~9B)"]
DefaultBypass["Default NS 0-Prefix Bypass"]
Slot["CRC16 Slot & {hashtag}"]
end
subgraph Engine["Storage Engine Layer"]
Batch["Atomic WriteBatch (Cross-Keyspace WAL)"]
Catalog["Catalog Compact Index"]
end
subgraph Storage["Fjall LSM Partitions"]
DataKS["Data Keyspace<br/>(业务载荷与子键 / 64KB 块)"]
MetaKS["Meta Keyspace<br/>(常驻索引、TTL 与元数据 / 4KB 块)"]
end
WeDb --> KeyComposer
NS --> KeyComposer
KeyComposer --> Engine
Engine --> DataKS
Engine --> MetaKS
```
### 1. 默认库前缀优化与 1-Byte 标签
- **默认命名空间**:键直接为原始字节 `user_key`,物理前缀占用 0 字节。
- **1-Byte Fast Tag (`KeyTag`)**:复合结构元数据与子键前缀采用 `#[repr(u8)] KeyTag` 编码(`\x00\x01:key` 等),消除冗余字符串标签。
### 2. OPPV 保序变长整型编码 (`oppv_u64`)
- 数据库编号与租户数字 ID 采用 **OPPV(Order-Preserving Prefix Varint)** 编码:
- 分库 $0 \sim 127$:占用 1 字节(相比固定 8 字节大端序节约空间)。
- 全范围保持字节字典序与数值顺序等价:$\forall a < b \implies \text{encode}(a) < \text{encode}(b)$。
### 3. 多租户数字 ID 映射与重命名
- 租户名称与 `ns_id: u64` 采用全局元数据原子映射。
- **租户改名 (`rename_namespace`)**:更新元数据映射即可完成重命名,无需重写底层业务键。
### 4. 流式迭代器
- **`WeDb::iter(&self) -> Namespaces`**:基于字典序跳跃二分(Prefix Jump-Seek)定位租户。
- **`Namespace::iter(&self) -> Dbs`**:直接流式解包底层 OPPV 变长 Catalog 目录。
---
## 使用演示
### 1. 数据库打开与配置 (`WeDb` & `Conf`)
```rust
use wedb_embed::{Conf, PersistMode, Result, WeDb};
fn main() -> Result<()> {
// 1. 自定义配置打开
let mut conf = Conf::new("./data/db_demo");
conf.db.flush_workers = 2; // 后台刷盘线程数
let db = WeDb::open_with_conf(&conf)?;
// 2. 刷盘与周期任务
db.persist(PersistMode::SyncAll)?; // 手动同步持久化 WAL 与脏页
db.active_expire_cycle(100)?; // 触发主动过期采样清理
Ok(())
}
```
### 2. 多租户与分库隔离 (`Namespace`)
```rust
use wedb_embed::{KvOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/multitenant_demo")?;
// 租户隔离句柄(轻量句柄,零拷贝)
let ns_apple = db.namespace("tenant_apple");
let ns_google = db.namespace("tenant_google");
ns_apple.set(b"config:theme", b"dark", &[])?;
ns_google.set(b"config:theme", b"light", &[])?;
// 物理隔离读取
assert_eq!(ns_apple.get(b"config:theme")?.unwrap(), b"dark");
assert_eq!(ns_google.get(b"config:theme")?.unwrap(), b"light");
// 多 DB 选择 (如 SELECT 1)
let db1 = db.select_db(1);
db1.set(b"db1_key", b"value1", &[])?;
// 遍历租户下已激活的 DB(流式迭代)
for db_idx in &ns_apple {
println!("Active DB: {}", db_idx);
}
// 租户重命名
db.rename_namespace("tenant_apple", "tenant_apple_v2")?;
// 键空间管理
let key_count = ns_apple.key_count()?;
let keys = ns_apple.keys(b"config:*")?;
let exists_count = ns_apple.exists(&[b"config:theme".as_slice()])?;
ns_apple.del(&[b"config:theme".as_slice()])?;
ns_apple.clear()?; // 级联清空整个命名空间
Ok(())
}
```
### 3. 键值与字符串 (`KvOps`)
```rust
use wedb_embed::{KvOps, Result, WeDb, string::Set};
fn main() -> Result<()> {
let db = WeDb::open("./data/kv_demo")?;
// 基础读写与过期设置 (set_ex)
db.set(b"site", b"webc.site", &[])?;
let val = db.get(b"site")?;
db.set_ex(b"temp_token", b"xyz", 3600)?;
// 条件写入 (NX / XX)
db.set(b"lock", b"1", &[Set::Nx])?;
// 数值增减
db.incr(b"counter")?;
db.incrby(b"counter", 10)?;
db.decrby(b"counter", 5)?;
// 批量与字符串操作
db.mset(&[(b"k1", b"v1"), (b"k2", b"v2")])?;
let values = db.mget(&[b"k1".as_slice(), b"k2".as_slice()])?;
let len = db.strlen(b"site")?;
let old_val = db.getset(b"site", b"new_site")?;
let del_val = db.getdel(b"site")?;
Ok(())
}
```
### 4. 哈希字典 (`HashOps`)
```rust
use wedb_embed::{HashOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/hash_demo")?;
// 字段写入与读取
db.hset(b"user:100", b"name", b"Alice")?;
db.hset(b"user:100", b"age", b"20")?;
let name = db.hget(b"user:100", b"name")?;
// 批量写入与获取
db.hmset(b"user:100", &[(b"city", b"Beijing"), (b"role", b"Admin")])?;
let fields = db.hmget(b"user:100", &[b"name".as_slice(), b"city".as_slice()])?;
// 数值自增与元数据
db.hincrby(b"user:100", b"age", 1)?;
let exists = db.hexists(b"user:100", b"name")?;
let len = db.hlen(b"user:100")?;
let all = db.hgetall(b"user:100")?;
let keys = db.hkeys(b"user:100")?;
let vals = db.hvals(b"user:100")?;
db.hdel(b"user:100", &[b"role".as_slice()])?;
Ok(())
}
```
### 5. 列表 (`ListOps`)
```rust
use wedb_embed::{ListOps, Result, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/list_demo")?;
// 双端推入与弹出
db.lpush(b"tasks", &[b"task1".as_slice(), b"task2".as_slice()])?;
db.rpush(b"tasks", &[b"task3".as_slice()])?;
let first = db.lpop(b"tasks", 1)?;
let last = db.rpop(b"tasks", 1)?;
// 范围查询与修剪
let len = db.llen(b"tasks")?;
let items = db.lrange(b"tasks", 0, -1)?;
db.ltrim(b"tasks", 0, 10)?;
db.lset(b"tasks", 0, b"updated_task")?;
Ok(())
}
```
### 6. 集合 (`SetOps`)
```rust
use wedb_embed::{Result, SetOps, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/set_demo")?;
db.sadd(b"tags", &[b"rust".as_slice(), b"database".as_slice(), b"lsm".as_slice()])?;
let is_member = db.sismember(b"tags", b"rust")?;
let count = db.scard(b"tags")?;
let members = db.smembers(b"tags")?;
db.srem(b"tags", &[b"lsm".as_slice()])?;
let popped = db.spop(b"tags", 1)?;
Ok(())
}
```
### 7. 有序集合 (`ZSetOps`)
```rust
use wedb_embed::{Result, WeDb, ZSetOps};
fn main() -> Result<()> {
let db = WeDb::open("./data/zset_demo")?;
// 添加成员与分数
db.zadd(b"leaderboard", &[(100.0, b"player1".as_slice()), (200.0, b"player2".as_slice())], &[])?;
db.zincrby(b"leaderboard", 50.0, b"player1")?;
// 排名与分数
let score = db.zscore(b"leaderboard", b"player1")?;
let rank = db.zrank(b"leaderboard", b"player1")?;
let rev_rank = db.zrevrank(b"leaderboard", b"player1")?;
// 范围查询
let top = db.zrange(b"leaderboard", 0, 10, &[])?;
let count = db.zcount(b"leaderboard", 100.0, 300.0)?;
let card = db.zcard(b"leaderboard")?;
db.zrem(b"leaderboard", &[b"player1".as_slice()])?;
Ok(())
}
```
### 8. 位图、布隆过滤、时序与地理位置 (`BitmapOps`, `BloomOps`, `TsOps`, `GeoOps`)
```rust
use wedb_embed::{BitmapOps, BloomOps, GeoOps, Result, TsOps, WeDb};
fn main() -> Result<()> {
let db = WeDb::open("./data/advanced_demo")?;
// 位图 (Bitmap)
db.setbit(b"online_users", 1001, true)?;
let is_online = db.getbit(b"online_users", 1001)?;
let online_count = db.bitcount(b"online_users", 0, -1)?;
// 布隆过滤器 (Bloom Filter)
db.bf_add(b"filter", b"item_1")?;
let exists = db.bf_exists(b"filter", b"item_1")?;
// 时序数据 (TimeSeries)
db.ts_add(b"metrics:cpu", 1700000000000, 42.5, &[])?;
let latest = db.ts_get(b"metrics:cpu")?;
// 地理位置 (Geo)
db.geoadd(b"cities", &[(116.4074, 39.9042, b"Beijing".as_slice()), (121.4737, 31.2304, b"Shanghai".as_slice())])?;
let dist = db.geodist(b"cities", b"Beijing", b"Shanghai", None)?;
Ok(())
}
```
---
## 技术堆栈
- **核心语言**:Rust Edition 2024
- **底层存储**:`fjall` (LSM-Tree 持久化引擎)
- **JSON 解析**:`sonic-rs` (SIMD 指令集加速 JSON 引擎)
- **哈希算法**:`rapidhash` (非加密哈希算法)
- **二进制编解码**:`bitcode` (二进制序列化)
- **字符串存储**:`hipstr` (紧凑字符串存储)
- **时间库**:`coarsetime`、`ts_` (时间戳)
- **错误定义**:`thiserror`
---
## 性能基准与回归测试
### 运行环境:Ubuntu CI (GitHub Actions Runner)
- **CPU**:`Intel(R) Xeon(R) 6973P-C (4 核)`
- **内存**:`15.6 GB`
- **系统**:`Ubuntu 24.04.4 LTS (Linux 6.17.0-1022-azure) (x86_64)`
- **编译器**:`rustc 1.98.0 (88d9e12ae 2026-08-18)`
#### WeDb (嵌入式引擎) vs Redis (v8.10.1 独立服务) 性能对比
> **测试说明**:
> - WeDb:进程内直接调用,基于 LSM 磁盘持久化(WAL 日志)
> - Redis:独立服务,开启 AOF 持久化(appendfsync everysec),通过本地 Unix Domain Socket 通信
**综合性能对比**:WeDb 加权吞吐量 **168.915 万次/秒** | Redis 加权吞吐量 **2.408 万次/秒** | WeDb 综合性能倍数 **$\color{#1a7f37}\text{70.1x}$**
| SET | 6.009 µs | 119.717 万次/秒 | 63.3 µs | 2.074 万次/秒 | $\color{#1a7f37}\text{57.7x}$ |
| GET | 267.8 ns | 401.284 万次/秒 | 55.29 µs | 2.084 万次/秒 | $\color{#1a7f37}\text{192.5x}$ |
| MSET | 12.44 µs | 24.857 万次/秒 | 69.21 µs | 3.122 万次/秒 | $\color{#1a7f37}\text{8.0x}$ |
| MGET | 3.295 µs | 52.743 万次/秒 | 49.88 µs | 2.103 万次/秒 | $\color{#1a7f37}\text{25.1x}$ |
| INCRBY | 8.867 µs | 98.717 万次/秒 | 79.99 µs | 2.063 万次/秒 | $\color{#1a7f37}\text{47.9x}$ |
| HSET | 11.27 µs | 66.313 万次/秒 | 55.14 µs | 2.053 万次/秒 | $\color{#1a7f37}\text{32.3x}$ |
| HGET | 1.425 µs | 176.741 万次/秒 | 63.48 µs | 2.085 万次/秒 | $\color{#1a7f37}\text{84.8x}$ |
| LPUSH | 12.19 µs | 31.586 万次/秒 | 81.02 µs | 2.037 万次/秒 | $\color{#1a7f37}\text{15.5x}$ |
| LPOP | 3.307 µs | 50.942 万次/秒 | 51.65 µs | 2.035 万次/秒 | $\color{#1a7f37}\text{25.0x}$ |
| SADD | 9.665 µs | 68.166 万次/秒 | 69.82 µs | 3.917 万次/秒 | $\color{#1a7f37}\text{17.4x}$ |
| SISMEMBER | 579.3 ns | 186.463 万次/秒 | 33.42 µs | 5.244 万次/秒 | $\color{#1a7f37}\text{35.6x}$ |
| ZADD | 12.26 µs | 52.715 万次/秒 | 95.59 µs | 2.094 万次/秒 | $\color{#1a7f37}\text{25.2x}$ |
| ZRANGE | 9.29 µs | 37.495 万次/秒 | 34.76 µs | 4.279 万次/秒 | $\color{#1a7f37}\text{8.8x}$ |
| DEL | 8.569 µs | 73.746 万次/秒 | 56.91 µs | 2.053 万次/秒 | $\color{#1a7f37}\text{35.9x}$ |
| EXISTS | 3.642 µs | 239.063 万次/秒 | 53.85 µs | 2.029 万次/秒 | $\color{#1a7f37}\text{117.8x}$ |
#### 性能回归测试
**综合加权性能**:加权估算吞吐量 **124.290 万次/秒** | 加权延迟变动 **$\color{#0969da}\text{-25%}$**
| BITCOUNT | 2.912 µs | 210.040 万次/秒 | $\color{#0969da}\text{-10%}$ |
| BITPOS | 763.5 ns | 306.560 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| GETBIT | 1.908 µs | 226.963 万次/秒 | $\color{#0969da}\text{-11%}$ |
| SETBIT | 11.4 µs | 65.703 万次/秒 | $\color{#0969da}\text{-41%}$ |
| BF.ADD | 106.3 µs | 2.080 万次/秒 | $\color{#0969da}\text{-27%}$ |
| BF.EXISTS | 563.1 ns | 237.192 万次/秒 | $\color{#0969da}\text{-19%}$ |
| BF.INFO | 186 ns | 565.931 万次/秒 | $\color{#1a7f37}\text{-4%}$ |
| CF.ADD | 21.1 µs | 30.285 万次/秒 | $\color{#0969da}\text{-42%}$ |
| CF.DEL | 19.47 µs | 15.941 万次/秒 | $\color{#0969da}\text{-35%}$ |
| CF.EXISTS | 665.8 ns | 169.405 万次/秒 | $\color{#0969da}\text{-20%}$ |
| BATCH_COMMIT | 9.836 µs | 26.476 万次/秒 | $\color{#0969da}\text{-30%}$ |
| DEL | 18.39 µs | 50.736 万次/秒 | $\color{#0969da}\text{-48%}$ |
| EXISTS | 2.533 µs | 354.862 万次/秒 | $\color{#0969da}\text{-22%}$ |
| NAMESPACE | 7.039 µs | 98.328 万次/秒 | $\color{#0969da}\text{-48%}$ |
| GEOADD | 13.16 µs | 53.248 万次/秒 | $\color{#0969da}\text{-37%}$ |
| GEODIST | 3.196 µs | 197.707 万次/秒 | $\color{#1a7f37}\text{-1%}$ |
| GEOHASH | 1.26 µs | 285.144 万次/秒 | $\color{#1a7f37}\text{+1%}$ |
| GEOPOS | 1.728 µs | 342.466 万次/秒 | $\color{#1a7f37}\text{+6%}$ |
| HDEL | 35 µs | 8.389 万次/秒 | $\color{#0969da}\text{-20%}$ |
| HEXISTS | 420.3 ns | 249.501 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| HGET | 1.56 µs | 147.973 万次/秒 | $\color{#0969da}\text{-12%}$ |
| HGETALL | 30.8 µs | 4.442 万次/秒 | $\color{#1a7f37}\text{-9%}$ |
| HINCRBY | 10.64 µs | 86.059 万次/秒 | $\color{#0969da}\text{-44%}$ |
| HKEYS | 25.58 µs | 5.397 万次/秒 | $\color{#0969da}\text{-14%}$ |
| HLEN | 1.96 µs | 336.361 万次/秒 | $\color{#0969da}\text{-11%}$ |
| HMGET | 3.538 µs | 51.546 万次/秒 | $\color{#1a7f37}\text{-5%}$ |
| HSET | 9.949 µs | 61.312 万次/秒 | $\color{#0969da}\text{-41%}$ |
| HVALS | 27.25 µs | 5.382 万次/秒 | $\color{#0969da}\text{-24%}$ |
| PFADD | 28.24 µs | 12.857 万次/秒 | $\color{#0969da}\text{-41%}$ |
| PFCOUNT | 45.74 µs | 3.602 万次/秒 | $\color{#0969da}\text{-20%}$ |
| PFMERGE | 34.24 µs | 7.123 万次/秒 | $\color{#0969da}\text{-35%}$ |
| JSON.ARRLEN | 4.744 µs | 237.643 万次/秒 | $\color{#0969da}\text{-29%}$ |
| JSON.DEL | 13.38 µs | 44.053 万次/秒 | $\color{#0969da}\text{-47%}$ |
| JSON.GET | 5.82 µs | 207.125 万次/秒 | $\color{#0969da}\text{-33%}$ |
| JSON.NUMINCRBY | 14.19 µs | 75.643 万次/秒 | $\color{#0969da}\text{-48%}$ |
| JSON.SET | 7.076 µs | 62.539 万次/秒 | $\color{#0969da}\text{-45%}$ |
| JSON.TYPE | 3.341 µs | 279.486 万次/秒 | $\color{#0969da}\text{-29%}$ |
| LINDEX | 1.531 µs | 200.481 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| LLEN | 695.5 ns | 360.231 万次/秒 | $\color{#1a7f37}\text{-8%}$ |
| LPOP | 4.442 µs | 49.579 万次/秒 | $\color{#0969da}\text{-39%}$ |
| LPUSH | 26.37 µs | 73.260 万次/秒 | $\color{#0969da}\text{-43%}$ |
| LRANGE | 36.37 µs | 4.587 万次/秒 | $\color{#0969da}\text{-16%}$ |
| LREM | 32.33 µs | 8.628 万次/秒 | $\color{#0969da}\text{-19%}$ |
| LSET | 4.271 µs | 77.042 万次/秒 | $\color{#0969da}\text{-45%}$ |
| LTRIM | 116.4 µs | 0.983 万次/秒 | $\color{#0969da}\text{-42%}$ |
| RPOP | 4.115 µs | 51.520 万次/秒 | $\color{#0969da}\text{-39%}$ |
| RPUSH | 10.63 µs | 72.780 万次/秒 | $\color{#0969da}\text{-42%}$ |
| SADD | 12.4 µs | 64.725 万次/秒 | $\color{#0969da}\text{-42%}$ |
| SCARD | 786 ns | 372.301 万次/秒 | $\color{#1a7f37}\text{-10%}$ |
| SISMEMBER | 1.603 µs | 155.812 万次/秒 | $\color{#0969da}\text{-16%}$ |
| SMEMBERS | 31.29 µs | 5.288 万次/秒 | $\color{#0969da}\text{-16%}$ |
| SPOP | 1.877 ms | 0.055 万次/秒 | $\color{#0969da}\text{-15%}$ |
| SRANDMEMBER | 32.24 µs | 5.294 万次/秒 | $\color{#0969da}\text{-18%}$ |
| SREM | 24.01 µs | 8.340 万次/秒 | $\color{#0969da}\text{-19%}$ |
| SI.ADD | 10.56 µs | 67.159 万次/秒 | $\color{#0969da}\text{-41%}$ |
| SI.CARD | 1.556 µs | 360.881 万次/秒 | $\color{#1a7f37}\text{-2%}$ |
| SI.EXISTS | 1.658 µs | 191.278 万次/秒 | $\color{#1a7f37}\text{-9%}$ |
| SI.RANGE | 17.17 µs | 11.806 万次/秒 | $\color{#0969da}\text{-16%}$ |
| SI.REM | 27.07 µs | 8.110 万次/秒 | $\color{#0969da}\text{-19%}$ |
| APPEND | 20.18 µs | 56.689 万次/秒 | $\color{#0969da}\text{-51%}$ |
| DECRBY | 3.075 µs | 114.116 万次/秒 | $\color{#0969da}\text{-53%}$ |
| GET | 1.396 µs | 190.006 万次/秒 | $\color{#0969da}\text{-11%}$ |
| GETDEL | 8.726 µs | 57.339 万次/秒 | $\color{#0969da}\text{-52%}$ |
| GETRANGE | 190.8 ns | 548.847 万次/秒 | $\color{#1a7f37}\text{-6%}$ |
| INCRBY | 8.12 µs | 116.306 万次/秒 | $\color{#0969da}\text{-52%}$ |
| MGET | 10.18 µs | 52.274 万次/秒 | $\color{#0969da}\text{-13%}$ |
| MSET | 25.02 µs | 22.925 万次/秒 | $\color{#0969da}\text{-28%}$ |
| SET | 8.494 µs | 123.870 万次/秒 | $\color{#0969da}\text{-53%}$ |
| SETRANGE | 9.97 µs | 55.991 万次/秒 | $\color{#0969da}\text{-51%}$ |
| STRLEN | 334.8 ns | 611.247 万次/秒 | $\color{#1a7f37}\text{+1%}$ |
| XADD | 21.5 µs | 69.930 万次/秒 | $\color{#0969da}\text{-43%}$ |
| XDEL | 28.17 µs | 31.066 万次/秒 | $\color{#0969da}\text{-40%}$ |
| XLEN | 212.2 ns | 491.400 万次/秒 | $\color{#1a7f37}\text{-5%}$ |
| XRANGE | 22.76 µs | 7.770 万次/秒 | $\color{#0969da}\text{-19%}$ |
| XREAD | 21.6 µs | 7.770 万次/秒 | $\color{#0969da}\text{-18%}$ |
| TDIGEST.ADD | 6.174 µs | 53.362 万次/秒 | $\color{#0969da}\text{-42%}$ |
| TDIGEST.BYRANK | 8.605 µs | 210.393 万次/秒 | $\color{#0969da}\text{-32%}$ |
| TDIGEST.CDF | 10.59 µs | 106.689 万次/秒 | $\color{#0969da}\text{-30%}$ |
| TDIGEST.QUANTILE | 9.608 µs | 256.213 万次/秒 | $\color{#0969da}\text{-31%}$ |
| TS.ADD | 45.21 µs | 11.967 万次/秒 | $\color{#0969da}\text{-20%}$ |
| TS.GET | 3.319 µs | 164.799 万次/秒 | $\color{#0969da}\text{-23%}$ |
| TS.INCRBY | 42.69 µs | 6.349 万次/秒 | $\color{#0969da}\text{-24%}$ |
| TS.RANGE | 52.34 µs | 2.300 万次/秒 | $\color{#0969da}\text{-11%}$ |
| ZADD | 16.56 µs | 50.813 万次/秒 | $\color{#0969da}\text{-37%}$ |
| ZCARD | 1.098 µs | 653.595 万次/秒 | $\color{#1a7f37}\text{-10%}$ |
| ZCOUNT | 77.73 µs | 1.563 万次/秒 | $\color{#0969da}\text{-24%}$ |
| ZINCRBY | 13.43 µs | 11.692 万次/秒 | $\color{#1a7f37}\text{-9%}$ |
| ZPOPMIN | 28.23 µs | 7.082 万次/秒 | $\color{#1a7f37}\text{-7%}$ |
| ZRANGE | 9.684 µs | 34.590 万次/秒 | $\color{#0969da}\text{-19%}$ |
| ZRANK | 31.76 µs | 9.390 万次/秒 | $\color{#1a7f37}\text{-7%}$ |
| ZREM | 42.02 µs | 6.464 万次/秒 | $\color{#1a7f37}\text{+1%}$ |
| ZREVRANGE | 9.264 µs | 24.096 万次/秒 | $\color{#0969da}\text{-20%}$ |
| ZSCORE | 590.1 ns | 203.128 万次/秒 | $\color{#0969da}\text{-16%}$ |
---
### 运行环境:macOS (Apple M2 Max)
- **CPU**:`Apple M2 Max (12 核)`
- **内存**:`64.0 GB`
- **系统**:`macOS 26.5.1 (Darwin 25.5.0) (aarch64)`
- **编译器**:`rustc 1.98.0 (88d9e12ae 2026-08-18)`
#### WeDb (嵌入式引擎) vs Redis (v8.10.1 独立服务) 性能对比
> **测试说明**:
> - WeDb:进程内直接调用,基于 LSM 磁盘持久化(WAL 日志)
> - Redis:独立服务,开启 AOF 持久化(appendfsync everysec),通过本地 Unix Domain Socket 通信
**综合性能对比**:WeDb 加权吞吐量 **38.314 万次/秒** | Redis 加权吞吐量 **1.228 万次/秒** | WeDb 综合性能倍数 **$\color{#1a7f37}\text{31.2x}$**
| SET | 29.66 µs | 50.025 万次/秒 | 531.2 µs | 0.808 万次/秒 | $\color{#1a7f37}\text{61.9x}$ |
| GET | 6.999 µs | 63.211 万次/秒 | 76.74 µs | 1.587 万次/秒 | $\color{#1a7f37}\text{39.8x}$ |
| MSET | 152.2 µs | 5.247 万次/秒 | 561.2 µs | 0.770 万次/秒 | $\color{#1a7f37}\text{6.8x}$ |
| MGET | 22.37 µs | 28.580 万次/秒 | 328.5 µs | 0.825 万次/秒 | $\color{#1a7f37}\text{34.6x}$ |
| INCRBY | 136.6 µs | 25.138 万次/秒 | 1.354 ms | 0.503 万次/秒 | $\color{#1a7f37}\text{50.0x}$ |
| HSET | 48.29 µs | 25.536 万次/秒 | 535.7 µs | 0.915 万次/秒 | $\color{#1a7f37}\text{27.9x}$ |
| HGET | 8.04 µs | 41.391 万次/秒 | 89.16 µs | 1.614 万次/秒 | $\color{#1a7f37}\text{25.6x}$ |
| LPUSH | 141.9 µs | 5.299 万次/秒 | 464.4 µs | 0.800 万次/秒 | $\color{#1a7f37}\text{6.6x}$ |
| LPOP | 40.24 µs | 7.262 万次/秒 | 153.5 µs | 1.446 万次/秒 | $\color{#1a7f37}\text{5.0x}$ |
| SADD | 37.58 µs | 30.386 万次/秒 | 544.4 µs | 0.888 万次/秒 | $\color{#1a7f37}\text{34.2x}$ |
| SISMEMBER | 4.583 µs | 77.519 万次/秒 | 85.2 µs | 1.284 万次/秒 | $\color{#1a7f37}\text{60.4x}$ |
| ZADD | 52.37 µs | 28.927 万次/秒 | 241.8 µs | 1.519 万次/秒 | $\color{#1a7f37}\text{19.0x}$ |
| ZRANGE | 33.37 µs | 26.233 万次/秒 | 113.1 µs | 1.256 万次/秒 | $\color{#1a7f37}\text{20.9x}$ |
| DEL | 16.33 µs | 42.882 万次/秒 | 164.2 µs | 1.916 万次/秒 | $\color{#1a7f37}\text{22.4x}$ |
| EXISTS | 12.33 µs | 117.069 万次/秒 | 50.87 µs | 2.287 万次/秒 | $\color{#1a7f37}\text{51.2x}$ |
#### 性能回归测试
**综合加权性能**:加权估算吞吐量 **129.193 万次/秒** | 加权延迟变动 **$\color{#1a7f37}\text{+12%}$**
| BITCOUNT | 8.083 µs | 218.103 万次/秒 | $\color{#cf222e}\text{+22%}$ |
| BITPOS | 397.9 ns | 310.174 万次/秒 | $\color{#0969da}\text{-48%}$ |
| GETBIT | 1.187 µs | 195.122 万次/秒 | $\color{#cf222e}\text{+32%}$ |
| SETBIT | 79.33 µs | 43.649 万次/秒 | $\color{#cf222e}\text{+34%}$ |
| BF.ADD | 433.3 µs | 1.130 万次/秒 | $\color{#cf222e}\text{+88%}$ |
| BF.EXISTS | 845.8 ns | 226.142 万次/秒 | $\color{#1a7f37}\text{+10%}$ |
| BF.INFO | 340.6 ns | 388.651 万次/秒 | $\color{#cf222e}\text{+80%}$ |
| CF.ADD | 65.49 µs | 10.668 万次/秒 | $\color{#cf222e}\text{+145%}$ |
| CF.DEL | 67.79 µs | 12.309 万次/秒 | $\color{#1a7f37}\text{+13%}$ |
| CF.EXISTS | 869.4 ns | 145.560 万次/秒 | $\color{#cf222e}\text{+25%}$ |
| BATCH_COMMIT | 67.24 µs | 22.227 万次/秒 | $\color{#cf222e}\text{+20%}$ |
| DEL | 85.12 µs | 30.386 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| EXISTS | 5.666 µs | 299.850 万次/秒 | $\color{#1a7f37}\text{-0%}$ |
| NAMESPACE | 69.91 µs | 52.192 万次/秒 | $\color{#cf222e}\text{+35%}$ |
| GEOADD | 77.12 µs | 42.123 万次/秒 | $\color{#1a7f37}\text{+12%}$ |
| GEODIST | 559.4 ns | 222.222 万次/秒 | $\color{#cf222e}\text{+16%}$ |
| GEOHASH | 413.6 ns | 276.625 万次/秒 | $\color{#0969da}\text{-57%}$ |
| GEOPOS | 366.7 ns | 334.448 万次/秒 | $\color{#cf222e}\text{+25%}$ |
| HDEL | 80.33 µs | 10.022 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| HEXISTS | 411 ns | 293.600 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| HGET | 817.2 ns | 139.237 万次/秒 | $\color{#1a7f37}\text{+15%}$ |
| HGETALL | 37.58 µs | 7.553 万次/秒 | $\color{#1a7f37}\text{-8%}$ |
| HINCRBY | 68.16 µs | 64.893 万次/秒 | $\color{#1a7f37}\text{-5%}$ |
| HKEYS | 20.74 µs | 8.006 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| HLEN | 317.2 ns | 396.668 万次/秒 | $\color{#1a7f37}\text{-7%}$ |
| HMGET | 10.79 µs | 47.081 万次/秒 | $\color{#1a7f37}\text{+15%}$ |
| HSET | 56.91 µs | 45.310 万次/秒 | $\color{#1a7f37}\text{0%}$ |
| HVALS | 22.16 µs | 7.435 万次/秒 | $\color{#0969da}\text{-11%}$ |
| PFADD | 68.2 µs | 9.718 万次/秒 | $\color{#cf222e}\text{+16%}$ |
| PFCOUNT | 41.79 µs | 2.978 万次/秒 | $\color{#1a7f37}\text{-8%}$ |
| PFMERGE | 51.37 µs | 5.921 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| JSON.ARRLEN | 17.91 µs | 92.421 万次/秒 | $\color{#cf222e}\text{+100%}$ |
| JSON.DEL | 93.7 µs | 26.674 万次/秒 | $\color{#1a7f37}\text{+5%}$ |
| JSON.GET | 15.66 µs | 150.263 万次/秒 | $\color{#cf222e}\text{+23%}$ |
| JSON.NUMINCRBY | 17.79 µs | 55.835 万次/秒 | $\color{#1a7f37}\text{0%}$ |
| JSON.SET | 56.29 µs | 40.683 万次/秒 | $\color{#cf222e}\text{+20%}$ |
| JSON.TYPE | 5.208 µs | 200.200 万次/秒 | $\color{#cf222e}\text{+20%}$ |
| LINDEX | 533.3 ns | 230.203 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| LLEN | 237.8 ns | 439.754 万次/秒 | $\color{#1a7f37}\text{+13%}$ |
| LPOP | 6.457 µs | 42.863 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| LPUSH | 55.37 µs | 63.211 万次/秒 | $\color{#1a7f37}\text{-0%}$ |
| LRANGE | 31.2 µs | 4.318 万次/秒 | $\color{#cf222e}\text{+22%}$ |
| LREM | 129.3 µs | 10.910 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| LSET | 4.541 µs | 54.585 万次/秒 | $\color{#cf222e}\text{+300%}$ |
| LTRIM | 194.2 µs | 0.706 万次/秒 | $\color{#1a7f37}\text{+4%}$ |
| RPOP | 5.29 µs | 47.081 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| RPUSH | 75.45 µs | 52.219 万次/秒 | $\color{#0969da}\text{-51%}$ |
| SADD | 76.66 µs | 45.310 万次/秒 | $\color{#cf222e}\text{+20%}$ |
| SCARD | 353.7 ns | 343.407 万次/秒 | $\color{#cf222e}\text{+29%}$ |
| SISMEMBER | 666.2 ns | 165.673 万次/秒 | $\color{#0969da}\text{-15%}$ |
| SMEMBERS | 20.83 µs | 8.368 万次/秒 | $\color{#1a7f37}\text{-0%}$ |
| SPOP | 1.262 ms | 0.093 万次/秒 | $\color{#1a7f37}\text{-0%}$ |
| SRANDMEMBER | 115.5 µs | 7.794 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| SREM | 121.5 µs | 11.112 万次/秒 | $\color{#1a7f37}\text{-1%}$ |
| SI.ADD | 142.5 µs | 53.362 万次/秒 | $\color{#1a7f37}\text{+12%}$ |
| SI.CARD | 236.5 ns | 562.114 万次/秒 | $\color{#1a7f37}\text{+1%}$ |
| SI.EXISTS | 647.9 ns | 209.512 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| SI.RANGE | 15.91 µs | 18.185 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| SI.REM | 91.49 µs | 10.106 万次/秒 | $\color{#1a7f37}\text{+8%}$ |
| APPEND | 88.83 µs | 40.016 万次/秒 | $\color{#0969da}\text{-56%}$ |
| DECRBY | 62.74 µs | 85.763 万次/秒 | $\color{#1a7f37}\text{-10%}$ |
| GET | 666.1 ns | 234.412 万次/秒 | $\color{#1a7f37}\text{+6%}$ |
| GETDEL | 442.2 µs | 17.268 万次/秒 | $\color{#cf222e}\text{+128%}$ |
| GETRANGE | 114.1 ns | 989.120 万次/秒 | $\color{#1a7f37}\text{+4%}$ |
| INCRBY | 97.58 µs | 82.850 万次/秒 | $\color{#1a7f37}\text{-3%}$ |
| MGET | 4.874 µs | 60.024 万次/秒 | $\color{#1a7f37}\text{+11%}$ |
| MSET | 139.3 µs | 20.169 万次/秒 | $\color{#1a7f37}\text{+15%}$ |
| SET | 116.7 µs | 75.075 万次/秒 | $\color{#1a7f37}\text{+14%}$ |
| SETRANGE | 158.5 µs | 35.829 万次/秒 | $\color{#0969da}\text{-51%}$ |
| STRLEN | 96.56 ns | 1197.175 万次/秒 | $\color{#1a7f37}\text{-1%}$ |
| XADD | 49.99 µs | 57.176 万次/秒 | $\color{#1a7f37}\text{+2%}$ |
| XDEL | 55.87 µs | 22.862 万次/秒 | $\color{#cf222e}\text{+23%}$ |
| XLEN | 163.6 ns | 626.174 万次/秒 | $\color{#1a7f37}\text{+12%}$ |
| XRANGE | 34.04 µs | 9.099 万次/秒 | $\color{#1a7f37}\text{+9%}$ |
| XREAD | 28.37 µs | 8.636 万次/秒 | $\color{#1a7f37}\text{+12%}$ |
| TDIGEST.ADD | 17.54 µs | 30.779 万次/秒 | $\color{#1a7f37}\text{+7%}$ |
| TDIGEST.BYRANK | 11.83 µs | 141.343 万次/秒 | $\color{#1a7f37}\text{+13%}$ |
| TDIGEST.CDF | 17.91 µs | 96.061 万次/秒 | $\color{#cf222e}\text{+19%}$ |
| TDIGEST.QUANTILE | 16.08 µs | 184.672 万次/秒 | $\color{#1a7f37}\text{-7%}$ |
| TS.ADD | 165 µs | 14.203 万次/秒 | $\color{#1a7f37}\text{+1%}$ |
| TS.GET | 697.5 ns | 176.305 万次/秒 | $\color{#1a7f37}\text{+7%}$ |
| TS.INCRBY | 27.2 µs | 6.878 万次/秒 | $\color{#1a7f37}\text{+7%}$ |
| TS.RANGE | 28.58 µs | 3.804 万次/秒 | $\color{#1a7f37}\text{+13%}$ |
| ZADD | 141.1 µs | 38.110 万次/秒 | $\color{#cf222e}\text{+19%}$ |
| ZCARD | 174 ns | 748.503 万次/秒 | $\color{#cf222e}\text{+24%}$ |
| ZCOUNT | 60.24 µs | 2.077 万次/秒 | $\color{#cf222e}\text{+21%}$ |
| ZINCRBY | 106.8 µs | 12.534 万次/秒 | $\color{#cf222e}\text{+22%}$ |
| ZPOPMIN | 14.83 µs | 10.885 万次/秒 | $\color{#1a7f37}\text{+7%}$ |
| ZRANGE | 11.29 µs | 37.523 万次/秒 | $\color{#cf222e}\text{+20%}$ |
| ZRANK | 10.29 µs | 15.535 万次/秒 | $\color{#1a7f37}\text{+5%}$ |
| ZREM | 172.6 µs | 11.348 万次/秒 | $\color{#1a7f37}\text{+3%}$ |
| ZREVRANGE | 12.49 µs | 25.265 万次/秒 | $\color{#1a7f37}\text{+10%}$ |
| ZSCORE | 765.1 ns | 156.838 万次/秒 | $\color{#cf222e}\text{+29%}$ |