SQLServer大批量數據庫遷移方案


  在項目實施過程中,有時候會遇到大批量數據庫(上百個)同時遷移的問題,如果采用常規的備份還原的方式會消耗非常多的時間,對業務會造成非常大的影響,生產環境下業務很難接受這種方式,所以我們采取鏡像的方式來做遷移,即提前搭建鏡像,在遷移的時候進行故障轉移,然后斷開鏡像連接,由於數據庫太多,我們會通過腳本進行批量操作。

  首先需要建立所有數據庫的源服務器到目標數據庫的鏡像關系,由於數據庫太多,這里我們也使用腳本進行批量備份和還原:

  1. 批量備份指定的數據庫和日志(備份數據庫時間較長,故備份日志):

Declare @total int
select @total=count(name) from sys.databases where name in
  (
  'DB1','DB2','DB3','DB4','DB100'
  )
while @total<>0
  begin
  Declare @DBname varchar(1000)
  Declare @sql01 varchar(2000)
  Declare @sql02 varchar(2000)
  Declare @path varchar(1000)
  Declare @date varchar(50)
  select @DBname=a.name from
        (select name,row_number()over(order by name) sequence from sys.databases
          where name in
            (
            'DB1','DB2','DB3','DB4','DB100'
            ))a
  where @total=a.sequence;
set @date=replace(convert(varchar,getdate(),23),'-','')
set @path='Y:\Backup\'+@DBname+'_'+@date+'.bak';
print @path
set @sql01='BACKUP DATABASE '+quotename(@DBname,'[]')+' TO DISK='+quotename(@path,'''')+' WITH NOFORMAT,NOINIT,NAME='+quotename('Full             Database Backup','''')+',SKIP,NOREWIND,NOUNLOAD,STATS = 10'
print @sql01
exec (@sql01);
set @total=@total-1
end

  備份日志:

Declare @total int
select @total=count(name) from sys.databases where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path varchar(1000)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from sys.databases
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
 set @date=replace(convert(varchar,getdate(),23),'-','')
 set @path='Y:\Backup\'+@DBname+'_'+@date+'_Log.bak';
 print @path
set @sql01='BACKUP LOG '+quotename(@DBname,'[]')+' TO DISK='+quotename(@path,'''')+' WITH NOFORMAT,NOINIT,NAME='+quotename('Log Backup','''')+',SKIP,NOREWIND,NOUNLOAD,STATS = 10'
print @sql01
exec (@sql01);
set @total=@total-1
end

  Copy數據庫備份文件和日志備份文件到目標服務器

  2. 自動檢查邏輯名(用RESTORE FILELISTONLY命令從備份文件中讀取數據庫的信息,本例中有三個邏輯文件)后進行還原:

Create table #database
(name varchar(100))
insert into #database
values
('DB1')
,('DB2')
,('DB3')
,('DB4')
,('DB100')
--drop table #database
--select * from #database
Declare @total int
select @total=count(name) from #database where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path1 varchar(1000)
 Declare @path2 varchar(1000)
 Declare @path3 varchar(1000)
 Declare @path4 varchar(1000)
 Declare @Mdfname varchar(50)
 Declare @Ndfname varchar(50)
 Declare @Logname varchar(50)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from #database
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
 set @date=replace(convert(varchar,getdate(),23),'-','')
 set @path1='Y:\Backup\'+@DBname+'_'+@date+'.bak'
 set @path2='W:\SQLServer\Data\'+@DBname+'.mdf'
 set @path3='W:\SQLServer\Data\'+@DBname+'.ndf'
 set @path4='Y:\SQLServer\Log\'+@DBname+'.ldf'
create table #Logicalname
(LogicalName nvarchar(128)
,PhysicalName nvarchar(260)
,Type char(1)
,FileGroupName nvarchar(128)
,Size numeric(20,0)
,MaxSize numeric(20,0)
,FileID bigint
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0) NULL
,UniqueID uniqueidentifier
,ReadOnlyLSN numeric(25,0)
,ReadWriteLSN numeric(25,0)
,BackupSizeInBytes bigint
,SourceBlockSize int
,FileGroupID int
,LogGroupGUID uniqueidentifier
,DifferentialBaseLSN numeric(25,0)
,DifferentialBaseGUID uniqueidentifier
,IsReadOnly bit
,IsPresent bit
,TDEThumbprint varbinary(32)
)
Declare @restore varchar(1000)
set @restore='RESTORE FILELISTONLY from disk='+QUOTENAME(@path1,'''')
print @restore
exec (@restore)
insert into #Logicalname exec (@restore)
--select * from #Logicalname
 select @Mdfname=LogicalName from #Logicalname where Fileid=1
 select @Ndfname=LogicalName from #Logicalname where FileID=3
 select @Logname=LogicalName from #Logicalname where FileID=2
set @sql01='RESTORE DATABASE '+quotename(@DBname,'[]')+' from disk='+quotename(@path1,'''')+' WITH FILE = 1,MOVE '+quotename(@Mdfname,'''')+' to '+
   quotename(@path2,'''')+',move '+quotename(@Ndfname,'''')+' to '+quotename(@path3,'''')+
   ',move '+quotename(@Logname,'''')+' to '+quotename(@path4,'''')+',NORECOVERY,NOUNLOAD,STATS = 10'
print @sql01
exec (@sql01);
set @total=@total-1
drop table #Logicalname
end

  還原日志

Declare @total int
select @total=count(name) from sys.databases where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path varchar(1000)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from sys.databases
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
 set @date=replace(convert(varchar,getdate(),23),'-','')
 set @path='Y:\Backup\'+@DBname+'_'+@date+'_Log.bak';
 print @path
set @sql01='RESTORE LOG '+quotename(@DBname,'')+' from disk='+quotename(@path,'''')+' with FILE=1,NORECOVERY,NOUNLOAD,STATS = 10'
print @sql01
exec (@sql01);
set @total=@total-1
end 

  3. 創建鏡像

  在目標服務器上進行partner配置:

Declare @total int
select @total=count(name) from sys.databases where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path varchar(1000)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from sys.databases
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
set @sql01='ALTER DATABASE '+quotename(@DBname,'')+' set PARTNER='+quotename('TCP://SourceServer.domain:5022','''')
print @sql01
exec (@sql01);
set @total=@total-1
end
  在源服務器上配置鏡像,並將鏡像設置為高性能模式(以防影響生產環境業務性能):
Declare @total int
select @total=count(name) from sys.databases where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path varchar(1000)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from sys.databases
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
set @sql01='ALTER DATABASE '+quotename(@DBname,'')+' set PARTNER='+quotename('TCP://DestinationServer.Domain:5022','''');
set @sql02='ALTER DATABASE '+quotename(@DBname,'')+' SET PARTNER SAFETY OFF';
print @sql01
print @sql02
exec (@sql01);
exec (@sql02);
set @total=@total-1
end
  4. 以上都是遷移前的准備工作,其它准備工作如遷移賬號和權限,遷移Job這里也給出相應的方法:
  遷移所有賬號:請參考微軟官方文檔https://support.microsoft.com/zh-cn/help/918992/how-to-transfer-logins-and-passwords-between-instances-of-sql-server
  賬號權限遷移,在源服務器用以下腳本生成語句,copy所有語句到目的服務器執行:

select N'EXEC sp_addsrvrolemember  N''' +sp.name+ ''' ,N''' + rsp.name+''' '

FROM sys.server_principals sp    

LEFT JOIN sys.server_role_members srm ON sp.principal_id=srm.member_principal_id    

LEFT JOIN sys.server_principals rsp ON srm.role_principal_id=rsp.principal_id  

where rsp.name  is not null

  遷移Job:右鍵Job, 按’F7’, 多選了之后右鍵,點擊create to new window,然后copy整個窗口的內容到目的服務器執行

  5. 遷移時,先更改鏡像狀態為安全模式(只有在安全模式下,鏡像才能執行故障轉移):

Declare @UserDBname nvarchar(1000)
Declare @sql nvarchar(2000)
Declare @Total int
select @Total=count(*) from sys.databases where name not in               
(
N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
)
while @Total<>0
 begin
 select @UserDBname=a.name from (
         Select name, row_number() over(order by name)  Sequence from sys.databases
           where name not in
(
N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
)
) a
 where @Total=a.Sequence;
set @sql=
 'use master;'+
 'Alter database '+quotename(@UserDBname,'')+' set partner failover
 ';
print @sql;
exec (@sql);
set @Total=@Total-1
end
 
  查看數據庫鏡像同步狀態:
select DB_name(database_id),mirroring_state_desc,mirroring_role_desc,mirroring_safety_level_desc,mirroring_partner_name,mirroring_partner_instance from sys.database_mirroring
where mirroring_guid is not NULL
 
  6. 在遷移后的鏡像主節點(目的服務器)上執行命令刪除鏡像:
Declare @total int
select @total=count(name) from sys.databases where name in
(
'DB1','DB2','DB3','DB4','DB100'
)
while @total<>0
begin
 Declare @DBname varchar(1000)
 Declare @sql01 varchar(2000)
 Declare @sql02 varchar(2000)
 Declare @path varchar(1000)
 Declare @date varchar(50)
 select @DBname=a.name from
      (select name,row_number()over(order by name) sequence from sys.databases
      where name in
(
'DB1','DB2','DB3','DB4','DB100'
))a
      where @total=a.sequence;
set @sql01=
 'use master;'+
 'Alter database '+quotename(@DBname,'')+' set partner off
 ';
print @sql01
exec (@sql01);
set @total=@total-1
end
 
  至此,便完成了遷移的過程,在遷移完后,需要檢查數據庫的孤立賬號以及更新統計信息:
  7. 批量檢查孤立賬號:
Declare @UserDBname nvarchar(1000)
Declare @sql nvarchar(2000)
Declare @Total int
select @Total=count(*) from sys.databases where name not in               
(
N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
)
while @Total<>0
 begin
 select @UserDBname=a.name from (
         Select name, row_number() over(order by name)  Sequence from sys.databases
           where name not in
(
N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
)
) a
 where @Total=a.Sequence;
set @sql=
 'use '+quotename(@UserDBname,'')+'; select DB_Name() as DBName; '+
 'exec sp_change_users_login @Action='+quotename('Report','''')
 ;
print @sql;
exec (@sql);
set @Total=@Total-1
end
 
  批量刪除孤立賬號:
Declare @UserDBname nvarchar(1000)
Declare @sql nvarchar(2000)
Declare @Total int
--drop table #Orphan_User
Create table #Orphan_User
(
UserName varchar(100),
UserID varchar(500)
)
select @Total=count(*) from sys.databases where
--           name not in
--(
--N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
--N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
--N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
--)
--and
name in (N'ZRBT_01077')
while @Total<>0
 begin
 select @UserDBname=a.name from (
         Select name, row_number() over(order by name)  Sequence from sys.databases
           where
--           name not in
--(
--N'master', N'model', N'msdb', N'tempdb', N'distribution', N'DWDiagnostics',
--N'DWConfiguration', N'DWQueue', N'resource',N'ReportServer',N'ReportServerTempDB',
--N'ReportServer$KABA_MAINTempDB',N'ReportServer$KABA_MAIN'
--)
--and
name in (N'ZRBT_01077')
) a
 where @Total=a.Sequence;
set @sql=
 'use '+quotename(@UserDBname,'')+
 ';insert into #Orphan_User exec sp_change_users_login @Action='+quotename('Report','''')
 ;
print @sql;
exec (@sql);
select * from #Orphan_User
Declare @UserName nvarchar(1000);
Declare DropUser cursor
   for
select UserName from #Orphan_User;
open DropUser;
fetch next from DropUser into @UserName;
while @@FETCH_STATUS=0
begin
Declare @DropUsersql varchar(1000)
set @DropUsersql=
'use '+quotename(@UserDBname,'')+';
DROP SCHEMA'+quotename(@UserName,'[]')+';
DROP USER'+ quotename(@UserName,'[]');
print (@DropUsersql)
exec (@DropUsersql)
fetch next from DropUser into @UserName;
end
close DropUser;
deallocate DropUser;
Truncate table #Orphan_User
set @Total=@Total-1
end
 
  在這個過程中,需要和應用確認具體的需求,比如使用的賬號信息和Job的信息等,遷移完成后,需要應用更新連接信息,應用檢查完沒問題,遷移就算完成了。   
  這便是整個遷移的過程,在遇到的實際情況中,環境可能更加復雜,比如是群集之間的遷移,或者遷移后應用不能更改IP(可以通過修改服務器IP實現),遷移后鏡像是否保留等,都是可以以本文為藍本,做出相應的方案的。
  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM