多于 1 列的 B 树索引是啥样的?
Posted
技术标签:
【中文标题】多于 1 列的 B 树索引是啥样的?【英文标题】:what does a B-tree index on more than 1 column look like?多于 1 列的 B 树索引是什么样的? 【发布时间】:2010-12-11 12:25:15 【问题描述】:所以我正在阅读索引及其实现,我偶然发现了这个网站,其中简要说明了 b-tree 索引:
http://20bits.com/articles/interview-questions-database-indexes/
b-tree 索引对于仅在单个列上的索引非常有意义,但是假设我创建了一个包含多个列的索引,那么 b-tree 是如何工作的呢? b-tree 中每个节点的值是多少?
例如,如果我有这张桌子:
table customer:
id number
name varchar
phone_number varchar
city varchar
我创建了一个索引:(id, name, city)
然后运行以下查询:
SELECT id, name
FROM customer
WHERE city = 'My City';
这个查询如何利用多列索引,或者除非索引被创建为 (city, id, name) 或 (city, name, id),否则它不使用它?
【问题讨论】:
对于 mysql,请参阅dba.stackexchange.com/a/223159/1876 【参考方案1】:在大多数实现中,键只是一个较长的键,它包含所有键值,并带有分隔符。那里没有魔法;-)
在您的示例中,键值可能类似于
“123499|约翰·多伊|康威,新罕布什尔州” “32144|比尔盖茨|华盛顿州西雅图”这些带有复合键的索引的一个特点是在某些情况下可以使用中间树节点来“覆盖”查询。
例如,如果查询是在给定 ID 的情况下查找 Name 和 City,由于 ID 在索引中排在第一位,因此索引可以通过此高效搜索。一旦进入中间节点,它就可以从key中“解析”Name和City,而不需要去叶子节点读取。
但是,如果查询还想显示电话号码,那么当找到完整记录时,逻辑将沿叶子向下。
【讨论】:
这是一个很好的第一直觉,但实现可能还有更多,例如,数据的类型很重要(Postgres 中常见的数字、可变长度文本和奇异的文本),所以任何连接需要在使用时零碎处理,需要一些数据字典。仅索引扫描也需要工作。此外,Oracle 索引组织表是存储整个表的 B 树 (B*-树),包括非索引列。在所有这些情况下,列数据必须保持分隔以恢复信息。可能的例外:对仅 CHAR 成分的常规索引扫描【参考方案2】:一些实现只是将值按列的顺序用分隔符连接起来。
另一个解决方案是在 b-tree 中简单地有一个 b-tree。当您点击第一列的叶子时,您将获得匹配记录列表和下一列的迷你 b 树,依此类推。因此,索引中指定的列的顺序对于该索引是否对特定查询有用有很大的不同。
这是我上周写的一个相关问题:
Does SQL Server jump leaves when using a composite clustered index?
【讨论】:
【参考方案3】:除了已经描述的“复合键”机制之外,一种可能性是kdtree
,它的工作方式类似于二叉树,但是当您遍历每个级别时,您会循环通过k
维度。也就是说,树的第一级将第一个维度分成两部分,第二级将第二个维度拆分,k+1
th 级再次拆分第一个维度,等等。这允许对任意数量的数据进行有效分区的尺寸。这种方法在“空间”数据库(例如 Oracle Spatial、PostGIS 等)中很常见,但在“常规”多索引表中可能没有那么有用。
http://en.wikipedia.org/wiki/Kd-tree
【讨论】:
【参考方案4】:它可以使用 (id,name,city) 索引来满足“City = ?”谓词,但效率非常低。
为了使用索引来满足这个查询,它需要遍历大部分树结构来寻找所需城市的条目。这仍然可能比扫描表格快一个数量级!
(city,name,id) 的索引将是您查询的最佳索引。它可以轻松找到所有需要的城市条目,并且不需要访问基础表来获取 id 和 name 值。
【讨论】:
【参考方案5】:假设键由 Python 元组 (col1, col2, col3) 表示...索引操作涉及比较 tuple_a
和 tuple_b
...如果您不知道 col1 和col2,但只有 col3,那么它必须读取整个索引(“全索引扫描”),效率不高。
如果您在 (col1, col2, col3) 上有一个索引,那么当 WHERE 子句包含对 (1) 所有 3 列 (2) 的引用时,您可以预期任何 RDBMS 都将使用该索引(以直接方式) col1 和 col2 (3) 都只有 col1。
否则(例如,仅在 WHERE 子句中的 col3),RDBMS 将根本不使用该索引(例如 SQLite),或者将进行全索引扫描(例如 Oracle)[如果没有其他索引更好]。
在您的具体示例中,假设 id 是客户的唯一标识符,让它出现在索引中是没有意义的(除了您的 DBMS 应该为标记为 UNIQUE 的主键或列设置的索引) .
【讨论】:
不适用于 Oracle。索引全扫描、跳过扫描或快速全索引扫描不需要使用前导列。 @David:谢谢。我已经编辑了我的答案,这样人们就不需要推迟对第一句话的判断,直到人们进一步阅读细则;-)【参考方案6】:在 Oracle 中,即使未过滤前导列,也可以使用复合键索引。这是通过三种机制完成的:
-
快速全索引扫描,其中多块读取用于遍历整个索引段。
索引全扫描,其中索引按块的逻辑顺序读取(我相信我读到在最近的版本中,Oracle 可以为此使用多块读取,但实际上您应该依靠单块读取)李>
索引跳过扫描,其中非谓词前导列的基数非常低,允许 Oracle 执行多个索引范围扫描,针对前导列的每个唯一值进行一次。根据我的经验,这些情况非常罕见。
查找 Richard Foote 或 Jonathan Lewis 撰写的文章,了解有关 Oracle 索引内部结构的更多信息。
【讨论】:
【参考方案7】:"索引将按第一个关键元素排序,然后按第二个关键元素,依此类推" https://www.qwertee.io/blog/postgresql-b-tree-index-explained-part-1/
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review以上是关于多于 1 列的 B 树索引是啥样的?的主要内容,如果未能解决你的问题,请参考以下文章
Oracle中创建了索引,啥样的原因可能使索引不能正常使用?