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