{"id":13527689,"url":"https://github.com/al8n/stretto","last_synced_at":"2025-04-08T04:15:13.475Z","repository":{"id":39902756,"uuid":"390026116","full_name":"al8n/stretto","owner":"al8n","description":"Stretto is a Rust implementation for Dgraph's ristretto (https://github.com/dgraph-io/ristretto). A high performance memory-bound Rust cache.","archived":false,"fork":false,"pushed_at":"2024-04-27T13:28:35.000Z","size":394,"stargazers_count":398,"open_issues_count":16,"forks_count":28,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-05-01T17:32:37.570Z","etag":null,"topics":["cache","caching","concurrent","rust","rust-crate","rust-lang","rust-library","tinylfu"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/al8n.png","metadata":{"files":{"readme":"README-zh_hans.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"al8n","patreon":"al8n"}},"created_at":"2021-07-27T15:02:38.000Z","updated_at":"2024-05-04T04:32:37.717Z","dependencies_parsed_at":"2022-07-07T22:29:38.588Z","dependency_job_id":"72398500-8e14-4f33-a7a2-ef25cbecadcb","html_url":"https://github.com/al8n/stretto","commit_stats":{"total_commits":116,"total_committers":11,"mean_commits":"10.545454545454545","dds":0.3793103448275862,"last_synced_commit":"effd0b5733d7302aeee113d916d5db32fd4ed79b"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/al8n%2Fstretto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/al8n%2Fstretto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/al8n%2Fstretto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/al8n%2Fstretto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/al8n","download_url":"https://codeload.github.com/al8n/stretto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247773719,"owners_count":20993639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","caching","concurrent","rust","rust-crate","rust-lang","rust-library","tinylfu"],"created_at":"2024-08-01T06:01:57.034Z","updated_at":"2025-04-08T04:15:13.451Z","avatar_url":"https://github.com/al8n.png","language":"Rust","funding_links":["https://github.com/sponsors/al8n","https://patreon.com/al8n"],"categories":["Rust","Libraries","库 Libraries"],"sub_categories":["Caching","缓存 Caching"],"readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003eStretto\u003c/h1\u003e\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n\n[Ristretto](https://github.com/dgraph-io/ristretto) 项目的纯 Rust 实现. \n\n高性能、线程安全、内存绑定的 Rust 缓存。\n\n[English](README.md) | 简体中文\n\n[\u003cimg alt=\"github\" src=\"https://img.shields.io/badge/GITHUB-al8n/Stretto-8da0cb?style=for-the-badge\u0026logo=Github\" height=\"22\"\u003e][Github-url]\n[\u003cimg alt=\"Build\" src=\"https://img.shields.io/github/actions/workflow/status/al8n/stretto/ci.yml?logo=Github-Actions\u0026style=for-the-badge\" height=\"22\"\u003e][CI-url]\n[\u003cimg alt=\"codecov\" src=\"https://img.shields.io/codecov/c/gh/al8n/stretto?style=for-the-badge\u0026token=P175Q03Q1L\u0026logo=codecov\" height=\"22\"\u003e][codecov-url]\n\n[\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-stretto-66c2a5?style=for-the-badge\u0026labelColor=555555\u0026logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K\" height=\"20\"\u003e][doc-url]\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/stretto?style=for-the-badge\u0026logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDUxMiA1MTIiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGQ9Ik0yNTYsMEwzMS41MjgsMTEyLjIzNnYyODcuNTI4TDI1Niw1MTJsMjI0LjQ3Mi0xMTIuMjM2VjExMi4yMzZMMjU2LDB6IE0yMzQuMjc3LDQ1Mi41NjRMNzQuOTc0LDM3Mi45MTNWMTYwLjgxDQoJCQlsMTU5LjMwMyw3OS42NTFWNDUyLjU2NHogTTEwMS44MjYsMTI1LjY2MkwyNTYsNDguNTc2bDE1NC4xNzQsNzcuMDg3TDI1NiwyMDIuNzQ5TDEwMS44MjYsMTI1LjY2MnogTTQzNy4wMjYsMzcyLjkxMw0KCQkJbC0xNTkuMzAzLDc5LjY1MVYyNDAuNDYxbDE1OS4zMDMtNzkuNjUxVjM3Mi45MTN6IiBmaWxsPSIjRkZGIi8+DQoJPC9nPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=\" height=\"22\"\u003e][crates-url]\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/d/stretto?color=critical\u0026logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjQ1MTE3MzMyOTU5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjM0MjEiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkzIiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZGVmcz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwvc3R5bGU+PC9kZWZzPjxwYXRoIGQ9Ik00NjkuMzEyIDU3MC4yNHYtMjU2aDg1LjM3NnYyNTZoMTI4TDUxMiA3NTYuMjg4IDM0MS4zMTIgNTcwLjI0aDEyOHpNMTAyNCA2NDAuMTI4QzEwMjQgNzgyLjkxMiA5MTkuODcyIDg5NiA3ODcuNjQ4IDg5NmgtNTEyQzEyMy45MDQgODk2IDAgNzYxLjYgMCA1OTcuNTA0IDAgNDUxLjk2OCA5NC42NTYgMzMxLjUyIDIyNi40MzIgMzAyLjk3NiAyODQuMTYgMTk1LjQ1NiAzOTEuODA4IDEyOCA1MTIgMTI4YzE1Mi4zMiAwIDI4Mi4xMTIgMTA4LjQxNiAzMjMuMzkyIDI2MS4xMkM5NDEuODg4IDQxMy40NCAxMDI0IDUxOS4wNCAxMDI0IDY0MC4xOTJ6IG0tMjU5LjItMjA1LjMxMmMtMjQuNDQ4LTEyOS4wMjQtMTI4Ljg5Ni0yMjIuNzItMjUyLjgtMjIyLjcyLTk3LjI4IDAtMTgzLjA0IDU3LjM0NC0yMjQuNjQgMTQ3LjQ1NmwtOS4yOCAyMC4yMjQtMjAuOTI4IDIuOTQ0Yy0xMDMuMzYgMTQuNC0xNzguMzY4IDEwNC4zMi0xNzguMzY4IDIxNC43MiAwIDExNy45NTIgODguODMyIDIxNC40IDE5Ni45MjggMjE0LjRoNTEyYzg4LjMyIDAgMTU3LjUwNC03NS4xMzYgMTU3LjUwNC0xNzEuNzEyIDAtODguMDY0LTY1LjkyLTE2NC45MjgtMTQ0Ljk2LTE3MS43NzZsLTI5LjUwNC0yLjU2LTUuODg4LTMwLjk3NnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjM0MjIiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkwIiBjbGFzcz0iIj48L3BhdGg+PC9zdmc+\u0026style=for-the-badge\" height=\"22\"\u003e][crates-url]\n\n\u003cimg alt=\"license\" src=\"https://img.shields.io/badge/License-Apache%202.0/MIT-blue.svg?style=for-the-badge\u0026fontColor=white\u0026logoColor=f5c076\u0026logo=data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyBmaWxsPSIjZmZmZmZmIiBoZWlnaHQ9IjgwMHB4IiB3aWR0aD0iODAwcHgiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI3Ni43MTUgMjc2LjcxNSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgc3Ryb2tlPSIjZmZmZmZmIj4KDTxnIGlkPSJTVkdSZXBvX2JnQ2FycmllciIgc3Ryb2tlLXdpZHRoPSIwIi8+Cg08ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KDTxnIGlkPSJTVkdSZXBvX2ljb25DYXJyaWVyIj4gPGc+IDxwYXRoIGQ9Ik0xMzguMzU3LDBDNjIuMDY2LDAsMCw2Mi4wNjYsMCwxMzguMzU3czYyLjA2NiwxMzguMzU3LDEzOC4zNTcsMTM4LjM1N3MxMzguMzU3LTYyLjA2NiwxMzguMzU3LTEzOC4zNTcgUzIxNC42NDgsMCwxMzguMzU3LDB6IE0xMzguMzU3LDI1OC43MTVDNzEuOTkyLDI1OC43MTUsMTgsMjA0LjcyMywxOCwxMzguMzU3UzcxLjk5MiwxOCwxMzguMzU3LDE4IHMxMjAuMzU3LDUzLjk5MiwxMjAuMzU3LDEyMC4zNTdTMjA0LjcyMywyNTguNzE1LDEzOC4zNTcsMjU4LjcxNXoiLz4gPHBhdGggZD0iTTE5NC43OTgsMTYwLjkwM2MtNC4xODgtMi42NzctOS43NTMtMS40NTQtMTIuNDMyLDIuNzMyYy04LjY5NCwxMy41OTMtMjMuNTAzLDIxLjcwOC0zOS42MTQsMjEuNzA4IGMtMjUuOTA4LDAtNDYuOTg1LTIxLjA3OC00Ni45ODUtNDYuOTg2czIxLjA3Ny00Ni45ODYsNDYuOTg1LTQ2Ljk4NmMxNS42MzMsMCwzMC4yLDcuNzQ3LDM4Ljk2OCwyMC43MjMgYzIuNzgyLDQuMTE3LDguMzc1LDUuMjAxLDEyLjQ5NiwyLjQxOGM0LjExOC0yLjc4Miw1LjIwMS04LjM3NywyLjQxOC0xMi40OTZjLTEyLjExOC0xNy45MzctMzIuMjYyLTI4LjY0NS01My44ODItMjguNjQ1IGMtMzUuODMzLDAtNjQuOTg1LDI5LjE1Mi02NC45ODUsNjQuOTg2czI5LjE1Miw2NC45ODYsNjQuOTg1LDY0Ljk4NmMyMi4yODEsMCw0Mi43NTktMTEuMjE4LDU0Ljc3OC0zMC4wMDkgQzIwMC4yMDgsMTY5LjE0NywxOTguOTg1LDE2My41ODIsMTk0Ljc5OCwxNjAuOTAzeiIvPiA8L2c+IDwvZz4KDTwvc3ZnPg==\" height=\"22\"\u003e\n\n\n\n\u003c/div\u003e\n\n## 特性\n* **内部可变** - 毋须为并发编程而使用 `Arc\u003cRwLock\u003cCache\u003c...\u003e\u003e`，用 `Cache\u003c...\u003e` 或者 `AsyncCache\u003c...\u003e` 就够了。\n* **异同两制** - `stretto` 通过 `crossbeam` 实现同步版本, 也支持runtime agnostic异步。但是本质是统一的。\n    * 在同步版本中，缓存会开启两个额外的操作系统线程。一个是策略线程，另一个为写入线程；\n    * 在异步版本中，缓存会开启两个额外的绿色协程。一个为策略协程，另一个为写入协程。\n* **写入策略** - `stretto` 仅会存储键值对中的值，并不会存储键。\n* **高命中率** - 在 `Dgraph` 开发者独树一帜的录入/撤除策略的加持下，Ristretto 的性能在同级下是坠吼的，跑得比谁都快。\n    * **录入：TinyLFU 算法** - 更高的性能，仅需为每个计数器额外 +12bits。\n    * **撤除：SampledLFU 算法** - 性能比肩 LRU，但在搜索与数据库追踪上更胜一筹。\n* **高吞吐量** - 多种操作处理冲突，带来催人跑的高带宽。\n* **基于权重** - 插入大权重的新缓存项可以淘汰多个低权重的缓存项。（权重可以是任何属性）\n* **完全并行** - 在并行中性能仅会略微降低。新线程？开，都可以开。\n* **可选度量** - 可选的吞吐量、命中率或者其他统计指标的度量衡。\n* **Simple API** - 考察、设定您理想的 `CacheBuilder`/`AsyncCacheBuilder` 参数，然后起飞！🚀\n\n## 目录\n\n- [特性](#特性)\n- [目录](#目录)\n- [安装](#安装)\n- [操作方法](#操作方法)\n  - [示例](#示例)\n    - [同步](#同步)\n    - [异步](#异步)\n  - [配置](#配置)\n    - [num\\_counters](#num_counters)\n    - [max\\_cost](#max_cost)\n    - [key\\_builder](#key_builder)\n    - [buffer\\_size](#buffer_size)\n    - [metrics](#metrics)\n    - [ignore\\_internal\\_cost](#ignore_internal_cost)\n    - [cleanup\\_duration](#cleanup_duration)\n    - [update\\_validator](#update_validator)\n    - [callback](#callback)\n    - [coster](#coster)\n    - [hasher](#hasher)\n- [鸣谢](#鸣谢)\n- [许可](#许可)\n\n## 安装\n- 使用同步缓存\n```toml\n[dependencies]\nstretto = \"0.8\"\n```\n或\n```toml \n[dependencies]\nstretto = { version = \"0.8\", features = [\"sync\"] }\n```\n\n\n- 使用异步缓存\n```toml \n[dependencies]\nstretto = { version = \"0.8\", features = [\"async\"] }\n```\n\n- 同步异步同时使用\n```toml \n[dependencies]\nstretto = { version = \"0.8\", features = [\"full\"] }\n```\n\n## 操作方法\n### 示例\n#### 同步\n```rust\nuse stretto::{Cache, DefaultKeyBuilder};\nuse std::time::Duration;\n\nfn main() {\n    let c = Cache::new(12960, 1e6 as i64, DefaultKeyBuilder::default()).unwrap();\n\n    // 设定一个键为 \"a\", 权为 1 的值\n    c.insert(\"a\", \"a\", 1);\n\n    // 设定一个键为 \"a\"，权为 1 的带生存期的值\n    c.insert_with_ttl(\"b\", \"b\", 1, Duration::from_secs(3));\n    \n    // 等待值存入缓存中\n    c.wait().unwrap();\n\n    // 当尝试访问值时，会返回一个包含了 RwLockReadGuard 的 ValueRef\n    // 当完成使用这个值时，ValueRef 需要释放\n    let v = c.get(\u0026\"a\").unwrap();\n    assert_eq!(v.value(), \u0026\"a\");\n    // 手动释放\n    v.release(); // 或者析构 v\n\n    // 离开作用域后锁会被自动释放\n    {\n        // 当尝试访问值时，会返回一个包含了 RwLockReadGuard 的 ValueRef\n        // 当完成使用这个值时，ValueRef 需要释放\n\n        let mut v = c.get_mut(\u0026\"a\").unwrap();\n        v.write(\"aa\");\n        assert_eq!(v.value(), \u0026\"aa\");\n        // 释放值\n    }\n\n    // 如果只对 v 操作一次\n    let v = c.get_mut(\u0026\"a\").unwrap();\n    v.write_once(\"aaa\");\n\n    let v = c.get(\u0026\"a\").unwrap();\n    assert_eq!(v.value(), \u0026\"aaa\");\n    v.release();\n\n    // 缓存清零\n    c.clear().unwrap();\n    // 等待所有操作完成\n    c.wait().unwrap();\n    assert!(c.get(\u0026\"a\").is_none());\n}\n```\n\n#### 异步\n```rust\nuse stretto::{AsyncCache, DefaultKeyBuilder};\nuse std::time::Duration;\n\n#[tokio::main]\nasync fn main() {\n    // 在这个例子中, 我们使用tokio运行时, 所以需要将tokio::spawn作为spawner在构建缓存的时候\n    let c = AsyncCache::new(12960, 1e6 as i64, DefaultKeyBuilder::default(), tokio::spawn).unwrap();\n\n    // 设定一个键为 \"a\" 权为 1 的值\n    c.insert(\"a\", \"a\", 1).await;\n\n    // 设定一个键为 \"a\"，权为 1，生存期为 1s 的值\n    c.insert_with_ttl(\"b\", \"b\", 1, Duration::from_secs(1)).await;\n    \n    // 等待值存入缓存中\n    c.wait().await.unwrap();\n\n    \n    // 当尝试访问值时，会返回一个包含了 RwLockReadGuard 的 ValueRef\n    // 当完成使用这个值时，ValueRef 需要释放\n    let v = c.get(\u0026\"a\").unwrap();\n    assert_eq!(v.value(), \u0026\"a\");\n    // 释放值\n    v.release(); // 或者直接析构 v\n\n    // 离开作用域时锁会自动释放\n    {\n        // 当尝试访问值时，会返回一个包含了 RwLockReadGuard 的 ValueRef\n        // 当完成使用这个值时，ValueRef 需要释放\n        let mut v = c.get_mut(\u0026\"a\").unwrap();\n        v.write(\"aa\");\n        assert_eq!(v.value(), \u0026\"aa\");\n        // 释放值\n    }\n    \n\n    // 如果只对 v 操作一次\n    let v = c.get_mut(\u0026\"a\").unwrap();\n    v.write_once(\"aaa\");\n\n    let v = c.get(\u0026\"a\").unwrap();\n    println!(\"{}\", v);\n    assert_eq!(v.value(), \u0026\"aaa\");\n    v.release();\n\n    // 缓存清空\n    c.clear().await.unwrap();\n    // 等待操作完成\n    c.wait().await.unwrap();\n\n    assert!(c.get(\u0026\"a\").is_none());\n}\n```\n### 配置\n如果希望定制缓存，请使用 `CacheBuilder` 来创建 `Cache` 对象。\n\n#### num_counters\n\n`num_counters` （计数器数）是用于保存录入与淘汰信息的 4 位访问计数器的数目。Dgraph 的开发者们在将其设为约 10 倍于缓存容量的时候获得了不错的性能。\n\n比如，在每个缓存项的权为 1 且 `max_cost` 设定为 100 时，应将 `num_counters` 设为 1,000；或者如果缓存项权值不等，而期望缓存可以容纳约 10,000 项时，应将 `num_counter` 设为 100,000——应当考虑的是可以装满缓存的**唯一键值数量**而非 `max_cost` 的值。\n\n#### max_cost\n\n`max_cost` （最大权值和）是缓存是否进行撤除操作的参考。在 `max_cost` 为 100 时，如果插入一个权为 1 的项使得缓存内总权值之和为 101，那么一个缓存项会被淘汰。\n\n`max_cost` 可以被用于表示缓存的最大体积（字节）。举个例子，如果 `max_cost` 为 1,000,000 (1 MB，1 兆字节) 而缓存已经装入 1,000 个 1 KB 的项，一个被接收的新缓存项会导致 5 个 1KB 的缓存项被撤除。\n\n权值可以是任意属性，亦即 `max_cost` 也可以指代任何属性的权值的和的最大值。\n\n#### key_builder\n\n```rust\npub trait KeyBuilder {\n    type Key: Hash + Eq + ?Sized;\n\n    /// hash_index 用于将键哈希运算成一个 u64 值\n    fn hash_index\u003cQ\u003e(\u0026self, key: \u0026Q) -\u003e u64 \n        where \n            Self::Key: core::borrow::Borrow\u003cQ\u003e,\n            Q: Hash + Eq + ?Sized;\n\n    /// 如果希望使用一个 128 位哈希，需要实现此方法。\n    /// 默认返回 0\n    fn hash_conflict\u003cQ\u003e(\u0026self, key: \u0026Q) -\u003e u64 \n        where \n            Self::Key: core::borrow::Borrow\u003cQ\u003e,\n            Q: Hash + Eq + ?Sized;\n    { 0 }\n\n    /// 将键进行哈希运算，返回 128 位哈希结果。\n    fn build_key\u003cQ\u003e(\u0026self, k: \u0026Q) -\u003e (u64, u64) \n        where \n            Self::Key: core::borrow::Borrow\u003cQ\u003e,\n            Q: Hash + Eq + ?Sized;\n    {\n        (self.hash_index(k), self.hash_conflict(k))\n    }\n}\n```\n\n`KeyBuilder`（键生成器）是使用于所有的键的哈希算法。`Stretto` 并不会存储键的真正的值，\n而是会将其使用 `KeyBuilder` 处理。\n`Stretto` 内建了两套默认的键生成器，\n一套为 `TransparentKeyBuilder`（透明键生成器），另一套为 `DefaultKeyBuilder`（默认键生成器）。\n只有当键类型实现了 `TransparentKey` 特性时，才可以使用相比 `DefaultKeyBuider` 更快的 `TransparentKeyBuilder`。\n\n用户可以通过实现 `KeyBuilder` 特质另起炉灶，自己实现一套键生成器。\n\n注意当希望使用 128 位哈希时请将 `(u64, u64)` 中的两项都用到。如果只想使用 64 位哈希可以将元组中第一个（索引为 0）的值置 0。\n\n#### buffer_size\n\n`buffer_size`（缓存大小）是插入缓存的大小。Dgraph 的开发者们发现设为 32 × 1024 （的整倍数？）时性能很好。\n\n如果偶然发现插入性能大幅下降，同时出现较多冲突（通常并不会），请尝试将该值设定为更高的 32 × 1024 的整倍数。缓存的内部机制调教得当，用户一般不会需要修改该值。\n\n#### metrics\n\nMetrics（度量）应当在需要实时日志记录多种状态信息的时候设置为 `true`。之所以并未设定成默认启用，是因为可能会降低 10% 的吞吐量。\n#### ignore_internal_cost\n\n设定为 `true` 时缓存将会忽略存储值的内部开销，这在开销不以比特为单位时很有用。不过谨记这会导致更高的内存占用。\n\n#### cleanup_duration\n\n默认情况下缓存会每 500 毫秒清理一次过期的值\n\n#### update_validator\n\n```rust\npub trait UpdateValidator: Send + Sync + 'static {\n    type Value: Send + Sync + 'static;\n\n    /// should_update 在一个已经存在于缓存中的值被更新时调用\n    fn should_update(\u0026self, prev: \u0026Self::Value, curr: \u0026Self::Value) -\u003e bool;\n}\n```\n\n默认状态下，缓存总是会更新已经在缓存中的值。\n该特性用于确认该值是否被更新。\n#### callback\n\n```rust\npub trait CacheCallback: Send + Sync + 'static {\n    type Value: Send + Sync + 'static;\n\n    /// on_exit 在一个值被移除 (remove) 出缓存的时候调用。\n    /// 可以用于实现手动内存释放。\n    /// 在撤除 (evict) 或者拒绝 (reject) 值的时候亦会被调用\n    fn on_exit(\u0026self, val: Option\u003cSelf::Value\u003e);\n\n    /// on_evict 在撤除值的时候会被调用，同时会将哈希键、值和权传给函数。\n    fn on_evict(\u0026self, item: Item\u003cSelf::Value\u003e) {\n        self.on_exit(item.val)\n    }\n\n    /// on_reject 会被 policy 为每个所拒绝的值调用\n    fn on_reject(\u0026self, item: Item\u003cSelf::Value\u003e) {\n        self.on_exit(item.val)\n    }\n}\n```\n\nCacheCallBack（缓存回调）被用于定制在事件发生时对值的额外操作。\n\n#### coster\n\n```rust\npub trait Coster: Send + Sync + 'static {\n    type Value: Send + Sync + 'static;\n\n    /// cost 函数对值进行求值并返回对应的权重，该函数\n    /// 会在一个新值插入或一个值更新为 0 权值时被调用\n    fn cost(\u0026self, val: \u0026Self::Value) -\u003e i64;\n}\n```\n\n`Cost` 是一个可以传给 `CacheBuilder` 进行运行时权重求值的特征，并且仅仅对未丢弃的 `insert` 函数调用使用——这在计算权值相当耗时或者耗资源时非常有用，尤其是当用户不想在迟早被析构的值上浪费时间时。\n\n用户可以通过如下方法使得 Stretto 使用自己定制的 Coster 特征：\n\n1. 将 `Coster` 值设定为自己的 `Coster` 实现；\n2. 在插入新缓存项或更新缓存项，调用 `insert`时，将 `cost` 设为 0。\n\n#### hasher\n\n缓存的哈希器，默认为 `SipHasher`。\n\n## 鸣谢\n- 感谢 Dgraph 的开发者们，提供了如此亦可赛艇的 [Ristretto](https://github.com/dgraph-io/ristretto) Go 语言实现。\n\n## 许可\n\n\u003csup\u003e\n根据您的选择，在 \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003eApache 许可证\n2.0 版\u003c/a\u003e 或 \u003ca href=\"https://opensource.org/licenses/MIT\"\u003eMIT 许可证\u003c/a\u003e 下进行授权。\n\n\u003c/sup\u003e\n\n\u003cbr\u003e\n\n\u003csub\u003e\n除非您明确说明，任何由您有意提交以纳入本项目的贡献，如Apache-2.0许可证所定义的，应按上述规定进行双重许可，没有任何附加条款或条件。\n\u003c/sub\u003e\n\n[Github-url]: https://github.com/al8n/stretto/\n[CI-url]: https://github.com/al8n/stretto/actions/workflows/ci.yml\n[doc-url]: https://docs.rs/stretto\n[crates-url]: https://crates.io/crates/stretto\n[codecov-url]: https://app.codecov.io/gh/al8n/stretto/\n[license-url]: https://opensource.org/licenses/Apache-2.0\n[rustc-url]: https://github.com/rust-lang/rust/blob/master/RELEASES.md\n[license-apache-url]: https://opensource.org/licenses/Apache-2.0\n[license-mit-url]: https://opensource.org/licenses/MIT\n[rustc-image]: https://img.shields.io/badge/rustc-1.52.0--nightly%2B-orange.svg?style=for-the-badge\u0026logo=Rust\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fal8n%2Fstretto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fal8n%2Fstretto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fal8n%2Fstretto/lists"}