我使用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';
现在让我们开始一个事务并更新表VAL
中几乎所有行:
BEGIN TRANSACTION
UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;
这是该查询计划的一部分:
红色圆圈表示对非聚集索引的更新。蓝色圆圈表示聚簇索引的更新,聚簇索引本质上是表的数据。即使未提交事务,我们也会在查询执行的一部分中看到数据和索引已更新。请注意,根据所涉及数据的大小以及其他可能的因素,您将不会始终在计划中看到此信息。
在事务仍未提交的情况下,让我们SELECT
从上方重新访问该查询。
SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';
查询优化器仍然可以使用索引,这一次它估计将返回999999行。执行查询将返回预期结果。
那是一个简单的演示,但希望它能使事情变得更简单。
顺便说一句,我知道在少数情况下可以争辩说索引没有立即更新。这样做是出于性能方面的考虑,最终用户应该看不到不一致的数据。例如,有时删除操作不会完全应用于SQL Server中的索引。后台进程运行,并最终清理数据。如果您有好奇心,可以阅读有关幽灵记录的信息。