在数据库系统教程中,就像教科书数据库系统概念一样,有一个名为 Buffer Pool / Buffer Manager / Pager / 等等的模块。我没有看到太多细节,所以我很好奇如何提高它的并发性能?
例如,假设我们有一个 Trie 索引。如果我们在trie内部进行分页,没有缓冲池,我们可以很容易地让多个线程并发加载或驱逐叶子节点:你所需要做的就是从上到下获取节点的共享锁和排它锁叶节点的父节点。
但是,如果您让缓冲池来处理分页事务,那么我想您可能需要获取缓冲池的排他锁。那么,只有一个线程可以同时加载或逐出页面。
实际上,我已经在数据库实现中尝试过这一点。旧版本没有缓冲池并管理 trie 索引中的分页事物。新版本有一个缓冲池来代替 trie 索引本身来完成这项工作。有一个大锁保护 hashmap,将 Page ID 映射到缓冲池中相应的页面。单线程测试快了 40%,但是,如果有 10 个并发线程,则慢 5 倍!
我想无锁数据结构可能有帮助?但我也猜想这很难直接思考。那么你们是如何设计和实现缓冲池的呢?谢谢!
由于这里的讨论(中文,抱歉),我解决了这个问题。解决方案很简单,只需对缓冲区管理器进行分片即可。通过对页码进行哈希处理,将每个页面委托给一个分片。只要这个哈希函数的结果是均匀分布,多个线程等待同一个锁的概率就会很低。
在我的例子中,我将缓冲区管理器分为 128 个分片,哈希函数只是
page_no % 128
,有 10 个线程,一个简单的基准测试结果看起来相当惊人:
顺便说一句,MySQL似乎也采取这种方法(如果我误解了,请纠正我):https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-buffer-pools.html