RID 查找 - 逻辑搜索

RID lookup - logical search

例如我们有 table 和聚簇索引 table1 (col1 int , col2 int , col3 int),为 col1 定义的聚簇索引,为 col2 定义的非聚簇索引,我们写query - select * from table1 where col2 = 'some value' - 优化器进行非聚集索引查找,获取特定行并转到聚集索引以获取未包含在其中的其余数据非聚集索引(在这种情况下执行键查找以获取 col3)。键查找本质上是基于在叶级非聚集索引中找到的聚集索引值的聚集索引查找。

当我们在 table 上没有聚簇索引但我们有非聚簇索引时会发生什么?我知道它将执行 RID 查找,但这在逻辑上是如何工作的?聚簇索引值将使用 b-tree 搜索找到,因为我们有特定顺序的所有行,但是如何在堆 table 中找到它而没有任何特定顺序?根据我的理解,当我们在非聚集索引中找到行(它具有非聚集索引键 + rowid)时,我们需要扫描整个堆 table 以在那里找到这个 rowid,因为我们在那里没有任何顺序并且不能使用 b-tree 导航此行,是否正确?

这是一个概念性的解释。数据库将行存储在数据页上,数据页又存储行。 rid 根据 "physical" 位置直接标识行的位置。实际上,数据页在某种意义上是 "logical",它可以在内存中或磁盘上。

数据库不需要通过面向用户的索引来识别行的位置。它需要识别页面,然后是页面上的偏移量。 rid 包含有关页面和偏移量的信息。这样,页面管理系统就可以直接从rid中找到自己需要的页面了。

无需扫描任何内容即可找到该行。

也就是说,有些事情是数据库必须做的,比如:

  • 页面缓存中是否有数据页?如果没有,那就去拿吧。
  • 内存中的页面是否被修改?如果不允许,是否允许脏读。
  • 行是否锁定?

在理解数据库如何运行时,存储层的管理经常被忽视。就日常编写查询而言,页面管理似乎很管用。然而,访问一行并不是一个简单的操作。数据库在 ACID 属性、可靠性和可伸缩性方面的许多强大功能都依赖于存储管理层。

举个例子可能会有帮助。下面创建一个堆并向其中插入几行

CREATE TABLE Demo(X CHAR(1));

INSERT INTO Demo VALUES ('A'), ('B');

然后您可以使用以下命令查看 RID

SELECT X,
       %%physloc%% as rid, 
       sys.fn_PhysLocFormatter(%%physloc%%) as formatted
FROM Demo

哪个对我来说returns

+---+--------------------+-----------+
| X |        rid         | formatted |
+---+--------------------+-----------+
| A | 0xE700000001000000 | (1:231:0) |
| B | 0xE700000001000100 | (1:231:1) |
+---+--------------------+-----------+

RID 是一个 8 字节的二进制值,它是三个部分的串联。 FileNumber:PageNumber:SlotNumber

每个文件分为 8KB 页,编号从 0 开始,因此可以直接计算给定页码在文件中的偏移量。第 231 页是文件中从字节偏移量 1892352 开始的 8KB 部分 (231 * 8192)。

要定位属于 RID 1:231:1 的行,它只需要从缓冲区管理器(如果需要,它将从磁盘读取)获取相关页面 (1:231) 并转到页面上的第二个插槽(插槽编号从 0 开始)。

每个数据页在页脚中都有一个 slot array,给出页面上每一行的行偏移量。