【问题标题】:Why we use B+ tree for clustered index rather than hashing?为什么我们将 B+ 树用于聚集索引而不是散列?
【发布时间】:2021-06-07 08:29:52
【问题描述】:

在 MySQL InnoDB 或许多其他数据库引擎中,主键是通过聚集索引实现的。但是使用二级索引搜索后,引擎必须使用二级索引中提供的主键查找聚集索引(如果没有覆盖索引)。

InnoDB 使用 B+ 树作为其聚集索引,它是一个搜索复杂度为O(log n) 的结构,因此我们可以将过程总结如下:

  1. 使用聚集索引: 一次通过,费用O(n)
  2. 使用二级索引: 两通。第一次通过花费O(log n) 一个结果在m 记录中。那么对于每个m 记录,第二遍花费O(log n),因此时间复杂度将为m*O(log n)

我知道在使用hasing时,搜索的时间复杂度可以降低到O(1),所以我想知道为什么这些数据库引擎更喜欢使用B+树而不是hasing技术(例如构建一个KV存储)?是因为记录存储在磁盘上而不是内存中吗?

同时,我还有一个问题,其他一些数据库,比如 RocksDB,使用 KV 存储而不是 B+ 树。他们为什么使用它?

编辑

我想让这个问题更清楚。我发现很多表格都是用auto incrementPK设计的,而不是使用具有实际意义的东西,比如电话号码或IP。所以B+树的优势没有得到充分发挥。例如,B+树擅长在范围内搜索数据,但我在范围内搜索auto incrementPK在实践中很少见。

【问题讨论】:

  • 散列并不比 BTree 快得多;为什么没有充分的理由实施哈希? (我正在阅读原始实施者的想法。)
  • 我猜如果你在 MySQL 中对不是 auto increment 值的特定列值(例如 stringdouble)使用索引,MySQL 仍将使用 B-trees那个。

标签: mysql database indexing innodb


【解决方案1】:

B-tree 索引的一个重要特性是所谓的range scan. Hash 索引没有这个特性。旧 MySQL 表引擎的名称 MyISAM 提供了线索。它代表索引顺序访问方法。 BTREE 索引的固有顺序是一个主要特征。

如果我们有一个表 credit,其中包含例如列 credit_iduser_iddatestampamount,我们可能会使用此查询。

SELECT SUM(amount) amount FROM credit 
 WHERE datestamp >= CURDATE() - INTERVAL 7 DAY
   AND datestamp <  CURDATE();

(datestamp, amount) 上有一个两列 BTREE 索引,MySQL 可以随机访问索引 O(log n) 到第一个符合条件的日期戳,然后为每个连续的符合条件的日期戳依次访问它 O(1)。并且,由于amount在索引中,MySQL可以完全满足从索引中查询。 (它被称为covering index)。 InnoDB 索引隐式包含主键列,即保存所有表数据的聚集索引的键。

大多数大型生产表都为它们定义了几个覆盖索引,选择这些索引来加速在特定应用程序中花费最多时间的查询。

我并不是说 HASH 索引是无用的。离得很远。但很明显,如果没有 BTREE 索引,MySQL 的工作效率就会低得多。

(InnoDB 引擎的代码对插入具有自动递增主键的行进行了许多优化。如果应用程序使用其他东西(例如随机 guid)作为主键,它可能会破坏这些优化。)

【讨论】:

  • 嗨,Jones,我知道范围扫描是 B+tree 的一个优势,但是,正如我在帖子中列出的,我认为在处理 auto inc PK 时不需要此功能,因为自动范围inc PK 没有实际意义。覆盖索引也很好,如果我有col1/col2/col3 的索引,例如搜索col1,col2 会很方便。但是构建索引也需要大量的空间和设计。如果不能使用覆盖索引,比如用col2,col3搜索,就必须用PK回溯聚集索引,这涉及到对B+tree的多轮迭代。
  • 但是你有所有这些问题,而且基于哈希的索引更糟糕。同样,对于 b-tree,您至少有机会使“ORDER BY”子句与 b-tree 排序相匹配。哈希是随机的,任何“ORDER BY”都会导致排序。如果您对行数有一个合理的想法并且知道键的分布并且只想通过键访问单行 - 那么哈希稍微好一点,为什么它们通常更适合文档存储/无 sql 数据库.
  • 感谢您提供这个有趣的答案。请问,为什么像 MySQL 这样的 RDBMS 不使用目录或子目录来存储一行,例如如果users 表的主键是123456,为什么不将所有行存储在目录内的二进制文件中,例如db/users/123456/row_binary_data 或例如db/users/12/34/56/123456?又是因为排序顺序和范围扫描吗?谢谢!
  • 您如何看待 LSM-Trees?他们使用 SSTables(排序字符串表),它们是按键排序的数据段(文件)(这要归功于内存中的 memtable,它本质上是一个 AVL 树,当达到数据阈值时,它会定期清空并写入磁盘 -通常为几 MB)并使用内存中的哈希映射来有效地检索段中的数据。据我所知,这种数据索引还允许有效的范围查询。
【解决方案2】:

有效的散列需要对键的类型、数量和分布有一定的了解。加上处理冲突的复杂性(两个键最终具有相同的哈希值)。空间必须预先分配,可能太小而很快用完,或者太大导致大量资源浪费。

b-trees 在小的时候是有效的,如果有可用的磁盘空间,它可以增长到任何大小。

您引用了操作的数量,但 b-tree 使用简单的比较便宜,散列使用昂贵的复杂算法。因此,在 64,000 条记录的数据库中查找记录位置的七八次比较可能比计算哈希值使用的 CPU 更少。

【讨论】:

  • 当我查看 MySQL 表的许多定义时,它们使用自增主键,我认为它很容易优化。此外,即使他们使用 IP 或电话号码之类的东西,我仍然可以添加一个自动增量主键
  • 同时,为什么其他一些数据库更喜欢 KV 存储?
  • @calvin 可能是因为密钥通常是原始类型,例如int?计算这些数据类型的哈希值相对便宜(int 的哈希值本身就是int,请参阅stackoverflow.com/questions/11890805/…),我也只是在推理您的问题,并想在此评论中分享我的想法.
猜你喜欢
  • 2012-12-21
  • 2019-11-18
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-16
  • 1970-01-01
相关资源
最近更新 更多