SQL Server 批量主分區備份(Multiple Jobs)

一.本文所涉及的內容(Contents)

  1. 本文所涉及的內容(Contents)
  2. 背景(Contexts)
  3. 案例分析(Case)
    1. 方案一(Solution One)
    2. 方案二(Solution Two)
    3. 方案三(Solution Three)
  4. 實現代碼(SQL Codes)
  5. 主分區完整、差別還原(Primary Backup And Restore)
  6. 參考文獻(References)

二.背景(Contexts)

  在個人數據庫實例中,有不少相似下圖所示的數據庫,這些數據庫的名稱是有規律的,每一個數據庫包含的表都是相同的,其中2個表是相似流水記錄的表,表的數據量會比較大,佔用的空間有幾十G到上百G不等,這2個表相對於其它的配置表來講是比較不重要的。html

  如今有一個需求就是對數據庫進行備份,容許丟失這兩個表的數據,保留重要的配置表數據,你是否遇到過一樣的問題呢?這個時候你會怎麼作呢?你有什麼方案呢?有什麼方法能夠快速備份這些數據庫呢?sql

wps_clip_image-2334

(Figure1:數據庫列表)shell

閱讀本文以前你能夠先參考:SQL Server 批量完整備份數據庫

三.案例分析(Case)

  經過上面的描述,其中很重要的一點就是每一個數據庫中有2個大表,並且這些數據是不重要的,那麼咱們對這2個大表作表分區,把大數據放到其它文件組中,只留重要的配置表在主文件組(PRIMARY)中,接着就能夠對主文件組進行備份,這樣既知足了備份重要表數據,並且不會形成備份文件過大、佔用磁盤空間、備份時間過長等問題。測試

方案一:大數據

  使用維護計劃;請參考:SQL Server 維護計劃備份主分區,只要在【執行T-SQL語句】的任務中使用循環遞歸全部數據庫進行備份,在【清除歷史記錄】任務和【清除維護】任務中選擇父目錄就能夠了;ui

 

方案二:spa

  方案一中經過維護計劃生成的做業是看不到具體的備份代碼的,因此方案二就是爲了補充方案一的。若是你沒有做業執行時間的特殊要求,你能夠建立一個做業,循環數據庫名稱進行主分區的備份,只是把維護計劃【執行T-SQL語句】的內容放到了做業中;3d

這個方案有如下幾個缺點:代理

a) 沒有辦法單獨控制某個數據庫備份的時間;

b) 數據庫一多的話,在Job的Message裏面沒有辦法顯示那麼多的信息;

c) 備份串行執行的,沒有辦法進行並行備份;

d) 個個數據庫的備份不是單獨的,若是做業出錯,會形成後面的數據庫沒法備份;

e) 貌似是先寫入內存,等做業完成後才一次性寫入硬盤的?當數據庫備份文件比較大的時候會莫名只備份幾個數據庫做業就退出了,沒有查明是什麼緣由;

具體的操做步驟能夠參考:SQL Server 批量主分區備份(One Job),這個方案最終的做業形式爲:

wps_clip_image-26885

(Figure2:做業列表)

 

方案三:

  方案三是爲了控制備份的時間而準備的,就是每一個數據庫都建立一個做業,這樣的好處是能夠充分的控制到每一個數據庫,由於咱們能夠須要對某個數據庫進行完整備份,並且備份的粒度也有可能不一樣(重要客戶的備份粒度會更小)

wps_clip_image-16066

(Figure3:做業列表)

  既然咱們選擇了方案三,那咱們如何快速(批量)建立這些做業呢?咱們先以數據庫Barefoot.Opinion.9197爲例,建立出一個做業的模板後,經過修改、替換這個做業的代碼來實現批量生成新的可執行的做業腳本;

經過下面的幾個步驟你就能夠批量的生成備份做業:

1) 備份主分區完整備份的SQL代碼(備份配置表);備份主分區差別備份的SQL代碼(減輕備份文件空間壓力)

2) 自動刪除備份文件的SQL代碼;(保證磁盤有足夠空間)

3) 批量建立文件夾;(每一個數據庫單獨一個文件夾)

4) 批量建立做業方案;(每一個做業的名稱、目錄都不相同)

5) 批量修改做業計劃的時間;(均衡分配做業的執行時間)

6) 批量刪除做業;(方便維護)

7) 查看做業的執行狀況;(防止做業時間過長,過長能夠考慮預警)

四.實現代碼(SQL Codes)

(一) 下面的代碼實現了主分區完整備份和主分區差別備份,當是星期一的深夜的時候,咱們作完整備份,若是是其它時候咱們就作差別備份,具體是何時,這個就經過計劃裏面的時候來控制了(計劃的執行時間爲星期1、星期3、星期五,這就表明星期一深夜作了完整備份、星期三和星期五分別作了差別備份)。

生成的備份文件名爲:(這樣的文件名方便閱讀;並且能精確到秒,重複的概率不大)

DBName _Primary_Full_2013_01_14_002007.bak

DBName_Primary_Diff_2013_01_16_002034.bak

--1設置完整模式
USE [master]
GO
ALTER DATABASE [DBName.9197] SET RECOVERY FULL WITH NO_WAIT
GO

--2備份主分區
DECLARE @CurrentTime VARCHAR(50), @FileName VARCHAR(200)
SET @CurrentTime = REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120 ),'-','_'),' ','_'),':','')
IF(DATEPART(DW, GETDATE()) = 2)--星期一
BEGIN
    --完整備份
    SET @FileName = 'E:\DBBackup\DBName.9197\DBName.9197_Primary_Full_' + @CurrentTime+'.bak'
    BACKUP DATABASE [DBName.9197]
    FILEGROUP='PRIMARY' TO DISK=@FileName WITH FORMAT
END
ELSE
BEGIN
    --差別備份
    SET @FileName = 'E:\DBBackup\DBName.9197\DBName.9197_Primary_Diff_' + @CurrentTime+'.bak'
    BACKUP DATABASE [DBName.9197]
    FILEGROUP='PRIMARY' TO DISK=@FileName WITH DIFFERENTIAL,FORMAT
END
GO

--3設置簡單模式
USE [master]
GO
ALTER DATABASE [DBName.9197] SET RECOVERY SIMPLE WITH NO_WAIT
GO

 

(二) 下面的代碼實現了刪除備份文件,從下面的代碼中能夠看出刪除了14天以前的備份文件;

--刪除以前的備份文件
DECLARE @DeleteDate DATETIME
SET @DeleteDate = DATEADD(DAY, -14, GETDATE())

EXECUTE MASTER.SYS.XP_DELETE_FILE
0,
N'E:\DBBackup\DBName.9197\',
N'bak',
@DeleteDate

 

(三) 下面的代碼實現了批量建立文件夾,須要開啓xp_cmdshell開關,使用了遊標循環數據庫名進行建立文件夾;

--批量建立文件夾
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE

DECLARE @DBName VARCHAR(100)
DECLARE @SQL VARCHAR(1000)

DECLARE CurDBName CURSOR FOR
    SELECT name FROM sys.databases WHERE name LIKE '%Opinion%' AND STATE =0

OPEN CurDBName
    FETCH NEXT FROM CurDBName INTO @DBName
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        SET @SQL = 'mkdir E:\DBBackup\' + @DBName
        EXEC xp_cmdshell @SQL
        
        FETCH NEXT FROM CurDBName INTO @DBName
    END
CLOSE CurDBName
DEALLOCATE CurDBName

EXEC sp_configure 'show advanced options', 0
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 0
RECONFIGURE

 

(四) 下面的代碼實現了批量建立做業,這裏有一個建立做業的模板:JobTemplet.sql,我寫了一個Replaced.bat的批處理文件,這個批處理文件替換模板文件中數據庫的字符串,並生成建立新數據庫做業的SQL腳本,建立以後就會執行這個SQL腳原本建立做業;

1. 使用上面提供的SQL代碼,建立好做業的步驟和計劃,再使用SSMS生成建立做業的腳本,這個就是JobTemplet.sql;

USE [msdb]
GO
/****** 對象:  Job [Barefoot_Opinion_9565]    腳本日期: 01/06/2013 14:07:27 ******/
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
/****** 對象:  JobCategory [Database Maintenance]    腳本日期: 01/06/2013 14:07:27 ******/
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Database Maintenance' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

END

DECLARE @jobId BINARY(16)
EXEC @ReturnCode =  msdb.dbo.sp_add_job @job_name=N'DBName_9565', 
        @enabled=1, 
        @notify_level_eventlog=0, 
        @notify_level_email=0, 
        @notify_level_netsend=0, 
        @notify_level_page=0, 
        @delete_level=0, 
        @description=N'備份主分區', 
        @category_name=N'Database Maintenance', 
        @owner_login_name=N'oofraBnimdA_gz', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** 對象:  Step [Bakcup]    腳本日期: 01/06/2013 14:07:27 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Bakcup', 
        @step_id=1, 
        @cmdexec_success_code=0, 
        @on_success_action=3, 
        @on_success_step_id=0, 
        @on_fail_action=2, 
        @on_fail_step_id=0, 
        @retry_attempts=0, 
        @retry_interval=0, 
        @os_run_priority=0, @subsystem=N'TSQL', 
        @command=N'--1設置完整模式
USE [master]
GO
ALTER DATABASE [DBName.9565] SET RECOVERY FULL WITH NO_WAIT
GO

--2備份主分區(完整備份)
DECLARE @CurrentTime VARCHAR(50), @FileName VARCHAR(200)
SET @CurrentTime = REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120 ),''-'',''_''),'' '',''_''),'':'','''')
IF(DATEPART(DW, GETDATE()) = 2)--星期一
BEGIN
    SET @FileName = ''E:\DBBackup\DBName.9565\DBName.9565_Primary_Full_'' + @CurrentTime+''.bak''
    BACKUP DATABASE [DBName.9565]
    FILEGROUP=''PRIMARY'' TO DISK=@FileName WITH FORMAT
END
ELSE
BEGIN
    SET @FileName = ''E:\DBBackup\DBName.9565\DBName.9565_Primary_Diff_'' + @CurrentTime+''.bak''
    BACKUP DATABASE [DBName.9565]
    FILEGROUP=''PRIMARY'' TO DISK=@FileName WITH DIFFERENTIAL,FORMAT
END
GO

--3設置簡單模式
USE [master]
GO
ALTER DATABASE [DBName.9565] SET RECOVERY SIMPLE WITH NO_WAIT
GO
', 
        @database_name=N'DBName.9565', 
        @flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** 對象:  Step [Delete]    腳本日期: 01/06/2013 14:07:27 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Delete', 
        @step_id=2, 
        @cmdexec_success_code=0, 
        @on_success_action=1, 
        @on_success_step_id=0, 
        @on_fail_action=2, 
        @on_fail_step_id=0, 
        @retry_attempts=0, 
        @retry_interval=0, 
        @os_run_priority=0, @subsystem=N'TSQL', 
        @command=N'--刪除以前的備份文件
DECLARE @DeleteDate DATETIME
SET @DeleteDate = DATEADD(DAY, -14, GETDATE())

EXECUTE MASTER.SYS.XP_DELETE_FILE
0,
N''E:\DBBackup\DBName.9565\'',
N''bak'',
@DeleteDate', 
        @database_name=N'DBName.9565', 
        @flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Plan', 
        @enabled=1, 
        @freq_type=8, 
        @freq_interval=34, 
        @freq_subday_type=1, 
        @freq_subday_interval=0, 
        @freq_relative_interval=0, 
        @freq_recurrence_factor=1, 
        @active_start_date=20130105, 
        @active_end_date=99991231, 
        @active_start_time=10000, 
        @active_end_time=235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
    IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

 

2. 把下面的代碼保存爲Replaced.bat文件,這個批處理文件接受兩個參數,一個是@OldStr一個是@NewStr,即舊的數據庫名和新的數據庫名;

@if "%1"=="" goto error_parm
@if "%2"=="" goto error_parm
@echo off 
setlocal enabledelayedexpansion 
set file=E:\DBBackup\JobTemplet.sql
set "file=%file:"=%" 
for %%i in ("%file%") do set file=%%~fi 
echo. 
set replaced=%1
echo. 
set all=%2
for /f "delims=" %%i in ('type "%file%"') do ( 
    set str=%%i 
    set "str=!str:%replaced%=%all%!" 
    echo !str!>>"%file%"_%2.sql
)

 

3. 到這裏咱們已經有JobTemplet.sql和Replaced.bat文件了,咱們再建立一個Replaced_Test.bat批處理進行測試,這個批處理設置兩個變量,一個是@OldStr一個是@NewStr,再調用Replaced.bat來替換字符串;

@set temp1=9565
@set temp2=9001

call Replaced_2.bat %temp1%,%temp2%

經過上面3個步驟,建立了3個文件,以下圖所示:

wps_clip_image-10208

(Figure4:建立的模板和批處理文件)

 

4. 下面的SQL代碼就是使用遊標的形式,循環符合條件的數據庫,使用批處理替換模板文件,生成新的T-SQL文件,再執行新生成的T-SQL文件,這樣就建立了遊標當前數據庫的備份做業了:

-- =============================================
-- Author:      <聽風吹雨>
-- Blog:        <http://gaizai.cnblogs.com/>
-- Create date: <2013/12/03>
-- Description: <批量建立做業的T-SQL文件(替換字符串),並執行這個T-SQL文件>
-- =============================================
--開啓高級功能
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE

DECLARE @DBName VARCHAR(100)--數據庫名稱
DECLARE @CmdFile VARCHAR(1000)--建立File的Cmd命令字符串
DECLARE @CmdJob VARCHAR(1000)--執行Job的Cmd命令字符串
DECLARE @OldStr varchar(100)--須要替換的字符串
DECLARE @NewStr varchar(100)--替換成字符串
SET @OldStr = '9565'

--查找符合條件的數據庫
DECLARE CurDBName CURSOR FOR
    SELECT name FROM sys.databases WHERE name LIKE '%Opinion%' AND STATE =0

OPEN CurDBName
    FETCH NEXT FROM CurDBName INTO @DBName

WHILE @@FETCH_STATUS = 0
BEGIN
    --使用批處理替換模板文件,生成新的T-SQL文件
    SET @NewStr = SUBSTRING(@DBName,LEN('DBName.')+1,LEN(@DBName))
    SET @CmdFile = 'E:\DBBackup\Replaced.bat ' + @OldStr + ' ' + @NewStr
    PRINT @CmdFile
    EXEC xp_cmdshell @CmdFile
    
    --執行新生成的T-SQL文件
    SET @CmdJob = 'sqlcmd -i"E:\DBBackup\JobTemplet.sql _'+@NewStr+'.sql"'
    PRINT @CmdJob
    EXEC xp_cmdshell @CmdJob
    
    --循環下一個數據庫
    FETCH NEXT FROM CurDBName INTO @DBName
END

CLOSE CurDBName
DEALLOCATE CurDBName

--關閉高級功能
EXEC sp_configure 'xp_cmdshell', 0
RECONFIGURE
EXEC sp_configure 'show advanced options', 0
RECONFIGURE

上面的腳本中你須要注意兩點,以下圖所示:「bat」字符串後面是有一個空格的,若是沒有這個空格就會報錯的;另一點就是「sql」字符串與「_」之間也是有個空格的,這個空格是由於在使用Replaced.bat批處理建立文件的文件名的時候多一個空格(暫時還沒找出緣由),因此這裏的文件名稱須要加一個空格;

wps_clip_image-3441

(Figure5:SQL代碼

執行完上面的腳本以後,在E:\DBBackup的目錄下會生成下圖所示的SQL腳本文件:

wps_clip_image-6703

(Figure6:生成的SQL腳本文件)

(五) 到這裏,SSMS中已經建立了10個做業,就以下圖所顯示:

wps_clip_image-6930

(Figure7:建立的做業列表)

可是爲了能分散做業的執行時間,咱們有兩種方式作到這點:

1) 在JobTemplet.sql中有一個參數@active_start_time=10000,這個參數的意思是在深夜1點鐘的時候執行做業,因此能夠在Replaced.bat這個批處理再加一個參數,而且在JobTemplet.sql中替換成咱們想要的值,在外層T-SQL調用Replaced.bat的時候記錄這個值,並傳入到Replaced.bat中(這就留給讀者本身實現吧,我只實現了下面的第二種方法);

2) 修改已經建立做業的執行時間,下面的代碼實現了批量修改做業的執行時間,使用遊標的形式,循環調用msdb.dbo.sp_update_schedule修改做業的執行時間;

USE [msdb]
GO

-- =============================================
-- Author:      <聽風吹雨>
-- Blog:        <http://gaizai.cnblogs.com/>
-- Create date: <2013/12/03>
-- Description: <批量修改Job備份時間>
-- =============================================
DECLARE @DBName VARCHAR(100)
DECLARE @ScheduleId INT
DECLARE @Date DATETIME
DECLARE @Time VARCHAR(50)
DECLARE @SQL VARCHAR(1000)

SET @Date = '2013-01-08 00:20:00.000'
DECLARE CurDBName CURSOR FOR
    SELECT name,schedule_id FROM SYSJOBS AS J
    LEFT JOIN [SYSJOBSCHEDULES] AS S
    ON J.job_id= S.job_id
    WHERE NAME LIKE 'DBName_%' ORDER BY NAME

OPEN CurDBName
    FETCH NEXT FROM CurDBName INTO @DBName,@ScheduleId

WHILE @@FETCH_STATUS = 0
BEGIN
    --修改做業的執行時間
    SET @Time = REPLACE(CONVERT(VARCHAR, @Date, 8 ),':','')
    SET @SQL = 'EXEC msdb.dbo.sp_update_schedule 
        @schedule_id = ''' + CONVERT(VARCHAR(50),@ScheduleId) + ''',
        @active_start_time=' + @Time
    PRINT(@DBName +''+ @SQL)
    EXEC(@SQL)
    
    --遞增分鐘
    SET @Date = DATEADD(mi,20,@Date)
    
    --Get Next DataBase
    FETCH NEXT FROM CurDBName INTO @DBName,@ScheduleId
END

CLOSE CurDBName
DEALLOCATE CurDBName

 

(六) 下面的代碼實現了批量刪除做業,使用遊標的形式,循環調用sp_delete_job;

USE [msdb]
GO

-- =============================================
-- Author:      <聽風吹雨>
-- Blog:        <http://gaizai.cnblogs.com/>
-- Create date: <2013/12/03>
-- Description: <批量刪除Job>
-- =============================================
DECLARE @DBName VARCHAR(100)
DECLARE CurDBName CURSOR FOR
SELECT name FROM msdb.dbo.sysjobs

OPEN CurDBName
    FETCH NEXT FROM CurDBName INTO @DBName

WHILE @@FETCH_STATUS = 0
BEGIN
    --刪除Job
    exec sp_delete_job @job_name = @DBName

    --Get Next DataBase
    FETCH NEXT FROM CurDBName INTO @DBName
END

CLOSE CurDBName
DEALLOCATE CurDBName

 

(七) 查看做業的運行狀況;

--查詢做業的執行狀況
SELECT b.name,b.enabled,b.description,b.date_created,b.date_modified,
a.step_id,a.step_name,message,run_date,run_time,run_duration
FROM [msdb].dbo.[sysjobhistory] AS a
LEFT JOIN [msdb].[dbo].[sysjobs] AS b
ON a.job_id = b.job_id
ORDER BY name

wps_clip_image-17603

(Figure8:做業執行狀況)

--查詢做業與計劃的對應關係
SELECT J.name,schedule_id
 FROM [msdb].[dbo].[sysjobs] AS J
LEFT JOIN [msdb].[dbo].[sysjobschedules] AS S
on J.job_id= S.job_id
WHERE J.name LIKE '%' ORDER BY J.name

wps_clip_image-7570

(Figure9:做業與計劃的對應關係)

在表[msdb].[dbo].[sysschedules]中也一樣包含做業計劃信息;

wps_clip_image-2337

(Figure10:sysschedules信息)

五.主分區完整、差別還原(Primary Backup And Restore)

  既然作了上面主文件組的備份,固然咱們須要去測試這個主文件組的還原了,這樣才能夠當遇到問題能夠快速還原備份文件,達到還原數據的目的;

  接下來會在另一篇文章裏面專門講解;

六.參考文獻(References)

sp_update_schedule (Transact-SQL)

如何修改 SQL Server 代理主做業 (Transact-SQL)

bat實現文件字符串替換

Sqlcmd 使用

相關文章
相關標籤/搜索