如何提高增加包含大量记录的表的主键字段的效率
如何提高增加包含大量記錄的表的主鍵字段的效率
LazyBee
1 問題的提出:
在給客戶升級數據庫系統時,由于報表的需要,系統中每一個表都需要有主鍵字段。系統審計表自然也有這個要求—需要增加一個identify的字段,但這個表中有2000多萬條記錄,使用以下SQLl語句:alter table erAuditEventTime add EventTime_ID int IDENTITY primary key clustered來增加時需要4個多小時??蛻粢笪覀儗Υ诉M行提速。
2 拋磚--解決過程
問題出來之后,我第一時間上google去狂搜了一番,可是沒有一條是關于對增加主鍵提速的。都是說建表增加主鍵提高效率的。這可怎么辦?剛開始我以為是日志增長過快的原因,因為我在測試數據庫上執行此語句時發現數據庫日志文件在以“光速”狂飆,一段時間下來就長到十幾個G,于是將數據庫的恢復模式改成Simple(簡單),效率還是沒有多大改善,于是認為會不會是鎖的問題呢,但是由于SQL Server會根據情況自動將鎖升級的,應該沒有問題,不管怎么樣還是給加上了with nocheck選項。好像依然沒戲,不知什么時候“靈光”一現,我能不能采用拷貝策略呢,不管三七二十一先試試再說,于是將上面的語句改寫成下面的方式:
?1--Rename?table
?2exec?sp_rename?'erAuditEventTime','zxg_erAuditEventTime'
?3go
?4--Copy?table?schema
?5select?top?0?*?into?erAuditEventTime?from?zxg_erAuditEventTime?
?6go
?7--Add?identify?field
?8alter?table?erAuditEventTime?
?9add?EventTime_ID?int?IDENTITY?primary?key?clustered
10go
11--Copy?data
12insert?into?erAuditEventTime?select?*?from?zxg_erAuditEventTime?
13go
14
改完之后,測試發現,完成這些語句需要38分26秒。欣喜……
3 反思
為什么將語句改成這種形式之后,效率能提高這么多呢?我覺得可能跟SQL Server的數據庫物理存儲有關。對此,讓我們先了解一下SQL Server的物理存儲:
數據庫文件:SQL Server包含三種類型的數據庫文件—主數據文件(Primary Data Files)、次要數據文件(Secondary data files)、日志文件(Log files)。主數據文件是數據庫的起點,指向數據庫中的其他文件。每個數據庫都只有一個主數據文件(擴展名為.mdf)。除主數據文件以外的其他數據文件都是次要數據文件。有些數據庫可能不含任何次要數據文件,而有些數據庫則含有多個次要數據文件(擴展名為.ndf)。日志文件包含用于恢復數據庫的所有日志信息。每個數據庫至少有一個日志文件,當然也可以有多個(擴展名為.ldf)。數據庫中所有文件的位置都記錄在數據庫的主文件和master數據庫中。大多數情況下,SQL Server數據庫引擎使用master數據庫中的文件信息。只有在下列情況下,數據庫引擎使用主數據文件的文件位置信息初始化master數據庫中的文件位置項:還原master數據庫時、使用帶有For Attach或For ATTACH_REBUILD_LOG選項的Create Database語句來附加數據庫時、從SQL Server2000升級到SQL Server2005時。
數據庫文件組:文件組是命名的文件集合,為了便于分配和管理,可以將數據庫對象和文件一起分成文件組。有兩種類型的文件組:主文件組、用戶定義文件組。主文件組包含主數據文件和沒有明確分配給其他文件組的其他文件。系統表的所有頁均分配在主文件組中。用戶定義的文件組是通過在Create Database 或Alter DataBase語句中使用FILEGROUP關鍵字指定的任何文件組。(日志文件不包括在文件組內,日志空間和數據空間分開管理)。并且一個文件只能屬于一個文件組。每個數據庫中均有一個文件組被指定為默認文件組,如果創建表或索引時未指定文件組,則將假定所有頁都從默認文件組分配。一次只能有一個文件組作為默認文件組。db_owner成員可以將默認文件組從一個文件組切換到另一個。如果沒有指定默認文件組,則將主文件組作為默認文件組。
頁(Page):是SQL Server中存儲數據的基本單位是頁(Page),頁的大小是8K,也就是SQL Server數據庫中每MB中有128頁。每頁的開頭是96個字節的標頭,用于存儲有關頁的系統信息,包括頁碼、頁的類型、頁的可用空間以及擁有該頁的對象的分配單元ID。下表說明SQL Server的數據庫文件中使用的頁類型:
| 頁類型 | 內容 |
| Data | 當 text in row設置為 ON 時,包含除 text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max)和 xml數據之外的所有數據的數據行。 |
| Index | 索引條目。 |
| Text/Image | 大型對象數據類型: ·????????????????????? text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max)和 xml數據。 數據行超過 8 KB 時為可變長度數據類型列: ·????????????????????? varchar、nvarchar、varbinary和 sql_variant |
| Global Allocation Map、Shared Global Allocation Map | 有關區是否分配的信息。 |
| Page Free Space | 有關頁分配和頁的可用空間的信息。 |
| Index Allocation Map | 有關每個分配單元中表或索引所使用的區的信息。 |
| Bulk Changed Map | 有關每個分配單元中自最后一條 BACKUP LOG 語句之后的大容量操作所修改的區的信息。 |
| Differential Changed Map | 有關每個分配單元中自最后一條 BACKUP DATABASE 語句之后更改的區的信息。 |
注意:日志文件不包含頁,而是包含一系列的日志記錄。數據庫的每個文件都有一個唯一的文件ID號,并且數據文件中的頁是按順序編號的,文件的首頁以0開始。若要唯一表示數據庫中的頁,需要同時使用文件ID和頁碼。
區(Extents):8個物理上連續的頁為一個區(即64k).區是SQL Server管理空間的基本單位,也就是說SQL Server為了提高效率,給對象分配空間時是以區為單位的,而不是以頁為單位。為了使分配空間更有效,SQL Server不會將某一個區中的所有空間分配給包含少量數據的表。為此,SQL Server包含兩種類型的區:統一區和混合區。統一區是由單個對象所有。區中的所有 8 頁只能由所屬對象使用。混合區,最多可由八個對象共享。區中八頁的每頁可由不同的對象所有。通常從混合區向新表或索引分配頁。當表或索引增長到 8 頁時,將變成使用統一區進行后續分配。如果對現有表創建索引,并且該表包含的行足以在索引中生成 8 頁,則對該索引的所有分配都使用統一區進行。
結論:由于在對含有大量數據的erAuditEventTime表增加一個Identify字段的時,對每一行數據都需要進行變更,SQL Server為了保證同一行數據都位于同一頁中,所以需要頻繁移動原有頁中的數據,導致大量而且頻繁的IO操作;而采用另外新建一個表,然后使用insert into 語句來進行數據拷貝工作時,SQL Server只需要給新的表分配一系列的沒有使用的統一區就可以了,大大減少了IO操作。而且這兩種方式創建和保存索引的性能是一樣的沒有區別,這可能就是性能相差這么大的真正原因。
4 引玉
由于本人對SQL Server也不是特別懂,所以也請園子里的大蝦們也發表發表高論,看看是否是這個原因導致的性能差異,以及針對這種案例是否有更好的解決方案?(轉載請注明出處:http://lazybee.cnblogs.com/,謝謝!)
轉載于:https://www.cnblogs.com/LazyBee/archive/2008/07/15/1243491.html
總結
以上是生活随笔為你收集整理的如何提高增加包含大量记录的表的主键字段的效率的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [导入]如何理解Return的返回值?
- 下一篇: 9月30号播出的电影为啥28号有场次