我在sql server 索引基礎知識系列中,第一篇就講了記錄數據的基本格式。那裡主要講解的是,資料庫的最小讀存單元:數據頁。一個數據頁是8k大小。
對於資料庫來說,它不會每次有一個數據頁變化後,就存到硬碟。而是變化達到一定數量級後才會作這個操作。 這時候,資料庫並不是以數據頁來作為操作單元,而是以64k的數據(8個數據頁,一個區)作為操作單元。
區是管理空間的基本單位。一個區是八個物理上連續的頁(即 64 kb)。這意味著 sql server 資料庫中每 mb 有 16 個區。
為了使空間分配更有效,sql server 不會將所有區分配給包含少量數據的表。sql server 有兩種類型的區:
統一區,由單個對象所有。區中的所有 8 頁只能由所屬對象使用。
混合區,最多可由八個對象共享。區中八頁的每頁可由不同的對象所有。
通常從混合區向新表或索引分配頁。當表或索引增長到 8 頁時,將變成使用統一區進行後續分配。如果對現有表創建索引,並且該表包含的行足以在索引中生成 8 頁,則對該索引的所有分配都使用統一區進行。
為何會這樣呢?
其實很簡單:
讀或寫 8kb 的時間與讀或寫 64 kb的時間幾乎相同。
在 8 kb 到 64 kb 範圍之內,單個磁碟 i/o 傳輸操作所花的時間主要是磁碟取數臂和讀/寫磁頭運動的時間。
因此,從數學上來講,當需要傳輸 64 kb 以上的 sql 數據時,
儘可能地執行 64 kb 磁碟傳輸是有益的,即分成數個64k的操作。
因為 64 kb 傳輸基本上與 8 kb 傳輸一樣快,而每次傳輸的 sql server 數據是 8 kb 傳輸的 8 倍。
我們通過一個實例來看 有and 操作符時候的最常見的一種情況。我們有下面一個表,
create table [dbo].[member]( [member_no] [dbo].[numeric_id] identity(1,1) not null, [lastname] [dbo].[shortstring] not null, [firstname] [dbo].[shortstring] not null, [middleinitial] [dbo].[letter] null, [street] [dbo].[shortstring] not null, [city] [dbo].[shortstring] not null, [state_prov] [dbo].[statecode] not null, [country] [dbo].[countrycode] not null, [mail_code] [dbo].[mailcode] not null, [phone_no] [dbo].[phonenumber] null, [photograph] [image] null, [issue_dt] [datetime] not null default (getdate()), [expr_dt] [datetime] not null default (dateadd(year,1,getdate())), [region_no] [dbo].[numeric_id] not null, [corp_no] [dbo].[numeric_id] null, [prev_balance] [money] null default (0), [curr_balance] [money] null default (0), [member_code] [dbo].[status_code] not null default (' '))
這個表具備下面的四個索引:
索引名 細節 索引的列
member_corporation_link nonclustered located on primary corp_no
member_ident clustered, unique, primary key located on primary member_no
member_region_link nonclustered located on primary region_no
memberfirstname nonclustered located on primary firstname
當我們執行下面的sql查詢時候,
select m.member_no, m.firstname, m.region_nofrom dbo.member as mwhere m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go