Mar*_*and 12 performance index transaction
我试图了解插入中涉及索引和事务的事件序列。
例如,Oracle 文档指出:
如果在加载数据之前创建 [或拥有] 一个或多个索引,则数据库必须在插入每一行时更新每个索引。
但是如果我创建一个事务,插入五行,然后提交会发生什么?索引是为每次插入更新还是仅在提交点更新?
逻辑告诉我,它们只会在提交点更新,因为在提交这些记录之前,更新的索引不可能有用。但这是真的吗?
如果是这样,当我有 1m 行要插入时,为了获得最佳性能,我应该对所有行进行一次大型提交,而不是对 100k 条记录进行 10 个事务?当然,我意识到如果第 999,999 行失败,这会带来更大的回滚风险。
如果我的术语有点过时,请见谅。我不是贸易 DBA。我对某个特定的数据库并不那么感兴趣,因为我对一般的数据库感兴趣,尽管 Oracle 和 Postgres 是我最常用的。我已经搜索过这个主题,但无法真正找到明确的答案。
我使用 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
Run Code Online (Sandbox Code Playgroud)
第二个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);
Run Code Online (Sandbox Code Playgroud)
以下查询可以使用非聚集索引,因为该索引是该查询的覆盖索引。它包含执行它所需的所有数据。正如预期的那样,没有返回任何回报。
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Run Code Online (Sandbox Code Playgroud)
现在让我们开始一个事务并更新表VAL中几乎所有的行:
BEGIN TRANSACTION
UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;
Run Code Online (Sandbox Code Playgroud)
这是查询计划的一部分:
红色圈出的是对非聚集索引的更新。蓝色圆圈是对聚集索引的更新,它本质上是表的数据。即使事务尚未提交,我们也看到数据和索引在查询执行的一部分中得到了更新。请注意,根据所涉及的数据大小以及可能的其他因素,您不会总是在计划中看到这一点。
由于事务仍未提交,让我们重新审视SELECT上面的查询。
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
Run Code Online (Sandbox Code Playgroud)
查询优化器仍然能够使用索引,这次它估计将返回 999999 行。执行查询返回预期结果。
这是一个简单的演示,但希望它使事情变得更清晰。
顺便说一句,我知道在一些情况下可能会认为索引没有立即更新。这样做是出于性能原因,最终用户不应看到不一致的数据。例如,有时删除不会完全应用于 SQL Server 中的索引。后台进程运行并最终清理数据。如果您好奇,可以阅读有关幽灵记录的信息。