一.需求背景
SQL Server开源的归档工具不多,DBA一般都是通过计划任务来触发执行,执行的脚本多是SP或者是SSIS包。SSIS包的性能稍好一些,但是维护更新成本高些。所以更常见的是通过SP脚本来实现归档操作。
当数据库规模较小时,可以方便的直接在数据库上进行脚本的编写部署。但是随着数据库越来越多,管理维护成本就会越来越大,越来越不方便。现在我们实行的方式是通过中央管理器来管理众多的数据库备份(这是在拥有专门的备份程序前的一个过渡方案)。我们将归档基础配置信息、归档运行历史记录、异常报错等数据统一维护在中央数据库上。如此,可以方便统一的查看、管理和维护。
二.主要架构
三.主要关联表
2.1 归档基础配置表
表字段含义,请耐心查看字段说明。
CREATE TABLE [dbo].DBData_ArchiveConfig]( ID] intIDENTITY(1,1) NOT NULL,IPvarchar](50) DBNameDataTableTargetIPTargetDBTargetTablePrerequisite300) DelMaxQTY] IsCheckOrderIDSP_NameStartTimedatetimeEndTime ) ON PRIMARY] GO EXEC sys.sp_addextendedproperty @name=N'MS_Description',@valueServer IP(数据位于中央管理器中,所以归档数据库库所在的IP要维护,可维修虚拟的IP)@level0typeSCHEMA@level0name@level1typeTABLE@level1name@level2typeCOLUMN@level2name' 要归档的数据库要归档的表备份指向的IP备份指向的数据库备份指向的表归档条件循环中一次归档删除的数据量此为 备用字段,考虑可能有些表,会和其他表关联为提高并发度,一个DB对应的归档SP可能是多个,通过此列,进行分组。此为拓展字段,原计划根据 开始时间、结束时间,每天可以多个时间段内执行GO
2.2 归档运行的Log表
DBData_ArchiveLog30) 80) BakQTYBakStartDateBakEndDateGO
2.3 异常错误信息表
执行的过程中会外包一层 try...catch,将操作过程中的错误信息保存在表 DBData_ArchiveErrLog。表结构如下:
DBData_ArchiveErrLog60) Errormsgnvarchar](max) TransDateTime] TEXTIMAGE_ON GO
四. 存储过程相应的主要代码
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER -- ============================================= -- Author: <Author,Name> Create date: <Create Date,> Description: <Description,1)"> ============================================= PROCEDURE SP_XXXXX_DataArchive] AS SET NOCOUNT ON; DECLARE @sql1 VARCHAR(MAX) @sql @sql2 ) @IP @DBName @DataTable @TargetIP @TargetDB @TargetTable @Prerequisite @DelMaxQTY INT @StartTime DATETIME @EndTime @qty INT @ISCHECKORDERID INT --Carson 2018-12-17 备份数据的时间往往比删除的时间长3倍,因此,如果考虑将备份的操作转移到辅助库,将会对线上的操作影响降至更低 @BakDateIP VARCHAR(30) set @BakDateIP=[XXX.XXX.XXX.XXX].'-----后面一定要有一个点------------------------------------------------归档操作--------------------------------- DECLARE DBName CURSOR FOR SELECT IP,DBName,DataTable,TargetIP,TargetDB,TargetTable,Prerequisite,DelMaxQTY,ISCHECKORDERID,StartTime,EndTime FROM 中央管理器中央管理数据库] WHERE DataTable <> '' AND TargetTable AND DBNAME = XXXXXXXXX' and SP_Name?????' OPEN DBName FETCH NEXT FROM DBName INTO @IP,1)">@DBName,1)">@DataTable,1)">@TargetIP,1)">@TargetDB@TargetTable,1)">@Prerequisite,1)">@DelMaxQTY,1)">@ISCHECKORDERID@StartTime,1)">@EndTime WHILE ( @@fetch_status = 0 ) BEGIN @datetime DATETIME IF @ISCHECKORDERID 1' AND @DataTable '' BEGIN SET @datetime CONVERT(10),GETDATE() - 30,1); font-weight: bold">120) @sql Insert into [+ @TargetIP + ].' @TargetDB .dbo.@TargetTable select * FROM @BakDateIP @DBName with(nolock) where @Prerequisite '' @sql1 DECLARE @icount INTEGER SELECT @icount = COUNT(1) FROM where insert into [中央管理器].[中央管理数据库].dbo.DBData_ArchiveLog (IP,BakQTY,BakStartDate,BakEndDate) select ''' @IP '''@DataTable CAST(AS 10)) ) FROM where SET @icount = @icount -(' ) WAITFOR DELAY ''00:00:01 END ' BEGIN TRY EXEC (@sql) @sql1) END TRY CATCH @Errmsg nvarchar() SELECT @Errmsg=ERROR_MESSAGE() ----0001 BEGIN SAVE ERR LOG IN TABLE INSERT INTO ].DBData_ArchiveErrLog (],1)">) VALUES(@TargetDB,1)">@Errmsg,1)">convert(25),1)">GETDATE(),1)">)) ----0001 END -----------0002 BEGIN SEND EMAIL MESSAGE---------------- @Subject nvarchar(200) @Body @SPName ) @Subject 数据库归档异常 -重要!;ServerIP: DB:@DBName @SPName '' @Body <html><body>Dear All,<br> <br> ServerIP:+ ; DataBase:@DBName上的Table归档异常,请及时检查!!! <br> You can get detail information from the table. <br><br><table border=1 bgcolor=#aaff11>' = @Body<tr bgcolor=#ff3311><td>ServerIP</td><td>DBName</td><td>TableName</td><td>TargetIP</td><td>TargetDB</td><td>Errmsg</td><td>TransDateTime</td></tr>' SELECT <tr bgcolor=#ffaa11><td>'NVARCHAR(50))</td><td>50)) +</td> <td>20))SUBSTRING(100)varchar(100),1); font-weight: bold">21)</td></tr></table>' @BODYREPLACE(@BODY,1)">'''',1)">'') IF ' '')<>'' BEGIN @AllEmailToAddress 3000)'' @AllEmailCcAddress @Allprofile_name @AllEmailToAddress@AllEmailCcAddressTOP 1 @Allprofile_name=NAME FROM msdb.dbo.sysmail_profile ORDER BY profile_id EXEC msdb..sp_send_dbmail @profile_name @Allprofile_name profile 名称 ,1)">@recipients = @AllEmailToAddress 收件人邮箱 ,1)">@copy_recipients@AllEmailCcAddress@subject @Subject 邮件标题 ,1)">@body @BODY 邮件内容 ,1)">@body_format = HTML' 邮件格式 ,1)">@file_attachments@Importance High' END ----------- 0002 end ------------ CATCH END @TargetIP@DelMaxQTY@ISCHECKORDERID,1)">@EndTime END CLOSE DBName DEALLOCATE DBName DECLARE DELETETABLE ???? DELETETABLE FROM DELETETABLE @DataTable@DelMaxQTY ) BEGIN DECLARE @icount INTEGER SELECT @icount = COUNT(1) FROM where WHILE @icount > 0 BEGIN DELETE TOP () FROM where SET @icount = @icount -(' ) WAITFOR DELAY END PRINT @sql1 @DelMaxQTY END DELETETABLE DELETETABLE GO
五.补充数据
1.数据库归档,一般都是先将当前库的历史数据归档到历史库,再将当前库的历史数据删除。这两个阶段,一般是前者耗时较多(一般都在2:1以上),虽然可以在select 过程加上nolock,但是或者I/O或者网络等原因,其实这个阶段对应用程序的影响还是比较大的。所以,建议将这两个阶段物理分开,即如果有配置AlwaysOn,请将第一个阶段在辅助数据库中执行。上面的SP示例,就是通过参数 @BakDateIP 来实现了这一作用。
2.存储过程中包含了try...catch,所以运行此sp就会很少报错,某一个表的异常不会相互影响。例如,我们常见的当前库、历史库由于表结构变更而导致的不一致,此情况出现后,try..catch可以捕捉到异常,将异常记录在档,并将此信息以邮件的形式发送给指定人,但整个SP不会执行失败。并且还会跳过这一个异常,继续执行下一个备份归档表的归档。
本文版权归作者所有,未经作者同意不得转载,谢谢配合!!!