#resp #cache #expiration #async #parser #rust

bin+lib bader-db

支持键过期的键值缓存 RESP 服务器

6 个版本

0.1.5 2023 年 4 月 21 日
0.1.4 2023 年 4 月 20 日

#103缓存

Download history 3/week @ 2024-04-02

58 每月下载次数

MIT 许可证

64KB
1.5K SLoC

Unit Tests Crates.io

BADER-DB (بادِر)

支持键过期的键值缓存 RESP 服务器 ⌛

支持的功能入门基本用法缓存淘汰测试

这是一个 rust 的正在进行中的学习项目,不应在生产中使用。

支持的功能

  • SET、GET 和 DELETE 值 ⚡ — 可设置带或不带过期时间的值。
  • 过期格式 🕰️ — 以秒 (EX) 或毫秒 (PX) 设置过期时间。
  • EXISTS 🏪 — 如果键存在则返回 "true",否则返回 "false"。
  • 被动和主动键淘汰 ⌛ — 与 Redis 的 expire 命令类似的内存高效概率淘汰算法。
  • 内存安全 🛡️ — 确保始终检索到最新值,处理竞争条件。

支持命令

  • SET
  • GET
  • DEL
  • EXISTS

入门

此crate可在crates.io上找到。使用它的最简单方法是将其添加到您的Cargo.toml中,定义依赖关系

[dependencies]
bader-db = "0.1.5"

基本用法

此 TCP 服务器基于 Tokio 构建,用于处理异步连接,请确保将其包含在您的依赖关系中。

use bader_db::run_server;
use anyhow::Result;
use std::time::Duration;

#[tokio::main]
pub async fn main() -> Result<()> {
    env_logger::init();
    let port = std::env::var("PORT").unwrap_or("6379".to_string());
    let sample = 10;
    let threshold = 0.5;
    let frequency = Duration::from_millis(100);
    run_server(format!("127.0.0.1:{}", port).as_str(), sample, threshold, frequency).await;
    Ok(())
}

run_server 方法将接收主机和淘汰算法参数。

缓存淘汰

淘汰算法参数

  • sample
  • frequency
  • threshold

缓存中键的过期是在间隔内进行的,每隔 frequency 触发一次,并在后台完成。执行此任务的方法基于 Redis 的实现,简单而有效。

以下是对淘汰过程的总结,解释得非常清晰

Wait for the next frequency tick.
Randomly select a batch of sample entries from the cache.
Check for and remove any expired entries found in the batch.
If the percentage of removed entries is greater than the threshold, go back to step 2; otherwise, go back to step 1.

这允许用户通过调整阈值和频率的值来控制淘汰的积极性。重要的是要记住,阈值值越高,缓存平均使用的内存就越多。

测试

您可以使用 redis-cli 来测试服务器。简单地安装 redis-cli,然后您可以使用

$ redis-cli set hello world

来获取它

$ redis-cli get hello

这将输出 world

您也可以使用以下方式设置带有过期时间的值

  • 使用 EX 标志来设置秒数
  • 使用 PX 标志来设置毫秒数
$ redis-cli set hello world ex 100

这将使值 world 在 100 秒后被移除。

$ redis-cli set hello world px 100

这将使值 world 在 100 毫秒后被移除。

您还可以运行这个简单的脚本来设置具有不同过期时间的值,并观察日志,以查看驱逐算法的执行过程。

use std::process::Command;
use std::{thread, time};

fn main() {
    println!("Started filling");

    for i in 10..2000 {
        let expiry = 10 * i;
        let sleep_time = time::Duration::from_nanos(100);
        thread::sleep(sleep_time);
        let i = format!("nx{}", i.to_string());
        Command::new("redis-cli")
            .arg("set")
            .arg(i.to_string())
            .arg(i.to_string())
            .arg("PX")
            .arg(expiry.to_string())
            .spawn()
            .expect("ls command failed to start");
    }

    for i in 10..5000 {
        let expiry = 10 * i;
        let sleep_time = time::Duration::from_nanos(100);
        thread::sleep(sleep_time);
        Command::new("redis-cli")
            .arg("set")
            .arg(i.to_string())
            .arg(i.to_string())
            .arg("PX")
            .arg(expiry.to_string())
            .spawn()
            .expect("ls command failed to start");
    }
    println!("Terminated.");
}

依赖项

~13-28MB
~375K SLoC