如何在自定义键值存储上实现 ACID 事务

本文关键字:实现 ACID 事务 存储 键值 自定义 | 更新日期: 2023-09-27 18:35:46

>我有一个服务器,它公开了类似于文件系统的存储 - 它有文件,文件夹,您可以上传文件,下载文件,复制,移动,删除等。绝对没有办法修改服务器的接口(想象一下FTP)。文件名长度也有限制。

我有多个客户端需要同时使用此存储。它们通信的唯一方法是通过此文件系统接口。不允许用于同步或对等的自定义中央服务器。

如何使用此服务器的接口作为后端,从客户端实现远程键值存储,能够进行 ACID 事务?多个客户端可以同时访问/修改键值存储,并且锁定机制应仅使用公开的服务器文件系统接口来实现。

可以轻松地将该文件系统转换为自定义键值存储,因此我真正需要的是一些将事务添加到任意键值存储的C#/.NET库。我不需要Facebook/Google风格的性能,键值存储将仅用于保存数十GB。


假设远程键值存储允许我string get(id)void put(id, string)delete(id)条目。无法从服务器获取其他信息(甚至不会在错误时引发异常)。鉴于访问存储的唯一客户端将是我的,我应该如何实现它们,以便只有一个客户端可以同时写入/删除(写锁定)?鉴于服务器接口非常非常有限,甚至可能吗?我想解决方案需要一些非常好的加密技巧,但我还没有想出任何可行的方法。


资源

  • 作为灵感,使用 Redis 的分布式锁似乎很有趣

如何在自定义键值存储上实现 ACID 事务

这只是一个粗略的想法,但你基本上需要实现一个"写入时复制"功能,其中当你去修改一个文件时,你会将文件复制到一个相同的文件名和一个特殊的扩展名,这将代表你写更改的工作文件,也代表你的"锁"。如果有人检测到"锁定文件"存在,他们可以检查上次写入锁定文件的时间,如果上次写入早于预设的超时,则可以假定锁定被放弃并删除锁定,如果最后一次写入不早于超时,则阻止对文件的访问。

当客户端修改完文件的复制版本时,它会将文件重命名回原始的"非锁定"文件名,在单个原子操作中覆盖未修改的原始副本。

这为您提供了 ACID 所需的一切,唯一的例外是 Durability,因为放弃的锁需要超时时间才能被检测为放弃。

如果您无法访问文件中的上次写入时间,您仍然可以使用此结构,但您需要添加第三个"元数据"文件来表示锁,而不是文件的"正在进行"副本。该元数据文件将仅将锁创建的时间戳作为其内容。

您可以使用ZoneTree,(https://github.com/koculu/ZoneTree)适用于 .NET 的持久、高性能、事务性和符合 ACID 的键值数据库。

它支持基于时间戳的并发控制。算法解释如下:https://en.wikipedia.org/wiki/Timestamp-based_concurrency_control

OptimisticZoneTree 将非事务性 ZoneTree(LSM 树)转换为符合 ACID 的键值存储。

您可以提取事务实现 (https://github.com/koculu/ZoneTree/tree/main/src/ZoneTree/Transactional) 以转换符合 ACID 的自定义键值存储。