Skip to content

🐯 [存储] ScanRange 全程持读锁,写入与刷盘被阻塞 · memtable.go #142

Description

@github-actions

来自 #139 的架构级评审建议。不阻塞合入,仅供参考是否有更好的架构解法。

⚠️ [重要 · 存储] ScanRange 全程持读锁,写入与刷盘被阻塞 storage/zstorage/memtable.go:188

问题根因ScanRangem.mu.RLock() 保护下完成整个遍历过程(可能涉及数千条记录的 JSON 解析 + 谓词评估)。期间所有写入操作(Put/Delete)以及刷盘操作(StartFlush 内部将 active→dirty)都会因为尝试获取写锁而阻塞。且当前的 firstGTE 遍历跳表的 Next[0] 链表在没有写锁保护的情况下原本是并发不安全的(见下一个 finding)。

为什么低级解法不够:泛解法「加注释说此操作应快」不解决问题的本质。把锁换成 sync.RWMutex 已是最低保障,但读锁持有时间不可控。

架构级方案:此问题与上一个 finding 的快照迭代器方案共享同一解法:创建快照后立即释放读锁。两个问题可一并解决。此外,对扫描这类端到端延迟容忍(几百 ms)但吞吐敏感的操作,可采用无锁遍历 + 版本号校验:遍历前记录跳表版本号(每次写入递增),遍历中不持任何锁,遍历结束后校验版本号是否变化,若变化则重试。在低更新频率的热窗口中,重试概率极低,而吞吐提升显著。

代价/收益:无锁遍历+版本号方案代价在于:重试逻辑增加了代码复杂度、偶发的时间消耗;快照方案代价是内存和实现复杂度。收益是写路径完全不阻塞,适合高写入负载的边缘场景。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions