基于 PostgreSQL 的 KV 方案:pgkv

pgkv 是一个基于 PostgreSQL UNLOGGED 表构建的高性能键值存储工具,定位是让开发者用 PostgreSQL 获得接近内存缓存的读写速度。

pgkv 的“高性能”来源于 UNLOGGED 表跳过了预写日志(WAL)。代价是牺牲崩溃安全性:数据库异常关闭后,表中的数据会被自动清空。因此它明确适用于缓存、会话、幂等键等可丢失场景。

它提供了相当完整的 Redis 风格 API,包括基础 set/get/delete、TTL 过期管理、批量操作、原子 set_nx 和计数器 increment。

目前主要有两个活跃形态:一是 Rust 的 crate(API 接近 Redis),二是 PostgreSQL 扩展(通过 pgkv.set() 等 SQL 函数调用,数据以 JSONB 存储)。

  1. Rust 客户端库 (crate pgkv)

    这是最直接的应用层调用方式,适合在 Rust 项目中使用。它直接连接 PostgreSQL,操作的是底层的 UNLOGGED 表。

    use pgkv::{Store, Config};
    
    fn main() -> pgkv::Result<()> {
        // 连接 PostgreSQL
        let store = Store::connect("postgresql://localhost/mydb")?;
    
        // 基础读写 (值可以是 &[u8] 或 &str)
        store.set("user:1:name", "Alice")?;
        if let Some(value) = store.get("user:1:name")? {
            println!("Name: {}", String::from_utf8_lossy(&value));
        }
        store.delete("user:1:name")?;
    
        // 带 TTL 的写入 (3600秒后过期)
        store.set_ex("session:token", b"data", std::time::Duration::from_secs(3600))?;
    
        // 原子操作:仅当 key 不存在时设置
        if store.set_nx("lock:resource", b"locked")? {
            println!("Lock acquired");
        }
        Ok(())
    }
    

    它还支持批量操作、前缀扫描等,API 设计接近 Redis。

  2. PostgreSQL 扩展 (SQL 函数)**

    这是另一种形态,作为数据库扩展安装后,直接在 SQL 中调用 Redis 风格的函数。数据以 JSONB 形式存储在 pgkv.store 表中。

    -- 安装扩展
    CREATE EXTENSION pgkv;
    
    -- 基本 KV 操作
    SELECT pgkv.set('user:42', '{"name":"Ada","active":true}');
    SELECT pgkv.get('user:42')->>'name';  -- 返回 "Ada"
    
    -- 带 TTL 的设置 (60秒后过期)
    SELECT pgkv.set('session:42', 'active', 60);
    SELECT pgkv.ttl('session:42');        -- 查看剩余时间
    
    -- 原子计数器
    SELECT pgkv.incr('jobs:completed');   -- 自增 1
    

    这个版本支持字符串、哈希、列表、集合、有序集合等数据结构,命令覆盖较全。

pgkv vs pg_memcache:性能对比

pgkv 是在 PostgreSQL 内部用 UNLOGGED 表模拟 KV 操作,而 pg_memcache 是让 PostgreSQL去连接外部的 Memcached 服务。因此,它们的调用方式和性能表现差异很大。

结论先行:如果追求极致的低延迟,pg_memcache 通常更快;如果追求架构简洁、避免引入新组件,pgkv 是更务实的选择。

pg_memcache 的性能逻辑:它的本质是“PostgreSQL 作为 Memcached 的客户端”,数据实际存在 Memcached 的内存里,访问延迟是微秒级的。但代价是引入了额外的组件依赖(需单独部署、运维 Memcached 服务)。

pgkv 的性能边界:

  • 写入:基于 UNLOGGED 表,跳过了 WAL(预写日志),写入速度显著高于普通表。实测显示单次 SET 约 39 µs,而普通表带 WAL 的 SET 约 62 µs(若强制刷新则达 4.3 ms)。
  • 读取:受 PostgreSQL B-tree 索引效率影响,点查性能不错,但无法匹敌纯内存的 Memcached。
  • 关键局限:数据仍持久化在磁盘(与 Memcached 纯内存不同),但崩溃后会全部丢失(这是 UNLOGGED 表的特性),且不会复制到从库,存在单点风险。

pg_memcache 的局限:它不是 PostgreSQL 内部存储,只是“代理”调用。数据完全不持久化,Memcached 重启即全部消失,且无法利用 PostgreSQL 的事务、复制、备份等核心能力。

  • 选 pg_memcache:当你已有 Memcached 集群,且只需要一个从 SQL 内部访问缓存的“快捷方式”时。它能让你在数据库函数、触发器中方便地读写 Memcached,但不要把它当作持久化 KV 存储。
  • 选 pgkv:当你希望避免引入 Redis/Memcached 等额外组件,愿意用牺牲崩溃安全性(数据可丢)来换取比普通表高得多的写入性能,并且希望充分利用 PostgreSQL 的 SQL 能力(如事务、复杂查询)时。它适合缓存、会话、幂等键等可丢失场景。
如果你有魔法,你可以看到一个评论框~