sqlserver 数据库表分区

参考文档 https://msdn.microsoft.com/zh-cn/library/ms345146(SQL.90).aspx
     http://blog.sina.com.cn/s/blog_4c8f1ac20100rhu7.html

  分区是为了改善大型表以及具有各种访问模式的表的可伸缩性和可管理性。分区使我们不需要将所有数据放到一个位置,但是需要创建和管理许多对象。当所有数据位于一个表中,删除旧数据需要对表及其相关索引进行逐行处理。删除数据的过程将创建大量的日志活动。但是如果使用分区,删除相同数量的数据只需从分区表中删除特定的分区,然后删除或截断独立的表。

SQLServer分区过程:

1、创建文件组

alter database OrderDB add filegroup [FG_OrderDetail_01];
alter database OrderDB add filegroup [FG_OrderDetail_02];

2、创建文件组文件

如果有多块硬盘,可以将每个文件组中的文件分配到每个硬盘上,这样可实现分布式磁盘I/O,数据库引擎就可以同时并行检索多块硬盘,提高并发读写速度。

alter database OrderDB add file(
name=N'FG_OrderDetail_01'
,filename=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\FTData\FG_OrderDetail_01_data.ndf'
,size=5MB
)to filegroup [FG_OrderDetail_01]; alter database OrderDB add file(
name=N'FG_OrderDetail_02'
,filename=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\FTData\FG_OrderDetail_02_data.ndf'
,size=5MB
)to filegroup [FG_OrderDetail_02];

3、创建分区函数

create partition function [partition_order_date](datetime)
as range left for values('20170131 23:59:59.997', '20170228 23:59:59.997')

4、创建分区方案 (若要切换分区需预留一个空分区,该例子以PRIMARY 文件组作为第3个空分区)

create partition scheme [scheme_order_date] as partition [partition_order_date]
to ([FG_OrderDetail_01], [FG_OrderDetail_02], [PRIMARY])

5、在分区方案下建表

CREATE TABLE [dbo].[order_detail](
[order_id] int NOT NULL,
[product_id] int NOT NULL,
[order_qty] int NOT NULL,
[unit_price] decimal(24,8) NOT NULL,
[total_amount] decimal(24,8) NOT NULL,
[order_date] datetime NOT NULL
CONSTRAINT OrderDetailDateCK CHECK ([order_date] >= '20170101' AND [order_date] < '20170301')
) ON [scheme_order_date]([order_date])

6、插入数据

INSERT dbo.[order_detail](order_id, total_amount, order_date)
SELECT o.PurchaseOrderID, o.TotalAmount, o.OrderDate
FROM dbo.PurchaseOrderDetail as o
WHERE (o.[OrderDate] >= '20170101' AND o.[OrderDate] < '20170301')

7、在分区方案下重建索引

IF EXISTS(SELECT * FROM sysobjects WHERE name='pk_order_detail' and xtype='PK')
ALTER TABLE dbo.order_detail DROP CONSTRAINT [pk_order_detail];
ALTER TABLE dbo.order_detail ADD CONSTRAINT [pk_order_detail]
PRIMARY KEY CLUSTERED(order_id, product_id) ON [scheme_order_date]([order_date]);
IF EXISTS (SELECT 1 FROM sysindexes WHERE name = 'idx_order_detail')
DROP INDEX [idx_order_detail] ON dbo.order_detail;
CREATE NONCLUSTERED INDEX [idx_order_detail] ON dbo.order_detail(order_date);

8、查看分区情况

(1)查看每个分区的具体数据

SELECT $partition.partition_order_date(o.order_date) AS [PartitionNo], count(*) AS [RowsInPartition]
FROM dbo.order_detail AS o
GROUP BY $partition.partition_order_date(o.order_date)
ORDER BY [PartitionNo]

sqlserver 数据库表分区

(2)查看所有使用分区的表

Select s.name As SchemaName, t.name As TableName
From sys.tables t
Inner Join sys.schemas s On t.schema_id = s.schema_id
Inner Join sys.partitions p on p.object_id = t.object_id
Where p.index_id In (0, 1)
Group By s.name, t.name Having Count(*) > 1 Order By s.name, t.name;

sqlserver 数据库表分区

(3)查看分区表的所有分区

SELECT * FROM sys.partitions WHERE OBJECT_NAME(OBJECT_ID)='order_detail';

sqlserver 数据库表分区

  上图表示order_details的聚集索引分为3个分区。index_id  0: heap(没有聚集索引的堆表),1:Clustered Index, 2:NonClustered Index

9、分区表连接查询(分区消除,两个表采用同一个分区方案)

  表和索引对齐

  当多个表使用同一个函数(但不一定使用同一个架构)时,此概念称为对齐。SQL Server 将按类似的方式对具有相同分区键的行进行分组。
  具有相同分区键值的相关数据将被放置到同一个文件中,而将必要的数据隔离出来以便进行连接。如果来自多个表的相关行都按照相同的方式进行分区,SQL Server 则可以连接分区,而无需在整个表或多个分区中(如果表使用了不同的分区函数)搜索匹配的行。在这种情况下,不仅可以对齐对象(因为它们使用相同的键),还可以按存储位置对齐(因为相同的数据位于相同的文件中)。

  当表和索引按照相同的顺序使用相同的分区函数和列时,表和索引将对齐。使用相同的函数对表及其索引进行分区通常可以优化性能。当表及其索引对齐后,SQL Server 则可以更有效地将分区移入和移出分区表,因为所有相关的数据和索引都使用相同的算法进行划分。

  如果定义表和索引时不仅使用了相同的分区函数,还使用了相同的分区架构,则这些表和索引将被认为是按存储位置对齐。按存储位置对齐的一个优点是,相同边界内的所有数据都位于相同的物理磁盘上。在这种情况下,可以单独在某个时间段内执行备份操作,还可以根据数据的变化在备份频率和备份类型方面改变您的策略。如果连接或收集了相同文件或文件组中的表和索引,则可以发现更多的好处。SQL Server 可以通过在多个分区中并行操作来获益。在按存储位置对齐和多 CPU的情况下,每个处理器都可以直接处理特定的文件或文件组,而不会与数据访问产生任何冲突,因为所有需要的数据都位于同一个磁盘上。这样,可以并行运行多个进程,而不会相互干扰。

对存储位置对齐的分区表进行连接查询可以大大提高效率

SELECT o.order_id, o.order_date, o.vendor_id, od.product_id, od.order_qty
FROM dbo.order AS o
INNER JOIN dbo.order_detail AS od ON o.order_id = od.order_id AND o.order_date = od.order_date
WHERE o.order_date >= '' AND o.order_date <= '20170131 11:59:59.997'

图二所示,Constant Scan用于获取分区ID,  Inner Join将两个分区数据集连接起来。SQL Server 正在消除所有不需要的分区,并且只选择包含正确数据的分区。把光标悬停在“Constant Scan”上会显示Argument参数 VALUES(((1)))),这代表分区号 。

sqlserver 数据库表分区    sqlserver 数据库表分区

 10、删除分区

可参考 https://appliedsql.net/2013/10/08/how-to-remove-a-table-partitioning-in-sql-server/

若表存在聚集索引,可直接去掉聚集索引对分区方案的依赖,否则需要直接删除表

IF EXISTS(SELECT * FROM sysobjects WHERE name='pk_order_detail' and xtype='PK')
ALTER TABLE dbo.order_detail DROP CONSTRAINT [pk_order_detail];
ALTER TABLE dbo.order_detail ADD CONSTRAINT [pk_order_detail] PRIMARY KEY CLUSTERED(order_id, product_id) ON [PRIMARY];

删除分区方案和分区函数

IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = 'scheme_order_date')
DROP PARTITION SCHEME scheme_order_date;
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = 'partition_order_date')
DROP PARTITION FUNCTION partition_order_date;

删除分区后聚集索引只剩一个分区

sqlserver 数据库表分区

滑动窗口方案:

  分区表分区切换并没有真正去移动数据,而是 SQL Server 在系统底层改变了表的元数据。因此分区表分区切换是高效、快速、灵活的。利用分区表的分区切换功能,我们可以快速加载数据到分区表。卸载分区数据到普通表,然后 truncate 普通表,以实现快速删除分区表数据。快速归档不活跃数据到历史表。   

  如果您打算在滑动窗口方案中管理数据,通常需要有一个可以拆分的空分区,以便放置新数据。移入和移出表的过程非常快,而且准备工作可以在分区表外完成。在SQL Server 2005的解决方案中:您可以轻松地移入新填充的分区(作为现有分区架构的额外分区),还可以移出任何旧分区。整个过程只需要很短的时间即可完成,通过使用并行批量加载和并行索引建立,还可以进一步提高效率。更重要的是,因为分区是在表范围之外进行管理的,所以添加分区之前不会对所查询的表造成任何影响。添加一个分区通常只需要几秒钟。当下一个月的数据(在本例中是 2017年03月)可用时,将按特定的操作顺序使用现有的文件组移入和移出数据。

一、管理将要移入的分区的分段表

1. 创建分段表

  为了获取更好的性能,将数据加载到未建立索引且未应用约束的堆中,然后在将表移入分区表之前添加约束(参见步骤 3)WITH CHECK。

CREATE TABLE [dbo].[order_detail_201703](
[order_id] int NOT NULL,
[product_id] int NOT NULL,
[order_qty] int NOT NULL,
[unit_price] decimal(24,8) NOT NULL,
[total_amount] decimal(24,8) NOT NULL,
[order_date] datetime NOT NULL
) ON [FG_OrderDetail_01]

2. 加载包含数据的分段表。如果文件是一致的,此过程应该通过 BULK INSERT 语句执行。

  如果要将数据单独放到一个新创建的(空)且未建立索引(堆)的表中,则可以先加载数据,而在加载数据之后建立索引。通常情况下,使用这种架构可以获得十倍或更好的性能。实际上,通过加载未建立索引的表可以利用多个 CPU,因此可以并行加载多个数据文件或从同一个文件中加载多个数据块(通过开始和结束行位置来定义)。

3. 加载数据后,即可添加约束。

ALTER TABLE [dbo].[order_detail_201703]
WITH CHECK ADD CONSTRAINT OrderDetailDateCK
CHECK ([order_date] >= '' AND [order_date] <= '20170331 23:59:59.997')

4. 为分段表建立索引。分段表必须与其要移入的表(成为该表的一个分区)具有相同的群集索引。

ALTER TABLE [dbo].[order_detail_201703]
ADD CONSTRAINT pk_order_detail PRIMARY KEY CLUSTERED (order_id, product_id) ON [FG_OrderDetail_01]

二、创建将要移出的分区的分段表

1. 创建第二个分段表。这是一个空表,用于存储移出的分区中的数据

CREATE TABLE [dbo].[order_detail_201701](
[order_id] int NOT NULL,
[product_id] int NOT NULL,
[order_qty] int NOT NULL,
[unit_price] decimal(24,8) NOT NULL,
[total_amount] decimal(24,8) NOT NULL,
[order_date] datetime NOT NULL
) ON [FG_OrderDetail_01]

2. 为分段表建立索引。分段表必须与其要移入的表(成为该表的一个分区,而该分区将成为此表)具有相同的群集索引。

ALTER TABLE [dbo].[order_detail_201701]
ADD CONSTRAINT pk_order_detail PRIMARY KEY CLUSTERED (order_id, product_id) ON [FG_OrderDetail_01] 

三、移出旧数据,并将新数据移入分区表。

1. 移出旧数据,放入第二个分段表中。

ALTER TABLE [dbo].[order_detail] SWITCH PARTITION 1 TO [dbo].[order_detail_201701]

2. 更改分区函数以删除2017年1月的边界点。此操作还会删除文件组与分区架构之间的关联。具体来说,FG_OrderDetail_01将不再是分区架构的一部分。

ALTER PARTITION FUNCTION Fun_OrderDetail() MERGE RANGE ('20170131 23:59:59.997')

3. 滚动现有2个分区的新数据,使FG_OrderDetail_01成为“下一个使用的”分区,此分区将是下一个用于拆分的分区。

ALTER PARTITION SCHEME Sch_OrderDetail NEXT USED [FG_OrderDetail_01]

4. 更改分区函数,为2017年3月添加新的边界点。

ALTER PARTITION FUNCTION Fun_OrderDetail() SPLIT RANGE ('20170331 23:59:59.997')

5. 更改基础表的约束定义(如果存在),以允许新范围的数据。
  因为添加约束的代价可能很昂贵(需要验证数据),所以最好的做法是继续扩大日期范围,而不是删除并重新创建约束。现在,只存在一个约束(OrderDetailDateCK),但以后将存在两个约束。

ALTER TABLE [dbo].[order_detail] ADD CONSTRAINT OrderDetailMinDateCK CHECK ([order_date] >= '')
ALTER TABLE [dbo].[order_detail] ADD CONSTRAINT OrderDetailMaxDateCK CHECK ([order_date] < '')
ALTER TABLE [dbo].[order_detail] DROP CONSTRAINT OrderDetailDateCK

6. 从第一个分段表中移入新数据。

ALTER TABLE [dbo].[order_detail_201703] SWITCH TO [dbo].[order_detail] PARTITION 2

四、删除分段表

DROP TABLE [dbo].[order_detail_201701]
DROP TABLE [dbo].[order_detail_201703]

五、备份文件组

  最后一步备份的对象是根据您的备份策略选择的。如果选择了基于文件或文件组的备份策略,则应执行文件或文件组备份。如果选择了基于整个数据库的备份策略,则可以执行完整数据库备份或差异备份。

BACKUP DATABASE OrderDB FILEGROUP = 'FG_OrderDetail_01' TO DISK = 'C:\OrderDB\OrderDB.bak'

总结 

  SQL Server 允许根据范围进行分区,还允许将表和索引都设计为使用相同的架构,以便更好地对齐。SQL Server 2005 已经考虑了如何简化分区的管理、开发和使用。它在性能和可管理性方面有以下优点:

  • 简化了需要进行分区以改善性能或可管理性的大型表的设计和实现。

  • 将数据加载到现有分区表的新分区中时,最大程度地减少了对其他分区中的数据访问的影响。

  • 将数据加载到现有分区表的新分区中时,性能相当于将同样的数据加载到新的空表中。

  • 在存档和/或删除分区表的一个分区时,最大程度地减少了对表中其他分区的访问的影响。

  • 允许通过将分区移入和移出分区表来维护分区。

  • 提供了更好的伸缩性和并行性,可以对多个相关表执行大量操作。

  • 改善了所有分区的性能。

  • 缩短了查询优化时间,因为不需要单独优化每个分区。

附加:合理组织数据库文件和文件

   1.主文件组完全独立,只存放系统对象,所有的用户对象都不在主文件组中,主文件组也不应该设为默认文件组,将系统对象和用户对象分开可以获得更好的性能
   2.如果有多块硬盘,可以将每个文件组中的文件分配到每个硬盘上,这样可实现分布式磁盘I/O,提高读写速度
   3.将访问频繁的表及其索引放到一个独立的文件组中,这样可以提高读取数据和索引的速度.
   4.将访问频繁的包含TEXT和IMAGE数据类型的列的表放到一个单独的文件组中,最好将 TEXT 和IMAGE放在一个独立的硬盘中
   5.将事务日志文件放到一个独立的硬盘上,千万不要和DATEFILE共用一个硬盘;日志操作属于密集型操作.
   6.将'只读'表单放到一个单独的文件组中,同样,'只写'也是
   7.不要过度使用'自动增长',设置自动增长值为一个合适的值,如:一周,同样,'自动收缩'也是如此
上一篇:解决sqlserver数据库表空间不自动释放问题


下一篇:SQLServer数据库,表内存,实例名分析SQL语句