一、 背景
这个数据库的数据文件mdf大概有8.5G左右,当还原数据库之后感觉可以做很多性能方面上的调优,合并数据后mdf数据文件大概有6.2G左右,行压缩后mdf数据文件大概有4.8G左右,页压缩后mdf数据文件大概有4.5G左右,这里处于技术研究的目的,讲讲研究的成果分析,不用于商业目的;
二、 优化项
我们可以从下面3个不同的方面来优化这两个数据库:
(一)对表进行分区;
(二)使用行压缩,压缩行数据;
(三)重新设计表结构,优化表空间;
三、 附加数据库
1.先把11个QunInfo(群信息)数据库附加到数据库,下面的导入SQL语句在原来的基础上做了些修改:统一数据库名,这样做的好处就是后面做处理的时候方便按照顺序执行数据库;
--附加数据库EXEC sp_attach_db "QunInfo01", "D:\DBBackup\QunData\QunInfo1_Data.MDF"EXEC sp_attach_db "QunInfo02", "D:\DBBackup\QunData\QunInfo2_Data.MDF"EXEC sp_attach_db "QunInfo03", "D:\DBBackup\QunData\QunInfo3_Data.MDF"EXEC sp_attach_db "QunInfo04", "D:\DBBackup\QunData\QunInfo4_Data.MDF"EXEC sp_attach_db "QunInfo05", "D:\DBBackup\QunData\QunInfo5_Data.MDF"EXEC sp_attach_db "QunInfo06", "D:\DBBackup\QunData\QunInfo6_Data.MDF"EXEC sp_attach_db "QunInfo07", "D:\DBBackup\QunData\QunInfo7_Data.MDF"EXEC sp_attach_db "QunInfo08", "D:\DBBackup\QunData\QunInfo8_Data.MDF"EXEC sp_attach_db "QunInfo09", "D:\DBBackup\QunData\QunInfo9_Data.MDF"EXEC sp_attach_db "QunInfo10", "D:\DBBackup\QunData\QunInfo10_Data.MDF"EXEC sp_attach_db "QunInfo11", "D:\DBBackup\QunData\QunInfo11_Data.MDF"
四、 合并数据库
2.修改各个数据库中表的名字:把QunList1统一修改为QunList01这样格式的,这样做的好处就是在合并数据的时候读取到的数据库的数据是按照顺序插入到表中的,不会造成数据页的拆分;
--格式化表名USE QunInfo01GOexec sp_rename 'QunList1','QunList01'exec sp_rename 'QunList2','QunList02'exec sp_rename 'QunList3','QunList03'exec sp_rename 'QunList4','QunList04'exec sp_rename 'QunList5','QunList05'exec sp_rename 'QunList6','QunList06'exec sp_rename 'QunList7','QunList07'exec sp_rename 'QunList8','QunList08'exec sp_rename 'QunList9','QunList09'
3.创建一个名为QunInfo的数据库,设置数据库为简单恢复模式;
4.在QunInfo数据库中创建一个临时表:tables,用来保存所有的数据库与表的信息,提供数据库合并用;
--创建临时表CREATE TABLE [QunInfo].[dbo].[tables]( [db_name] [sysname] NULL, [table_name] [sysname] NULL, [status] [bit] default 0) ON [PRIMARY]--生成数据库名称与表名称的对应列表EXEC sp_MSForEachDB 'USE [?]; --插入表信息 INSERT INTO [QunInfo].[dbo].[tables]([table_name]) SELECT name from [?].sys.tables where name like ''QunList%'' order by name --更新数据库名称 UPDATE [QunInfo].[dbo].[tables] SET [db_name] = ''?'' WHERE [db_name]
五、 优化数据库
5.经过评估,11个QunInfo数据库的QunList表数据的总和大概有9千万,QunList表中QunNum(群号)字段的最大值为100219998(可以通过QunInfo11数据库的QunList110表查询到:SELECT MAX(QunNum) FROM [QunInfo11].[dbo].[QunList110]),从业务的角度,可能需要查询某群的信息,所以这里就以QunNum作为分区,每1千万个群作为一个分区,这样计算那就需要11个文件组,如果你希望和GroupData数据库的Group表对齐的话,也可以按照5百万个群作为一个分区;
6.下面是一个创建分区脚本的SQL脚本,执行下面的SQL会生成一个新的脚本,执行那个脚本就可以创建11个文件组、分区函数和分区方案;
--生成分区脚本DECLARE @DataBaseName NVARCHAR(50)--数据库名称DECLARE @TableName NVARCHAR(50)--表名称DECLARE @ColumnName NVARCHAR(50)--字段名称DECLARE @PartNumber INT--分区最大编号DECLARE @PartNumberBegin INT--分区编号开始值DECLARE @PartNumberBeginTemp INT--分区编号开始值临时值DECLARE @PartNumberStr NVARCHAR(50)--分区值字符串DECLARE @Location NVARCHAR(50)--保存分区文件的路径DECLARE @Size NVARCHAR(50)--分区初始化大小DECLARE @FileGrowth NVARCHAR(50)--分区文件增量DECLARE @FunValue INT--分区分段值增量DECLARE @FunValueBegin INT--分区分段值开始值DECLARE @i INT--临时变量DECLARE @sql NVARCHAR(max)--设置下面变量SET @DataBaseName = 'QunInfo'SET @TableName = 'QunList'SET @ColumnName = 'QunNum'SET @PartNumber = 11SET @PartNumberBegin = 1SET @Location = 'D:\DBBackup\FG_QunList\'SET @Size = '1024MB'SET @FileGrowth = '1024MB'SET @FunValueBegin = 10000000SET @FunValue = 10000000SET @sql = 'USE ['+@DataBaseName +']GO'PRINT @sql + CHAR(13)--1.创建文件组SET @i = 1SET @PartNumberBeginTemp = @PartNumberBeginPRINT '--1.创建文件组'WHILE @i <= @PartNumberBEGIN SET @PartNumberStr = RIGHT('0' + CONVERT(NVARCHAR,@PartNumberBeginTemp),2) SET @sql = 'ALTER DATABASE ['+@DataBaseName +']ADD FILEGROUP [FG_'+@TableName+'_'+@ColumnName+'_'+@PartNumberStr+']' PRINT @sql + CHAR(13) SET @i=@i+1 SET @PartNumberBeginTemp = @PartNumberBeginTemp+1END--2.创建文件SET @i = 1SET @PartNumberBeginTemp = @PartNumberBeginPRINT CHAR(13)+'--2.创建文件'WHILE @i <= @PartNumberBEGIN SET @PartNumberStr = RIGHT('0' + CONVERT(NVARCHAR,@PartNumberBeginTemp),2) SET @sql = 'ALTER DATABASE ['+@DataBaseName +']ADD FILE(NAME = N''FG_'+@TableName+'_'+@ColumnName+'_'+@PartNumberStr+'_data'',FILENAME = N'''+@Location+'FG_'+@TableName+'_'+@ColumnName+'_'+@PartNumberStr+'_data.ndf'',SIZE = '+@Size+', FILEGROWTH = '+@FileGrowth+' )TO FILEGROUP [FG_'+@TableName+'_'+@ColumnName+'_'+@PartNumberStr+'];' PRINT @sql + CHAR(13) SET @i=@i+1 SET @PartNumberBeginTemp = @PartNumberBeginTemp+1END--3.创建分区函数PRINT CHAR(13)+'--3.创建分区函数'DECLARE @FunValueStr NVARCHAR(MAX)DECLARE @PNB INTSET @i = 1SET @PNB = 1SET @FunValueStr = convert(NVARCHAR(50),@FunValueBegin) + ','WHILE @i < @PartNumber-1BEGIN SET @FunValueStr = @FunValueStr + convert(NVARCHAR(50),(@FunValueBegin+@PNB*@FunValue)) + ',' SET @i=@i+1 SET @PNB=@PNB+1ENDSET @FunValueStr = substring(@FunValueStr,1,len(@FunValueStr)-1)SET @sql = 'CREATE PARTITION FUNCTION[Fun_'+@TableName+'_'+@ColumnName+'](INT) ASRANGE RIGHTFOR VALUES('+@FunValueStr+')'PRINT @sql + CHAR(13)--4.创建分区方案PRINT CHAR(13)+'--4.创建分区方案'DECLARE @FileGroupStr NVARCHAR(MAX) SET @i = 1SET @PartNumberBeginTemp = @PartNumberBeginSET @FileGroupStr = ''WHILE @i <= @PartNumberBEGIN SET @PartNumberStr = RIGHT('0' + CONVERT(NVARCHAR,@PartNumberBeginTemp),2) SET @FileGroupStr = @FileGroupStr + '[FG_'+@TableName+'_'+@ColumnName+'_'+@PartNumberStr+'],' SET @i=@i+1 SET @PartNumberBeginTemp = @PartNumberBeginTemp+1ENDSET @FileGroupStr = substring(@FileGroupStr,1,len(@FileGroupStr)-1)SET @sql = 'CREATE PARTITION SCHEME[Sch_'+@TableName+'_'+@ColumnName+'] ASPARTITION [Fun_'+@TableName+'_'+@ColumnName+']TO('+@FileGroupStr+')'PRINT @sql + CHAR(13)--5.分区函数的记录数PRINT CHAR(13)+'--5.分区函数的记录数'SET @sql = 'SELECT $PARTITION.[Fun_'+@TableName+'_'+@ColumnName+']('+@ColumnName+') AS Partition_num, MIN('+@ColumnName+') AS Min_value,MAX('+@ColumnName+') AS Max_value,COUNT(1) AS Record_numFROM dbo.['+@TableName+']GROUP BY $PARTITION.[Fun_'+@TableName+'_'+@ColumnName+']('+@ColumnName+')ORDER BY $PARTITION.[Fun_'+@TableName+'_'+@ColumnName+']('+@ColumnName+');'PRINT @sql + CHAR(13)
7.下面重新对QunList表进行设计,涉及的内容如下:
1) 在QunInfo数据库中创建分区表QunList,这里已经把原表的ID字段去掉了,这个字段并没有太大的意义;
2) 以[QunNum]作为聚集索引,而且是唯一的,这个需要开启IGNORE_DUP_KEY = ON选项,这样才可以在批量插入的时候忽略重复值;
3) 对原表的[MastQQ]字段从int类型变成smallint ,[CreateDate]字段从varchar(10)类型变为date,数据类型修改是为了减少表占用的空间,
4) 使用刚刚创建好的分区方案,之后创建的索引进行索引对齐;
5) 对表使用行压缩,减少数据库占用空间;
6) 对表进行页压缩会更节省空间?
--创建优化后的QunList表CREATE TABLE [dbo].[QunList]( [QunNum] [int] NOT NULL, [MastQQ] [smallint] NULL, [CreateDate] [date] NULL, [Title] [varchar](22) NULL, [Class] [varchar](38) NULL, [QunText] [varchar](80) NULL, CONSTRAINT [PK_QunList2] PRIMARY KEY CLUSTERED ( [QunNum] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = ROW) ON [Sch_QunList_QunNum]([QunNum])) ON [Sch_QunList_QunNum]([QunNum])GO
(Figure1:GroupData原表结构)
(Figure2:GroupData新表结构)
8.把11个数据库都合并到新创建的QunInfo的QunList表中;
--合并数据DECLARE @tablename sysnameDECLARE @dbname sysnameDECLARE @sql NVARCHAR(max)--游标DECLARE @itemCur CURSORSET @itemCur = CURSOR FOR SELECT db_name,table_name from [QunInfo].[dbo].[tables]OPEN @itemCurFETCH NEXT FROM @itemCur INTO @dbname,@tablenameWHILE @@FETCH_STATUS=0BEGIN SET @sql = ' INSERT INTO [QunInfo].[dbo].[QunList] ([QunNum] ,[MastQQ] ,[CreateDate] ,[Title] ,[Class] ,[QunText]) SELECT [QunNum] ,[MastQQ] ,[CreateDate] ,[Title] ,[Class] ,[QunText] FROM ['+@dbname+'].[dbo].['+@tablename+']' EXEC(@sql) UPDATE [QunInfo].[dbo].[tables] SET status = 1 WHERE db_name = @dbname AND table_name = @tablename --返回SQL PRINT(@sql)PRINT('GO')+CHAR(13) FETCH NEXT FROM @itemCur INTO @dbname,@tablenameEND CLOSE @itemCurDEALLOCATE @itemCur
(Figure3:QunList表分区记录数)
(Figure4:QunList数据行压缩前)
(Figure5:QunList数据行压缩后)
我们使用页压缩修改表QunList,看看数据占用大小的情况:
--页压缩ALTER TABLE [QunList] REBUILD WITH (DATA_COMPRESSION = PAGE );
(Figure6:QunList数据页压缩后)