和压缩(Compression)相比,数据库分区(Partition)的操作更为复杂繁琐。而且与Compression一次操作,终身保持不同,分区是一项需要长期维护周期变更的操作。
分区的意义在于将大数据从物理上切割为几个相互独立的小部分,从而在查询时只取出其中一个或几个分区,减少影响的数据;另外对于置于不同文件组的分区,并行查询的性能也要高于对整个表的查询性能。
事实上,在SQL Server 2005中就已经包含了分区功能,甚至在2005之前,还存在一个叫做“Partitioned Views”的功能,能通过将同样结构的表Union在一个View中,实现类似现在分区表的效果。而在SQL Server 2008中,分区功能得到了显著加强,使得我们不仅能够对表和索引做分区,而且允许对分区上锁,而不是之前的全表上锁。
和Compression一样,在SQL Server 2008中也提供了分区的向导界面。在企业管理器中,需要分区的表上右键选择Storage-》Create Partition:
这里会列出该表所有的字段,包括字段类型、长度、精度及小数位数的信息,可以选择其中的任意一一列作为分区列(Patitioning Column),不仅仅是数字或者日期类型,即使是字符串类型的列,也可以按照字母顺序进行分区。而以下类型的列不可用于分区:text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名、hierarchyid、空间索引或 CLR 用户定义的数据类型。此外,如果使用计算列作为分区列,则必须将该列设为持久化列(Persisit)。
在列表下方,提供了两个选项:
注意:这里建议使用聚集索引列作为分区列。一方面索引结构本身就应与查询相关,那么分区列与索引一致会保证查询的最大效率;另一方面,保证索引对齐而且是聚集索引对齐是保证分区的移入移出操作顺畅的前提,否则可能会出现无法移入移出的情况,而分区的移入移出又是管理大数据的重要策略——滑动窗口(SlideWindow)策略的基础操作。另外,如果要进行索引对齐,需要所有索引和表的压缩模式一致。
选好分区列后,如果没有应用“分配到可用分区表”选项,接下来则会进入选择\创建分区函数以及分区方案的界面。其中分区函数会指定分区边界,而分区方案则规划了每个分区所存储的文件组。
向导操作界面如下:
其中Left boundary说明每个分区的边界值被包含在边界值左侧的分区中,也就是每个分区内的数据约束是<=指定的边界值,相应的,Right boundary则说明每个分区的边界值被包含在边界值右侧的分区中,每个分区内的数据约束是<指定的边界值。
在下方的列表中,列出了当前分区方案下现有的分区。其中文件组(Filegroup)指定了每个分区存放的位置,如果将分区放置于位于不同磁盘中的不同文件组中,由于不同磁盘的读写互不干扰,这将提高分区表并行处理的效率。一般情况下,将所有分区放置在同一个文件组是比较稳妥的做法。关于文件组的展开阅读可以参阅:SQL Server Filegroups。
注意,在这里最后一个分区是没有指定边界的,用于保存所有>(Left Boundary)或>=(Right boundary)最后一个分区边界的数据。
如果选择时间类型的字段作为分区列,可以通过Set按钮实现按条件分组:
这样可以很方便得通过设置起止时间将表按照指定时间段自动分区,但之后依然需要手动指定每个分区的文件组。
制定好分区方案之后可以通过Estimate sotrage预估每个分区的行数、空间占用情况,不过除非需要以占用空间或行数来规划你的分区策略,一般不建议在这里进行预估,因为如果对空表来说,预估的结果当然都是0,而如果表中已经包含大量数据,预估则会花费比较长的时间。
通过以上设置,分区已经基本完毕,在向导的最后,可以选择是创建脚本还是立即执行分区操作。
我们可以查看在不同情况下创建分区的脚本的情况:
1.在表没有索引的情况下:
BEGIN TRANSACTION CREATE PARTITION FUNCTION [TestFunction](datetime) AS RANGE LEFT FOR VALUES (N'2010-01-01T00:00:00', N'2010-02-01T00:00:00', N'2010-03-01T00:00:00', N'2010-04-01T00:00:00', N'2010-05-01T00:00:00', N'2010-06-01T00:00:00') CREATE PARTITION SCHEME [TestScheme] AS PARTITION [TestFunction] TO ([PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY]) CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] ( [birthday] )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday]) DROP INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] WITH ( ONLINE = OFF ) COMMIT TRANSACTION
这里先创建Partition Function以及Partition Scheme,之后在分区列上创建聚集索引并按照分区方案分区,最后删除了这一索引。>
2.在表有索引的情况下:
如果原先没有聚集索引:
CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] ( [birthday] )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday]) DROP INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] WITH ( ONLINE = OFF )
这和没有索引的情况一样,如果表原先存在聚集索引,则脚本变为:
CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [TestScheme]([birthday])
可以看到原有的聚集索引(IX_id)在分区方案上被重建了。
如果选择了“对齐索引”选项,则会对所有索引都应用分区:
CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [TestScheme]([birthday]) CREATE NONCLUSTERED INDEX [UIX_birthday] ON [dbo].[Account] ( [birthday] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [TestScheme]([birthday]) CREATE NONCLUSTERED INDEX [UIX_name] ON [dbo].[Account] ( [name] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
这里不仅对聚集索引IX_id进行了分区,也对非聚集索引UIX_name和UIX_birthday进行了分区。
由于我在实际操作中主要考虑并行查询方面的效率,所以文章里只是略略带过,但评论中有人提到,所以摘录整理一些资料在下面:
转自:http://www.cnblogs.com/smjack/archive/2010/02/23/1671943.html
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
管理分区:
正如上一篇文章SqlServer性能优化——Partition(创建分区)中所述,分区并不是一个一劳永逸的操作,对一张表做好分区仅仅是开始,接下来可能需要频繁的变更分区,管理分区。
在企业管理器中,虽然有“管理分区”的菜单,里面的内容却可能与你的预想不同,这里并没有提供直接对分区进行操作的方法,所以一些普通的操作,比如“增加分区”、“删除分区”之类的操作就需要通过脚本实现了。
“增加分区”事实上就是将现有的分区分割开,基于此,在SQL Server中应用的是Split操作。在分离分区的时候,不仅仅要在Partition Function上指定分割的分界点,同样需要在Partition Scheme上指定新分区应用的文件组:
--指定下一个分区应用文件组PRIMARY ALTER PARTITION SCHEME [MyPartitionSchema] NEXT USED [PRIMARY] --指定分区分界点为5000 ALTER PARTITION FUNCTION MyPartitionFunction() SPLIT RANGE (5000)
需要注意的一点是,新增的分区中非聚簇索引的压缩模式会被置为None。
“删除分区”同样可以认为是将原来分离的分区合并在一起,所以对应的是Merge操作,而且由于并没有新增的分区,Partition Scheme并不需要改变:
ALTER PARTITION FUNCTION MyPartitionFunction () MERGE RANGE (5000)
“切换分区”可能是一个比看上去会应用的更频繁的操作,它的意义在于将一个分区的数据从一张表切换到另一张表中。这里定义被切换分区的表为“源表”,被切换到的表为“目标表”,则执行切换操作的前提是:
下面的操作将源表的第二个分区切换到目标表的第二个分区中。
ALTER TABLE [STable] SWITCH PARTITION 2 TO [DTable] PARTITION 2
分割、合并以及切换分区是元数据上的操作而不是对数据的移动,所以操作的效率要比直接操作数据高很多。
除了利用上文提到的通过“管理压缩”的方式查看某张表的分区信息之外,SQL Server还提供了一张系统表查看数据库中的分区情况:
比如可以通过如下的脚本,查出分区函数MyPartitionFunc的第一个分区的右边界:
SELECT value FROM sys.partition_range_values, sys.partition_functions WHERE sys.partition_functions.function_id = sys.partition_range_values.function_id AND sys.partition_functions.name = 'MyPartitionFunc' AND boundary_id = 1
还可以通过如下脚本,获取分区表中各分区的数据情况(行数,最大值,最小值):
SELECT partition = $PARTITION.MyParitionFunc([ParitionDate]) ,rows = COUNT(*) ,min = MIN([ParitionDate]) ,max = MAX([ParitionDate]) FROM [MyTable] GROUP BY $PARTITION.MyParitionFunc([ParitionDate]) ORDER BY PARTITION
具体可以参照MSDN:从已分区表和索引中查询数据和元数据
相关资料:
http://msdn.microsoft.com/zh-cn/library/ms188706.aspx
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
实战:
文件组
|
分区
|
取值范围
|
FG1
|
1
|
(过去某年, 2003/01/01)
|
Fg2
|
2
|
[2003/01/01, 2004/01/01)
|
Fg3
|
3
|
[2004/01/01,未来某年)
|
文件组
|
分区
|
取值范围
|
FG1
|
1
|
(过去某年, 2003/01/01)
|
Fg2
|
2
|
[2003/01/01, 2004/01/01)
|
Fg3
|
3
|
[2004/01/01, 2005/01/01)
|
Fg2
|
4
|
[2005/01/01, 未来某年)
|
文件组
|
分区
|
取值范围
|
Fg2
|
1
|
[过去某年, 2004/01/01)
|
Fg3
|
2
|
[2004/01/01, 2005/01/01)
|
Fg2
|
3
|
[2005/01/01, 未来某年)
|
本文出自 “李涛的技术专栏” 博客,请务必保留此出处http://terryli.blog.51cto.com/704315/169601