wedb_embed 0.1.1

Embedded database engine providing Redis-like APIs, built on fjall / 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 fjall 开发
Documentation

English | 中文

crates.io docs.rs


wedb_embed : Embedded database engine providing Redis-like APIs, built on fjall

Embedded multi-model database engine in Rust, providing Redis-compatible data structures and APIs, built on 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 LSM-Tree and cross-keyspace atomic write batches for crash-safe operations.

Core Architecture & Encoding Design

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)

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)

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)

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)

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)

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)

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)

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)

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

Composite Performance: WeDb Weighted Throughput 1.689 M ops/s | Redis Weighted Throughput 0.024 M ops/s | WeDb Overall Speedup $\color{#1a7f37}\text{70.1x}$

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

Composite Weighted Performance: Weighted Throughput 1.243 M ops/s | Weighted Latency Diff $\color{#0969da}\text{-25%}$

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

Composite Performance: WeDb Weighted Throughput 0.383 M ops/s | Redis Weighted Throughput 0.012 M ops/s | WeDb Overall Speedup $\color{#1a7f37}\text{31.2x}$

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

Composite Weighted Performance: Weighted Throughput 1.292 M ops/s | Weighted Latency Diff $\color{#1a7f37}\text{+12%}$

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%}$

wedb_embed : 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 fjall 开发

基于 Rust 开发的嵌入式多模型数据库引擎,提供与 Redis 兼容的数据结构与操作接口,底层采用 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 LSM-Tree 存储引擎与跨 Keyspace 原子 WriteBatch,保障数据安全。

核心架构与编码设计

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)

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)

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)

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)

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)

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)

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)

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)

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 (紧凑字符串存储)
  • 时间库coarsetimets_ (时间戳)
  • 错误定义thiserror

性能基准与回归测试

运行环境:Ubuntu CI (GitHub Actions Runner)

  • CPUIntel(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}$

命令 WeDb P95延时 WeDb 估算吞吐量 Redis P95延时 Redis 估算吞吐量 WeDb 性能倍数
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%}$

命令 P95延时 估算吞吐量 较基线变化 (%)
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)

  • CPUApple 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}$

命令 WeDb P95延时 WeDb 估算吞吐量 Redis P95延时 Redis 估算吞吐量 WeDb 性能倍数
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%}$

命令 P95延时 估算吞吐量 较基线变化 (%)
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%}$