数据库何时在事务中更新其索引?


11

我试图了解插入中同时涉及索引和事务的事件顺序。

例如,Oracle文档指出:

如果在加载数据之前创建[或具有]一个或多个索引,则数据库必须在插入每一行时更新每个索引。

但是,如果我创建一个事务,插入五行,然后提交,会发生什么?是否为每个插入或仅在提交点更新索引?

逻辑告诉我,它们只会在提交点进行更新,因为在提交那些记录之前,更新的索引可能无法使用。但这是真的吗?

如果是这样,当我要插入100万行时,为了获得最佳性能,我应该对所有行进行一次大型提交,而不是对10万条记录进行10次事务处理?当然,我意识到,如果第999,999行失败,则存在更大的回滚风险。

抱歉,如果我的术语有点用完。我不是行业DBA。尽管对Oracle和Postgres是我最常使用的数据库,但是我对特定数据库的兴趣不大,与一般数据库无关。我已经搜索了这个主题,但找不到真正的答案。

Answers:


8

我使用SQL Server和Oracle。可能会有一些例外,但是对于那些平台,通常的答案是数据和索引将同时更新。

我认为,在拥有事务的会话和其他会话的索引何时更新之间进行区分会有所帮助。默认情况下,其他会话在提交事务之前将不会看到更新的索引。但是,拥有该事务的会话将立即看到更新的索引。

考虑的一种方法是,在具有主键的表中考虑。在SQL Server和Oracle中,这是作为索引实现的。在大多数情况下,如果我们希望这样INSERT做会违反主键,那么我们通常会立即出现错误。为此,索引必须与数据同时更新。请注意,其他平台(例如Postgres)允许延迟约束,只有在提交事务后才检查延迟约束。

这是一个显示常见情况的快速Oracle演示:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

第二条INSERT语句引发错误:

SQL错误:ORA-00001:唯一约束(XXXXXX.SYS_C00384850)被违反

00001. 00000-“违反了唯一约束(%s。%s)”

*原因:UPDATE或INSERT语句试图插入重复的键。对于以DBMS MAC模式配置的Trusted Oracle,如果在不同级别存在重复条目,则可能会看到此消息。

*措施:要么取消唯一限制,要么不插入密钥。

如果您希望看到下面的索引更新操作,那么可以在SQL Server中进行一个简单的演示。首先创建一个两列表,该表具有一百万行,并且该VAL列上具有非聚集索引:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

以下查询可以使用非聚集索引,因为该索引是该查询的覆盖索引。它包含执行它所需的所有数据。与预期的一样,没有退货。

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

查询1

现在让我们开始一个事务并更新表VAL中几乎所有行:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

这是该查询计划的一部分:

查询2

红色圆圈表示对非聚集索引的更新。蓝色圆圈表示聚簇索引的更新,聚簇索引本质上是表的数据。即使未提交事务,我们也会在查询执行的一部分中看到数据和索引已更新。请注意,根据所涉及数据的大小以及其他可能的因素,您将不会始终在计划中看到此信息。

在事务仍未提交的情况下,让我们SELECT从上方重新访问该查询。

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

在此处输入图片说明

查询优化器仍然可以使用索引,这一次它估计将返回999999行。执行查询将返回预期结果。

那是一个简单的演示,但希望它能使事情变得更简单。

顺便说一句,我知道在少数情况下可以争辩说索引没有立即更新。这样做是出于性能方面的考虑,最终用户应该看不到不一致的数据。例如,有时删除操作不会完全应用于SQL Server中的索引。后台进程运行,并最终清理数据。如果您有好奇心,可以阅读有关幽灵记录的信息


这是一个超级答案-还要回答我想知道的另一件事:在插入或提交时是否会发生主键(或类似键)冲突。感谢您的完整答复。
马克·爱尔兰

相关问题(有关何时发生约束违规的问题)与是否使用延迟事务有关。例如,SQL Server尚未实现延迟事务,因此所有冲突都发生在语句末尾。其他DBMS拥有(例如,虽然不是针对所有类型的约束,都使用Postgres),所以当约束被延迟时,将在事务的提交阶段检查违规。
ypercubeᵀᴹ

Oracle还支持延迟约束
BobC

1

我的经验是,与使用批处理插入相比,1,000,000行插入实际上将需要更多资源,并且需要更长的时间才能完成。例如,可以将其实现为10,000行的100个插入。

这样可以减少插入批次的开销,如果批次失败,则回滚较小。

无论如何,对于SQL Server,都有一个bcp实用程序或BULK INSERT命令可用于批量插入。

而且,当然,您也可以实现自己的代码来处理这种方法。


1
通常,如果您需要在需要索引的表上插入大量行,则删除索引,加载数据然后重建索引可能会更快。Oracle还使用/ * + APPEND * /提示支持直接路径批量加载选项。
BobC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.