SQL Server索引進階:第十三級,插入,更新,刪除

來源:互聯網
上載者:User

標籤:

在第十級到十二級中,我們看了索引的內部結構,以及改變結構造成的影響。在本文中,繼續查看Insert,update,delete和merge造成的影響。首先,我們單獨看一下這四個命令。

插入INSERT

當向表中插入一行資料的時候,不管表是堆表還是叢集索引表,肯定會在表的索引中插入一個入口,過濾索引除外。這麼做的時候,SQL Server使用索引鍵的值從根頁到葉子層頁,到達葉子層頁之後,檢查頁的可用空間,如果有足夠的空閑空間,新的入口就會被插入適當的位置。

最終,SQL Server可能會試圖向一個已經沒有空間的頁插入入口資訊。這時候,SQL Server就會查詢位置結構,找一個有空閑空間的頁。一旦找到,就會做三件事,每一件都和要插入的索引鍵的順序有關:

隨機序列:正常情況,SQL Server會將滿頁的一半的入口移動的一個空頁,然後將新入口插入合適的頁,這就產生了兩個用了一半的頁。如果你的應用繼續插入資料,但是不刪除資料,這兩個頁將會從用了一半的狀態變成滿頁,然後再被分成兩個半頁,然後再次變成滿頁,這樣周而復始,迴圈往複。每頁的充滿率大概是75%。

增序序列:SQL Server發現新的入口需要插入滿頁的最後面,就會建立一頁,然後插入這個新入口,新頁再次滿了的話,就再建立一頁。一旦一頁滿了之後,他就一直是滿的,所以內部片段很小,甚至沒有。

降序序列:相反,如果SQL Server發現新的入口需要插入滿頁的開始,也會建立一頁,插入新的入口,但是由於是降序,內部片段接近100%。

刪除DELETE

當從表中刪除一行的時候,對應的索引入口會從索引中刪除。對每一個索引,SQL Server為了尋找入口,都要從根頁導航到葉子層頁。一旦SQL Server發現入口,就會做兩件事:立即刪除入口,或者是在行的頭部設定標記,使得入口變成ghost record,在適當的時候,就會刪除ghost record。

Ghost record在查詢的時候,會被忽略。它們只是在物理上還存在,邏輯上已經不存在了。一個索引的ghost record的數量可以通過系統函數sys.dm_db_index_physical_stats來擷取。

SQL Server沒有立即刪除是出於效能和並發管理的需要。不僅僅是刪除本身的效能,也包括隨後的交易回復效能。上面的做法使得復原一個刪除操作是很容易的,相比較從交易記錄中重新建立記錄而言。

下面的因素會影響刪除的處理過程:

  • 如果行被鎖定,刪除的索引會變成ghost record。
  • 如果執行的過程需要鎖定5000行資料,行層級的鎖會升級為表層級的鎖。
  • 作為並發技術,行版本的使用,也會導致出現ghost record。
  • 直到事務完成,才會刪除ghost record。
  • SQL Server的後台線程ghost-cleanup負責刪除ghost record,但是,什麼時候刪除也是不可預期的。刪除操作本身並不通知ghost-cleanup線程去這麼做,隨後的頁掃描會將包含ghost record的頁加入一個列表,ghost-cleanup線程會週期性處理這個列表。
  • ghost-cleanup線程大約每5秒鐘喚醒一次。每次會清理10頁。這些數字都是可以設定的。
  • 你可以通過sp_clean_db_free_space或者sp_clean_db_file_free_space來強制清理,將會刪除整個資料庫或者資料檔案中的ghost record。

換句話說,當你刪除資料行的時候,邏輯上講已經刪除了。如果沒有被理解刪除,只要SQL Server認為是安全的,他們就會被刪除。

更新UPDATE

當更新表中資料行的時候,需要修改索引的入口。對於每一個索引入口,SQL Server會執行就地的更新,或者是刪除再插入。只要有可能,SQL Server還是會使用就地更新。但是,也有一些情況不能就地更新,SQL Server就會執行刪除緊跟著插入。下面是一些這方面的原因:

  • 更新要修改鍵列,導致索引的入口需要重新分配。
  • 更新要修改很多列,導致入口不合適在當前頁。
  • 在表上有DML的觸發器。

如果修改的列是索引鍵的一部分,入口的位置肯定要變化。入口會從舊的位置刪除,以新鍵的順序在新的位置插入入口。大部分情況,都會再刪除之後執行插入。如果新的位置和舊的位置在同一頁,有可能會就地更新。SQL Server會從根頁到葉子層跑上兩次,一次用來尋找當前的入口位置,一次用來決定入口的新位置。

如果修改的列是叢集索引的一部分,所有的非叢集索引都需要更新,因為他們的標籤是由叢集索引的鍵組成的。

如果修改的不是索引鍵的一部分,入口的位置不會改變。但是,入口的大小可能會改變。如果頁中不夠空間存放新的入口,更新就會變成刪除再插入。

合并MERGER

在SQL Server 2008中引入了合併作業,很強大,很靈活,很好。合併作業會產生插入,更新,刪除語句。合并和你寫insert,udpate,delete產生的效果一樣,所以在本系列中沒有介紹。

MERGE 目標表  USING 源表  ON 匹配條件  WHEN MATCHED THEN     語句  WHEN NOT MATCHED THEN     語句; 

以上是MERGE的最最基本的文法,語句執行時根據匹配條件的結果,如果在目標表中 找到匹配記錄則執行WHEN MATCHED THEN後面的語句,如果沒有找到匹配記錄則執行WHEN NOT MATCHED THEN後面的語句。注意源表可以是表,也可以是一個子查詢語句。

格外強調一點,MERGE語句最後的分號是不能省略的!

MERGE ProductNew AS d USING     Product AS s ON s.ProductID = d.ProductId     WHEN NOT MATCHED THEN             INSERT( ProductID,ProductName,Price)                     VALUES(s.ProductID,s.ProductName,s.Price); 
MERGE ProductNew AS d USING     Product AS s ON s.ProductID = d.ProductId WHEN NOT MATCHED THEN     INSERT( ProductID,ProductName,Price)         VALUES(s.ProductID,s.ProductName,s.Price) WHEN MATCHED THEN     UPDATE SET d.ProductName = s.ProductName, d.Price = s.Price; 

一次性更新索引Index-at-a-Time Update

當執行插入,更新,刪除語句動作表的一行的時候,SQL Server肯定會修改資料,然後修改索引。在執行完插入,更新,刪除資料之後,SQL Server有兩個選擇:

  • 對每一行,執行完操作之後,都去修改索引。
  • 對每一行,執行完操作之後,對每個索引,將修改資訊掛起在一個集合中。等所有的行都執行完操作之後,在執行掛起的索引修改集合。

第二種叫做“一次性更新索引”,是插入,更新,刪除操作的一個選項。

SQL Server查詢最佳化工具將會決定採用哪一種來最佳化效能。如果修改的是表中的大部分行,很有可能會使用第二種。

為了證明,我們建立一張表,包含兩個索引。

USE AdventureWorks; GO IF EXISTS (SELECT *        FROM sys.objects          WHERE name = ‘FragTestII‘ and type = ‘U‘) BEGIN   DROP TABLE dbo.FragTestII; END GO CREATE TABLE dbo.FragTestII    (     PKCol  int not null    , InfoCol nchar(64) not null    , CONSTRAINT PK_FragTestII_PKCol primary key nonclustered (PKCol)    ); GO CREATE INDEX IX_FragTestII_InfoCol      ON dbo.FragTestII (InfoCol); GO 

先執行一個插入一條記錄的語句。

INSERT dbo.FragTestII VALUES (100000, ‘XXXX‘); 

的執行計畫,只是顯示了插入資料的過程,沒有顯示索引更新的資訊。這是因為,上面的情況下,索引的更新是行更新的一部分。

當時,當我們插入大量資料的時候,執行計畫就會不一樣了。

我們先構造一個20000條記錄的FragTest表,然後將FragTest的資料批量插入FragTestII表。

CREATE TABLE dbo.FragTest    (     PKCol  int IDENTITY(1,1)  not null    , InfoCol nchar(64) not null    , CONSTRAINT PK_FragTest_PKCol primary key nonclustered (PKCol)    ); GO  DECLARE @index INT SET @index=0  WHILE (@index<20000) BEGIN     INSERT INTO dbo.FragTest(InfoCol)VALUES(‘123‘)          SET @index=@index +1      END 
INSERT dbo.FragTestII   SELECT PKCol, InfoCol   FROM dbo.FragTest; 

執行計畫就是上面的樣子,包含很多的操作。一類操作是表中插入資料。有兩個排序,每個都包含一個插入索引的操作。

儘管是一個複雜的執行計畫,排序和更新掛起的索引的單獨執行的,但也是一個高效的執行計畫。相比隨即添加索引,有順序的添加索引,產生的片段會更少。

結論

在索引中插入入口會導致三種片段,這依賴於插入入口的順序。

從索引中刪除入口,包括從叢集索引中刪除,可能會立即刪除入口。也可能會建立ghost record使得索引入口成為邏輯刪除。ghost只是存在於葉子層。SQL Server在事務完成之後,才會刪除ghost record。

更新索引可能會立即就地更新,也可能是刪除後在插入。如果表中沒有DML的觸發器,如果更新沒有重新分配入口,或者增加入口的大小,通常還是會就地更新的。

如果資料修改語句影響的是大量的行,SQL Server可能會選擇一次性更新索引,先修改表,然後在更新每個索引。

SQL Server索引進階:第十三級,插入,更新,刪除

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.