#key-value #update #store #data-store #mutex #limited

concurrent-kv

带有有限并发更新的键值数据存储

2 个版本

使用旧 Rust 2015

0.2.1 2016 年 11 月 30 日
0.2.0 2016 年 11 月 30 日

#39#limited

MIT 许可证

10KB
118

concurrent-kv

Build Status

许可证

concurrent-kv 使用 MIT 许可证。有关详细信息,请参阅 LICENSE。


lib.rs:

通用的、线程安全的、内存中的键值数据存储

如名称所示,Library 结构体是该模块的主要导出项。一些支持类型和特性也被导出。

Library 结构体支持两种操作:getinsert,它们与 HashMap 集合中的对应操作类似。get 接受一个键并返回一个值。insert 使用提供的值更新数据存储中的提供的键。

Library 的线程安全特性是多重并发原语(Arc、ArcCell 和 Mutex)封装的结果。

Mutex 和 Arc 是标准库的一部分。我们使用互斥锁来防止多个写入者同时添加新键。注意:这微妙但显著地不同于防止对相同键的多次插入。

插入

有两种不同的场景需要考虑:在新的键下插入新值,以及在现有的键下插入新值。

新键下的新值

使用新键插入值需要在 HashMap 中分配额外的空间,并可能重新排列底层数据。为了防止一致性错误,Library 有一个内部的互斥锁(Library.insert_mutex),在插入键之前必须获得该锁。

use concurrent_kv::Library;

let lib: Library<String, i64> = Library::new();
lib.insert("qwerty".into(), 12345);
let val0 = lib.get("qwerty").unwrap();
lib.insert("asdfgh".into(), 67890);
let val1 = lib.get("asdfgh").unwrap();
assert_eq!(val0, 12345.into());
assert_eq!(val1, 67890.into());

现有键下的新值

由于键已经存在,HashMap 不需要分配任何额外的存储(我们只是交换 ArcCell 的内容)。因此,我们可以通过提供 ArcCell 的引用并直接交换来短路插入过程,从而跳过锁定获取。

这种为了性能而进行的权衡引入了“最后写入者胜出”的行为,即对同一键的多次插入。

use concurrent_kv::Library;
use std::sync::Arc;

let lib0: Arc<Library<String, i64>> = Library::new().into();
let val0 = lib0.get("abc");
assert_eq!(val0, None.into());
let lib1 = lib0.clone();
lib0.insert("abc".into(), 123);
let val123 = lib1.get("abc");
assert_eq!(val123, lib0.get("abc"));
assert_eq!(val0, None.into());
lib1.insert("abc".into(), 456);
let val456 = lib1.get("abc");
assert_eq!(val456, Some(456.into()));
assert_eq!(val123, Some(123.into()));

ArcCell

ArcCell 由 crossbeam 包提供。命名和文档糟糕透顶,所以我们在这里尝试提供解释。

如图所示,该类型的定义特性是能够原子地交换堆分配值的内容(即 Cell)。因此,一个更准确的名字可能是 AtomicCell

         A ----> N
  A -\
  A ---> A ----> O
  A --/      /
           A-

         A -\ /- N
  A -\       X
  A ---> A -/ \- > O
  A --/       /
            A-

see: https://internals.rust-lang.org/t/atomic-arc-swap/3588

注意事项

确保单个键的更新只发生一次是 的用户的责任。否则, 将默认采用“最后写入者获胜”的冲突解决策略(这通常不是最终用户期望的行为)。

依赖项

~15KB