1. 程式人生 > >SQL Server 2008 表分區的含義

SQL Server 2008 表分區的含義

2-2 alter ... 差距 對象 位置 prop 分區表 top

繼續看這個文檔

http://www.360doc.com/content/16/0104/11/22743342_525336297.shtml

SQL Server表分區

什麽是表分區

一般情況下,我們建立數據庫表時,表數據都存放在一個文件裏。

但是如果是分區表的話,表數據就會按照你指定的規則分放到不同的文件裏,把一個大的數據文件拆分為多個小文件,還可以把這些小文件放在不同的磁盤下由多個cpu進行處理。這樣文件的大小隨著拆分而減小,還得到硬件系統的加強,自然對我們操作數據是大大有利的。

所以大數據量的數據表,對分區的需要還是必要的,因為它可以提高select效率,還可以對歷史數據經行區分存檔等。但是數據量少的數據就不要湊這個熱鬧啦,因為表分區會對數據庫產生不必要的開銷,除啦性能還會增加實現對象的管理費用和復雜性。

跟著做,分區如此簡單

先跟著做一個分區表(分為11個分區),去除神秘的面紗,然後咱們再逐一擊破各個要點要害。

分區是要把一個表數據拆分為若幹子集合,也就是把把一個數據文件拆分到多個數據文件中,然而這些文件的存放可以依托一個文件組或這多個文件組,由於多個文件組可以提高數據庫的訪問並發量,還可以把不同的分區配置到不同的磁盤中提高效率,所以創建時建議分區跟文件組個數相同。

1.創建文件組

可以點擊數據庫屬性在文件組裏面添加

T-sql語法:

alter database <數據庫名> add filegroup <文件組名>
技術分享圖片
---創建數據庫文件組
alter database testSplit add filegroup ByIdGroup1
alter database testSplit add filegroup ByIdGroup2
alter database testSplit add filegroup ByIdGroup3
alter database testSplit add filegroup ByIdGroup4
alter database testSplit add filegroup ByIdGroup5
alter database testSplit add filegroup ByIdGroup6
alter database testSplit add filegroup ByIdGroup7
alter database testSplit add filegroup ByIdGroup8
alter database testSplit add filegroup ByIdGroup9
alter database testSplit add filegroup ByIdGroup10
技術分享圖片

2.創建數據文件到文件組裏面

可以點擊數據庫屬性在文件裏面添加

T-sql語法:

alter database <數據庫名稱> add file <數據標識> to filegroup <文件組名稱>

--<數據標識> (name:文件名,fliename:物理路徑文件名,size:文件初始大小kb/mb/gb/tb,filegrowth:文件自動增量kb/mb/gb/tb/%,maxsize:文件可以增加到的最大大小kb/mb/gb/tb/unlimited)
技術分享圖片
alter database testSplit add file 
(name=N‘ById1‘,filename=N‘J:\Work\數據庫\data\ById1.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup1
alter database testSplit add file 
(name=N‘ById2‘,filename=N‘J:\Work\數據庫\data\ById2.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup2
alter database testSplit add file 
(name=N‘ById3‘,filename=N‘J:\Work\數據庫\data\ById3.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup3
alter database testSplit add file 
(name=N‘ById4‘,filename=N‘J:\Work\數據庫\data\ById4.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup4
alter database testSplit add file 
(name=N‘ById5‘,filename=N‘J:\Work\數據庫\data\ById5.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup5
alter database testSplit add file 
(name=N‘ById6‘,filename=N‘J:\Work\數據庫\data\ById6.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup6
alter database testSplit add file 
(name=N‘ById7‘,filename=N‘J:\Work\數據庫\data\ById7.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup7
alter database testSplit add file 
(name=N‘ById8‘,filename=N‘J:\Work\數據庫\data\ById8.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup8
alter database testSplit add file 
(name=N‘ById9‘,filename=N‘J:\Work\數據庫\data\ById9.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup9
alter database testSplit add file 
(name=N‘ById10‘,filename=N‘J:\Work\數據庫\data\ById10.ndf‘,size=5Mb,filegrowth=5mb)
to filegroup ByIdGroup10
技術分享圖片

執行完成後,右鍵數據庫看文件組跟文件裏面是不是多出來啦這些文件組跟文件。

3.使用向導創建分區表

右鍵到要分區的表--- >> 存儲 --- >> 創建分區 --- >>顯示向導視圖 --- >> 下一步 --- >> 下一步。。

技術分享圖片

這裏舉例說下選擇列的意思:

假如你選擇的是int類型的列:那麽你的分區可以指定為1--100W是一個分區,100W--200W是一個分區....

假如你選擇的是datatime類型:那麽你的分區可以指定為:2014-01-01--2014-01-31一個分區,2014-02-01--2014-02-28一個分區...

根據這樣的列數據規則劃分,那麽在那個區間的數據,在插入數據庫時就被指向那個分區存儲下來。

我這裏選用orderid int類型 --- >> 下一步 --- >>

技術分享圖片

技術分享圖片

技術分享圖片

左邊界右邊界:就是把臨界值劃分給上一個分區還是下一個分區。一個小於號,一個小於等於號。

然後下一步下一步最後你會得到分區函數和分區方案。

技術分享圖片
USE [testSplit]
GO
BEGIN TRANSACTION

--創建分區函數
CREATE PARTITION FUNCTION [bgPartitionFun](int) AS RANGE LEFT FOR VALUES (N‘1000000‘, N‘2000000‘, N‘3000000‘, N‘4000000‘, N‘5000000‘, N‘6000000‘, N‘7000000‘, N‘8000000‘, N‘9000000‘, N‘10000000‘)

--創建分區方案
CREATE PARTITION SCHEME [bgPartitionSchema] AS PARTITION [bgPartitionFun] TO ([PRIMARY], [ByIdGroup1], [ByIdGroup2], [ByIdGroup3], [ByIdGroup4], [ByIdGroup5], [ByIdGroup6], [ByIdGroup7], [ByIdGroup8], [ByIdGroup9], [ByIdGroup10])

--創建分區索引
CREATE CLUSTERED INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] 
(
    [OrderId]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [bgPartitionSchema]([OrderId])

--刪除分區索引
DROP INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] WITH ( ONLINE = OFF )

COMMIT TRANSACTION
技術分享圖片

執行上面向導生成的語句。分區完成。。

4.秀一下速度。

首先我在表中插入啦1千萬行數據。給表分啦11個分區。前十個分區裏面一個是100W條數據。。

技術分享圖片

說兩句:

可見反常現象,掃描次數跟邏輯讀取次數都是無分區表的2倍之多,但查詢速度卻是快啦不少啊。這就是分區的神奇之處啊,所以要相信這世界一切皆有可能。

分區函數,分區方案,分區表,分區索引

1.分區函數

指定分依據區列(依據列唯一),分區數據範圍規則,分區數量,然後將數據映射到一組分區上。

創建語法:

create partition function 分區函數名(<分區列類型>) as range [left/right] 
for values (每個分區的邊界值,....) 
--創建分區函數
CREATE PARTITION FUNCTION [bgPartitionFun](int) AS RANGE LEFT FOR VALUES (N‘1000000‘, N‘2000000‘, N‘3000000‘, N‘4000000‘, N‘5000000‘, N‘6000000‘, N‘7000000‘, N‘8000000‘, N‘9000000‘, N‘10000000‘)

然而,分區函數只定義了分區的方法,此方法具體用在哪個表的那一列上,則需要在創建表或索引是指定。

刪除語法:

--刪除分區語法
drop partition function <分區函數名>
--刪除分區函數 bgPartitionFun
drop partition function bgPartitionFun

需要註意的是,只有沒有應用到分區方案中的分區函數才能被刪除。

2.分區方案

指定分區對應的文件組。

創建語法:

--創建分區方案語法
create partition scheme <分區方案名稱> as partition <分區函數名稱> [all]to (文件組名稱,....) 
--創建分區方案,所有分區在一個組裏面
CREATE PARTITION SCHEME [bgPartitionSchema] AS PARTITION [bgPartitionFun] TO ([ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1])

分區函數必須關聯分區方案才能有效,然而分區方案指定的文件組數量必須與分區數量一致,哪怕多個分區存放在一個文件組中。

刪除語法:

--刪除分區方案語法
drop partition scheme<分區方案名稱>
--刪除分區方案 bgPartitionSchema
drop partition scheme bgPartitionSchema1

只有沒有分區表,或索引使用該分區方案是,才能對其刪除。

3.分區表

創建語法:

--創建分區表語法
create table <表名> (
  <列定義>
)on<分區方案名>(分區列名)
技術分享圖片
--創建分區表
create table BigOrder (
   OrderId              int                  identity,
   orderNum             varchar(30)          not null,
   OrderStatus          int                  not null default 0,
   OrderPayStatus       int                  not null default 0,
   UserId               varchar(40)          not null,
   CreateDate           datetime             null default getdate(),
   Mark                 nvarchar(300)        null
)on bgPartitionSchema(OrderId)
技術分享圖片

如果在表中創建主鍵或唯一索引,則分區依據列必須為該列。

4.分區索引

創建語法:

--創建分區索引語法
create <索引分類> index <索引名稱> 
on <表名>(列名)
on <分區方案名>(分區依據列名)
--創建分區索引
CREATE CLUSTERED INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] 
(
    [OrderId]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [bgPartitionSchema]([OrderId])

使用分區索引查詢,可以避免多個cpu操作多個磁盤時產生的沖突。

分區表明細信息

這裏的語法,我就不寫啦,自己看語句分析吧。簡單的很。。

1.查看分區依據列的指定值所在的分區

--查詢分區依據列為10000014的數據在哪個分區上
select $partition.bgPartitionFun(2000000)  --返回值是2,表示此值存在第2個分區 

2.查看分區表中,每個非空分區存在的行數

--查看分區表中,每個非空分區存在的行數
select $partition.bgPartitionFun(orderid) as partitionNum,count(*) as recordCount
from bigorder
group by  $partition.bgPartitionFun(orderid)

技術分享圖片

3.查看指定分區中的數據記錄

---查看指定分區中的數據記錄
select * from bigorder where $partition.bgPartitionFun(orderid)=2

結果:數據從1000001開始到200W結束

分區的拆分與合並以及數據移動

1.拆分分區

在分區函數中新增一個邊界值,即可將一個分區變為2個。

--分區拆分
alter partition function bgPartitionFun()
split range(N‘1500000‘)  --將第二個分區拆為2個分區

註意:如果分區函數已經指定了分區方案,則分區數需要和分區方案中指定的文件組個數保持對應一致。

2.合並分區

與拆分分區相反,去除一個邊界值即可。

--合並分區
alter partition function bgPartitionFun()
merge range(N‘1500000‘)  --將第二第三分區合並

3.分區中的數據移動

你或許會遇到這樣的需求,將普通表數據復制到分區表中,或者將分區表中的數據復制到普通表中。

那麽移動數據這兩個表,則必須滿足下面的要求。

  • 字段數量相同,對應位置的字段相同
  • 相同位置的字段要有相同的屬性,相同的類型。
  • 兩個表在一個文件組中

1.創建表時指定文件組

--創建表
create table <表名> (
  <列定義>
)on <文件組名>

2.從分區表中復制數據到普通表

--將bigorder分區表中的第一分區數據復制到普通表中
alter table bigorder switch partition 1 to <普通表名>

3.從普通標中復制數據到分區表中

這裏要註意的是要先將分區表中的索引刪除,即便普通表中存在跟分區表中相同的索引。

--將普通表中的數據復制到bigorder分區表中的第一分區
alter table <普通表名> switch to bigorder partition 1 

分區視圖

分區視圖是先建立帶有字段約束的相同表,而約束不同,例如,第一個表的id約束為0--100W,第二表為101萬到200萬.....依次類推。

創建完一系列的表之後,用union all 連接起來創建一個視圖,這個視圖就形成啦分區視同。

很簡單的,這裏我主要是說分區表,就不說分區視圖啦。。

查看數據庫分區信息

技術分享圖片
SELECT OBJECT_NAME(p.object_id) AS ObjectName,
      i.name                   AS IndexName,
      p.index_id               AS IndexID,
      ds.name                  AS PartitionScheme,   
      p.partition_number       AS PartitionNumber,
      fg.name                  AS FileGroupName,
      prv_left.value           AS LowerBoundaryValue,
      prv_right.value          AS UpperBoundaryValue,
      CASE pf.boundary_value_on_right
            WHEN 1 THEN ‘RIGHT‘
            ELSE ‘LEFT‘ END    AS Range,
      p.rows AS Rows
FROM sys.partitions                  AS p
JOIN sys.indexes                     AS i
      ON i.object_id = p.object_id
      AND i.index_id = p.index_id
JOIN sys.data_spaces                 AS ds
      ON ds.data_space_id = i.data_space_id
JOIN sys.partition_schemes           AS ps
      ON ps.data_space_id = ds.data_space_id
JOIN sys.partition_functions         AS pf
      ON pf.function_id = ps.function_id
JOIN sys.destination_data_spaces     AS dds2
      ON dds2.partition_scheme_id = ps.data_space_id 
      AND dds2.destination_id = p.partition_number
JOIN sys.filegroups                  AS fg
      ON fg.data_space_id = dds2.data_space_id
LEFT JOIN sys.partition_range_values AS prv_left
      ON ps.function_id = prv_left.function_id
      AND prv_left.boundary_id = p.partition_number - 1
LEFT JOIN sys.partition_range_values AS prv_right
      ON ps.function_id = prv_right.function_id
      AND prv_right.boundary_id = p.partition_number 
WHERE
      OBJECTPROPERTY(p.object_id, ‘ISMSShipped‘) = 0
UNION ALL
SELECT
      OBJECT_NAME(p.object_id)    AS ObjectName,
      i.name                      AS IndexName,
      p.index_id                  AS IndexID,
      NULL                        AS PartitionScheme,
      p.partition_number          AS PartitionNumber,
      fg.name                     AS FileGroupName,  
      NULL                        AS LowerBoundaryValue,
      NULL                        AS UpperBoundaryValue,
      NULL                        AS Boundary, 
      p.rows                      AS Rows
FROM sys.partitions     AS p
JOIN sys.indexes        AS i
      ON i.object_id = p.object_id
      AND i.index_id = p.index_id
JOIN sys.data_spaces    AS ds
      ON ds.data_space_id = i.data_space_id
JOIN sys.filegroups           AS fg
      ON fg.data_space_id = i.data_space_id
WHERE
      OBJECTPROPERTY(p.object_id, ‘ISMSShipped‘) = 0
ORDER BY
      ObjectName,
      IndexID,
      PartitionNumber
      
      
技術分享圖片

分類: MSSQL數據庫 標簽: 表分區, 分區表 好文要頂 關註我 收藏該文 技術分享圖片

67 ? 上一篇:sql語句的優化分析
? 下一篇:SQL Server遊標 posted @ 2014-04-29 09:01 張龍豪 閱讀(70599) 評論(27) 編輯 收藏

評論列表 回復引用 #1樓 2014-04-29 09:23 Sam Xiao 表分區,分為水平分區和垂直分區。

你這種只算是水平分區。 回復引用 #2樓[樓主] 2014-04-29 09:29 張龍豪 @ Sam Xiao
回復引用 #3樓 2014-04-29 11:41 路人甲一 受教了。 回復引用 #4樓 2014-04-29 11:42 瀟湘隱者 不錯的總結。good 回復引用 #5樓 2014-04-29 12:58 richiezhang 總結的很好,收藏了 回復引用 #6樓 2014-04-29 18:23 淘沙浪 假如你選擇的是datatime類型:那麽你的分區可以指定為:2014-01-01--2014-01-31一個分區,2014-02-01--2014-02-28一個分區.
按此理解,1年就有12個分區,如果隨著時間的增長,該怎麽分區呢?另外,此前已經存了大量數據的表還能做分區麽?用時間類型的分區方案該是什麽樣的比較合理呢? 回復引用 #7樓[樓主] 2014-04-29 19:38 張龍豪 @ 淘沙浪
那只是個例子,你可以一年的數據分一個區,也可以一天的數據分一個區,當然這些都是根據數據量還有你的需求而定。 回復引用 #8樓 2014-04-30 17:04 Richard__Lee 為什麽我測試得到的數據和樓主的不一樣呢? 如果用聚集索引作分區的字段的話,基本找不出兩者的性能差距,如果沒用的,可以體現出來一點。 回復引用 #9樓 2014-04-30 17:08 段合江 @ Richard__Lee

這與庫中總數據量多少,還有你要提取數據量的多少有關系。 回復引用 #10樓 2014-05-01 13:05 Richard__Lee @ 望江河
就是樓主那樣 千萬的 每個分區百萬 取10000條 回復引用 #11樓[樓主] 2014-05-01 14:51 張龍豪 @ Richard__Lee
查的數據過多 回復引用 #12樓 2014-11-27 11:58 lucika.zh 辛苦。 回復引用 #13樓 2015-04-29 09:32 qly2046 我執行“alter partition function [bgpartitionPerson]()
split range(N‘2012-01-01T00:00:00‘) --將第二個分區拆為2個分區”,報錯“警告: 分區方案 ‘bgpartitionPerson‘ 沒有任何下次使用的文件組。分區方案未更改。”始終不明白什麽原因? 回復引用 #14樓 2015-05-20 10:27 a博聞強識 樓主辛苦!謝謝您的分享! 回復引用 #15樓 2015-07-20 19:39 曹學亮 大數據啊! 回復引用 #16樓 2015-10-29 11:35 newkoinfiniti 我有兩個結構一樣的表,但是表名不一樣,在同一個數據庫中。第一個表叫user表,創建好後我將裏面填充了10w條數據,然後將將表select into 到user_new中,然後按照樓主的方式對user_new 表創建分區。後來我再查詢時發現兩個表查詢速度幾乎一樣了。我再查看分區,發現:
select * from [testDB].[dbo].[user_new] where $partition.userPartFounction(id)=2

select * from [testDB].[dbo].[user_new] where $partition.userPartFounction(id)=2
都能查到數據,是不說我這個表分區創建的時候就對user和user_new都創建了同樣的表分區啊? 回復引用 #17樓 2015-10-29 11:36 newkoinfiniti select * from [testDB].[dbo].[user_new] where $partition.userPartFounction(id)=2

select * from [testDB].[dbo].[user] where $partition.userPartFounction(id)=2
都能查到數據,是不說我這個表分區創建的時候就對user和user_new都創建了同樣的表分區啊? 回復引用 #18樓 2015-11-03 10:40 Shuke 辛苦了 回復引用 #19樓 2016-01-08 18:01 我愛大白菜+1 請問下,這樣分區必須要一個數據文件一個文件組嗎?假如一個文件組多個數據文件的話,怎麽指定數據存儲在哪個文件呢 回復引用 #20樓 2016-05-11 11:54 Jiger 有沒有其他人驗證分區表的效率。

我的理解是分區更側重於數據庫的管理,比如按照分區進行數據的備份,還原,drop或delete等。但是對於查詢的效率提升有限(比起索引,高質量的Query而言),看到另外一個帖子也是這麽說。 回復引用 #21樓 2016-07-28 11:26 Anlod 按照樓主發的帖子,對照做了一個測試,發現最後兩段sql查詢出來的時間基本一致,查看IO CPU讀取日誌,發現分區表還沒有無分區表的效率高,數據量也是1000萬,邏輯和樓主發的一致,但沒有達到效果,這是為什麽? 回復引用 #22樓 2016-09-13 13:19 future. 學習!! 回復引用 #23樓 2016-09-21 09:35 biind 多謝分享 回復引用 #24樓 2016-11-16 10:22 靈雨飄零 總結的很好,收藏了 回復引用 #25樓 2016-12-28 20:12 唐小喵 樓主,我有幾個問題,希望你能幫我解答一下,謝謝!
1. 如果我有10個表,都有一個RowId 字段,我希望這些表都用這個字段按照相同的方案進行表分區,我可以共用一個PartitionScheme和一個PartitionFunction嗎?還是我需要分別創建10個類似的PartitionScheme和10個PartitionFunction給他們用?
2. 第三節:“3.使用向導創建分區表” 生成的腳本最後兩步是“創建分區索引”,“刪除分區索引”,為什麽創建了分區索引馬上又刪除了這個分區索引,這是為什麽呢?
3. 如果一個表已經存在一個聚集索引了,我不想用聚集索引這列做分區,可以用其他列做分區且仍然保持原來那列為聚集索引嗎?
4. 刪除分區的時候索引會失效嗎?

謝謝~~ 回復引用 #26樓 2017-01-09 09:24 2604529 mark 回復引用 #27樓 2017-08-18 14:45 Allen_Chang 樓主,問下 分區改善性能的話 需要改 代碼麽? 比如C# 代碼

SQL Server 2008 表分區的含義