3

我知道 SQL Server 可以在聚集索引中的叶级存储行的数据。我相信 PostgreSQL 不会这样做。如果是这样,它的存储范式是什么?

我的主要问题如下。考虑以下设计和数据(显示在 T-SQL 中):

CREATE TABLE dbo.Tree
    (
    [Key] int NOT NULL,
    ID int NOT NULL
    ) ON [PRIMARY]
GO
ALTER TABLE dbo.Tree ADD CONSTRAINT
    PK_Tree PRIMARY KEY CLUSTERED 
    (
    [Key],
    ID
    ) WITH (...) ON [PRIMARY]

INSERT INTO TREE ([Key], ID) VALUES (1, 1), (1, 2), (1, 3), (1, 4).

由于这是一个两列都作为 PK 的 btree,我是否正确地说“[Key] = 1”只会存储一次,而“ID = [1,2,3,4]”将是单独的值btree,而每个 sé 不会有叶值,因为没有不属于 PK 的行列?

这将如何在 PostgreSQL 中工作?

4

4 回答 4

14

TL;DR 版本 - 无论 DBMS 实现如何,您的键值始终存储在磁盘上。

PostgreSQL 将在磁盘上的页面中存储 4 行,每一行用于您插入的每一行。SQL Server 还将在磁盘上存储 4 行。B树是查找结构,不是页级存储结构。

在底层磁盘级别,PostgreSQL 使用无序磁盘结构来存储数据。发生这种情况是因为由于 MVCC 事务语义,PostgreSQL 可能在任何给定时间维护行的多个副本。每行都有一个 xmin 和 xmax 详细说明当前行的创建和销毁事务 ID。autovacuum 进程执行幽灵记录清理操作。PostgreSQL 中的索引指向堆表结构中的行。这组幻灯片详细介绍了该过程。特别是,您需要查看幻灯片 29 以了解 b-tree 查找是如何发生的,以及查看幻灯片 48-52 以了解数据如何存储在磁盘上的理论讨论。

在 SQL Server 中,您将在叶页上有记录,但只有四行聚集索引将只有 1 个索引级别 - 叶级别。您可以通过运行来验证这一点SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('dbo.Tree'), NULL, NULL, NULL)。您还可以通过以下方式验证 SQL Server 中的物理页面级别:

-- Locate the first page of the index
DBCC IND('test', 'Tree', 1);
GO
-- tell SQL Server to show DBCC output in the message page, not the SQL Server log
DBCC TRACEON (3604);
GO
-- look at nasty, dirty, on page data.
DBCC PAGE(test, 1,155,3);

一旦你看到 DBCC PAGE 输出,你就会恨我。最后,您应该会看到四行如下所示:

Slot 0 Offset 0x60 Length 15

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 15

Memory Dump @0x000000006D6FA060

0000000000000000:   10000c00 01000000 01000000 020000††††...............  

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

Key = 1                              

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

ID = 1                               

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (e2338e2f4a9f)  

这是 SQL Server 存储的实际行数据。您将在整个输出中看到 Key = 1 的多个副本,然后是 ID 信息。可以在此处找到这些命令的支持信息。

PostgreSQL 和 SQL Server 之间差异背后的原因来自 PostgreSQL 的 MVCC 实现。由于我们在 PostgreSQL 中可能有多个行的副本,因此最好将数据的多个副本保留在磁盘上,而不是修改支持的索引结构。只要有可能,PostgreSQL 就会进行仅堆更新,并且只在基础表上发布更新。SQL Server 做同样的事情,只有在可以避免更新支持的索引时才会更新聚集索引(或堆)。

于 2011-03-14T16:53:21.820 回答
2

你是对的 - Postgres 不能做你所要求的。有关详细信息,请参阅问题。

您可以使用该命令实现行的聚类CLUSTER,但这不会在您执行 DML 后使数据保持聚类。

于 2011-03-14T08:02:55.017 回答
2

我知道 SQL Server 可以在聚集索引中的叶级存储行的数据。我相信 PostgreSQL 不会这样做。如果是这样,它的存储范式是什么?

SQL Server其他引擎不同的是,PostgreSQL不会将更改记录的事务的 id 存储在索引中,仅存储在堆中。

索引只是指向堆(并将ctid适当的记录存储为行指针,因此是键的一部分)。

这意味着对于每个查询,即使它可以通过索引查找来满足,仍然应该进行堆查找以确保数据对当前事务的可见性。

如此说来,覆盖索引在以下方面没有多大用处PostgreSQL:因为无论如何都应该进行堆查找,所以引擎可以从堆中获取所有数据。

于 2011-03-14T17:04:37.003 回答
2

如果您想了解有关索引的更多信息,请查看我的SQL 索引教程。

于 2011-03-14T17:53:49.427 回答