استعادة قاعدة بيانات متعددة النسخ الاحتياطي في الصفقة

StackOverflow https://stackoverflow.com/questions/1029820

  •  06-07-2019
  •  | 
  •  

سؤال

كتبت إجراء مخزن الذي يعيد مجموعة من النسخ الاحتياطي لقاعدة البيانات.فإنه يأخذ اثنين المعلمات - مصدر الدليل و استعادة الدليل.يبدو الإجراء للجميع .باك الملفات في الدليل المصدر (بشكل متكرر) ويعيد كافة قواعد البيانات.

الإجراء المخزن يعمل كما هو متوقع ولكن كان لديه قضية واحدة - إذا كنت uncomment محاولة التقاط البيانات ، وإجراءات إنهاء مع رسالة الخطأ التالية:

error_number = 3013  
error_severity = 16  
error_state = 1  
error_message = DATABASE is terminating abnormally.

الجزء غريب في بعض الأحيان (ليس ثابت) استعادة يتم حتى لو كان خطأ.الإجراء:

create proc usp_restore_databases
(
    @source_directory varchar(1000),
    @restore_directory varchar(1000)
)
as
begin       

    declare @number_of_backup_files int

--  begin transaction
--  begin try

    -- step 0: Initial validation

        if(right(@source_directory, 1) <> '\') set @source_directory = @source_directory + '\'
        if(right(@restore_directory, 1) <> '\') set @restore_directory = @restore_directory + '\'

    -- step 1: Put all the backup files in the specified directory in a table -- 

        declare @backup_files table ( file_path varchar(1000))

        declare @dos_command varchar(1000)
        set @dos_command = 'dir ' + '"' + @source_directory + '*.bak" /s/b'

        /* DEBUG */ print @dos_command

        insert into @backup_files(file_path) exec xp_cmdshell  @dos_command

        delete from @backup_files where file_path IS NULL

        select @number_of_backup_files = count(1) from @backup_files

        /* DEBUG */ select * from @backup_files
        /* DEBUG */ print @number_of_backup_files

    -- step 2: restore each backup file --

        declare backup_file_cursor cursor for select file_path from @backup_files
        open  backup_file_cursor

        declare @index int; set @index = 0
        while(@index < @number_of_backup_files)
        begin


            declare @backup_file_path varchar(1000)
            fetch next from backup_file_cursor into @backup_file_path

            /* DEBUG */ print @backup_file_path

            -- step 2a: parse the full backup file name to get the DB file name.    
            declare @db_name varchar(100)

            set @db_name = right(@backup_file_path, charindex('\', reverse(@backup_file_path)) -1)  -- still has the .bak extension
            /* DEBUG */ print @db_name

            set @db_name = left(@db_name, charindex('.', @db_name) -1)          
            /* DEBUG */ print @db_name

            set @db_name = lower(@db_name)
            /* DEBUG */ print @db_name

            -- step 2b: find out the logical names of the mdf and ldf files
            declare @mdf_logical_name varchar(100),
                    @ldf_logical_name varchar(100)

            declare @backup_file_contents table 
            (
                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) NULL,
                ReadWriteLSN numeric(25,0) NULL,
                BackupSizeInBytes bigint,
                SourceBlockSize int,
                FileGroupID int,
                LogGroupGUID uniqueidentifier NULL,
                DifferentialBaseLSN numeric(25,0) NULL,
                DifferentialBaseGUID uniqueidentifier,
                IsReadOnly bit,
                IsPresent bit 
            )

            insert into @backup_file_contents 
            exec ('restore filelistonly from disk=' + '''' + @backup_file_path + '''')

            select @mdf_logical_name = LogicalName from @backup_file_contents where [Type] = 'D'
            select @ldf_logical_name = LogicalName from @backup_file_contents where [Type] = 'L'

            /* DEBUG */ print @mdf_logical_name + ', ' + @ldf_logical_name

            -- step 2c: restore

            declare @mdf_file_name varchar(1000),
                    @ldf_file_name varchar(1000)

            set @mdf_file_name = @restore_directory + @db_name + '.mdf'
            set @ldf_file_name = @restore_directory + @db_name + '.ldf'

            /* DEBUG */ print   'mdf_logical_name = ' + @mdf_logical_name + '|' +
                                'ldf_logical_name = ' + @ldf_logical_name + '|' +
                                'db_name = ' + @db_name + '|' +
                                'backup_file_path = ' + @backup_file_path + '|' +
                                'restore_directory = ' + @restore_directory + '|' +
                                'mdf_file_name = ' + @mdf_file_name + '|' +
                                'ldf_file_name = ' + @ldf_file_name


            restore database @db_name from disk = @backup_file_path 
            with
                move @mdf_logical_name to @mdf_file_name,
                move @ldf_logical_name to @ldf_file_name

            -- step 2d: iterate
            set @index = @index + 1

        end

        close backup_file_cursor
        deallocate backup_file_cursor

--  end try
--  begin catch
--        print error_message()
--      rollback transaction
--      return
--  end catch
--
--  commit transaction

end

هل لدى أي شخص أي أفكار لماذا هذا قد يكون يحدث ؟

سؤال آخر:هو رمز المعاملة مفيدة ؟ أي إذا كان هناك 2 قواعد البيانات المستعادة ، سوف SQL Server التراجع عن استعادة قاعدة بيانات واحدة إذا كان الثاني استعادة فشل ؟

هل كانت مفيدة؟

المحلول

وأساسا، هو ما كان يحدث أن واحدا من الملفات التي تحتاج إلى استعادتها لديه مشكلة، وعملية استعادة تم رمي خطأ ولكن الخطأ ليست خطيرة بما يكفي لإجهاض بروك. وهذا هو السبب في ذلك هو عدم وجود مشكلة دون محاولة اللحاق. ومع ذلك، مضيفا أن محاولة اللحاق الفخاخ أي خطأ مع الخطورة أكبر من 10، وبالتالي فإن مفاتيح التحكم في التدفق إلى كتلة الصيد الذي يعرض رسائل الخطأ وإحباط بروك.

نصائح أخرى

وأيضا، إذا كنت لا إزالة سجلات فارغة من قائمة الملف (منذ علق للخروج) ثم مع حلقة الخاص بك تبدأ من 0 ينتهي معالجة ملف غير existant للتكرار الأخير.

وبدلا من @index=0 بدلا يجب @index=1 ذلك

وأو غير تعليق خارج delete from @backup_files where file_path IS NULL

مشاكل لاحظت:

  • ارتكاب المعاملات يجب أن يكون داخل تبدأ محاولة....نهاية كتلة المحاولة
  • المؤشر لن تحصل مغلقة أو deallocated إذا كان خطأ واجه والسيطرة يذهب إلى BEGIN قبض...CATCH END

هذه محاولة تعديل التعليمات البرمجية.وسوف تثبت أن التعليمات البرمجية الخاصة بك تعمل بشكل جيد..

ALTER proc usp_restore_databases
(
    @source_directory varchar(1000),
    @restore_directory varchar(1000)
)
as
begin 
    declare @number_of_backup_files int

  begin transaction
  begin try
    print 'Entering TRY...'
--     step 0: Initial validation

        if(right(@source_directory, 1) <> '\') set @source_directory = @source_directory + '\'
        if(right(@restore_directory, 1) <> '\') set @restore_directory = @restore_directory + '\'

  --   step 1: Put all the backup files in the specified directory in a table -- 

        declare @backup_files table ( file_path varchar(1000))

        declare @dos_command varchar(1000)
        set @dos_command = 'dir ' + '"' + @source_directory + '*.bak" /s/b'

        /* DEBUG */ print @dos_command

        insert into @backup_files(file_path) exec xp_cmdshell  @dos_command

        --delete from @backup_files where file_path IS NULL

        select @number_of_backup_files = count(1) from @backup_files

        /* DEBUG */ select * from @backup_files
        /* DEBUG */ print @number_of_backup_files

    -- step 2: restore each backup file --

        declare backup_file_cursor cursor for select file_path from @backup_files
        open  backup_file_cursor

        declare @index int; set @index = 0
        while(@index < @number_of_backup_files)
        begin


                declare @backup_file_path varchar(1000)
                fetch next from backup_file_cursor into @backup_file_path

                /* DEBUG */ print @backup_file_path

      --           step 2a: parse the full backup file name to get the DB file name.    
                declare @db_name varchar(100)

                set @db_name = right(@backup_file_path, charindex('\', reverse(@backup_file_path)) -1)  -- still has the .bak extension
                /* DEBUG */ print @db_name

                set @db_name = left(@db_name, charindex('.', @db_name) -1)                      
                /* DEBUG */ print @db_name

                set @db_name = lower(@db_name)
                /* DEBUG */ print @db_name

        --         step 2b: find out the logical names of the mdf and ldf files
                declare @mdf_logical_name varchar(100),
                                @ldf_logical_name varchar(100)

                declare @backup_file_contents table     
                (
                        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) NULL,
                        ReadWriteLSN numeric(25,0) NULL,
                        BackupSizeInBytes bigint,
                        SourceBlockSize int,
                        FileGroupID int,
                        LogGroupGUID uniqueidentifier NULL,
                        DifferentialBaseLSN numeric(25,0) NULL,
                        DifferentialBaseGUID uniqueidentifier,
                        IsReadOnly bit,
                        IsPresent bit 
                )

                insert into @backup_file_contents 
                exec ('restore filelistonly from disk=' + '''' + @backup_file_path + '''')

                select @mdf_logical_name = LogicalName from @backup_file_contents where [Type] = 'D'
                select @ldf_logical_name = LogicalName from @backup_file_contents where [Type] = 'L'

                /* DEBUG */ print @mdf_logical_name + ', ' + @ldf_logical_name

          --       step 2c: restore

                declare @mdf_file_name varchar(1000),
                                @ldf_file_name varchar(1000)

                set @mdf_file_name = @restore_directory + @db_name + '.mdf'
                set @ldf_file_name = @restore_directory + @db_name + '.ldf'

                /* DEBUG */ print   'mdf_logical_name = ' + @mdf_logical_name + '|' +
                                                        'ldf_logical_name = ' + @ldf_logical_name + '|' +
                                                        'db_name = ' + @db_name + '|' +
                                                        'backup_file_path = ' + @backup_file_path + '|' +
                                                        'restore_directory = ' + @restore_directory + '|' +
                                                        'mdf_file_name = ' + @mdf_file_name + '|' +
                                                        'ldf_file_name = ' + @ldf_file_name
print @index

              --  restore database @db_name from disk = @backup_file_path 
              --  with
              --          move @mdf_logical_name to @mdf_file_name,
              --          move @ldf_logical_name to @ldf_file_name

            --     step 2d: iterate
                set @index = @index + 1

        end

        close backup_file_cursor
        deallocate backup_file_cursor

  end try
  begin catch
        print 'Entering Catch...'
        print error_message()
      rollback transaction
      return
  end catch

  commit transaction

end

راج

والمشكلة الفعلية هنا هي أن محاولة والصيد فقط تعطيك رسالة الخطأ الأخير 3013 "النسخ الاحتياطي إنهاء بشكل غير طبيعي"، ولكن لا تعطيك خطأ مستوى أدنى وذلك لسبب وتسبب في خطأ 3013.

إذا تنفيذ أمر احتياطية مثل مع و databasename غير صحيح، سوف تحصل على 2 الأخطاء. قاعدة بيانات النسخ الاحتياطي incorrect_database_name إلى القرص = بالسيارة: \ مسار \ filename.bak '

Msg 911, Level 16, State 11, Line 1
Could not locate entry in sysdatabases for database 'incorrect_database_name'. No entry found with that name. Make sure that the name is entered correctly.
Msg 3013, Level 16, State 1, Line 1
BACKUP DATABASE is terminating abnormally.

إذا كنت تريد أن تعرف الخطأ الفعلي لماذا النسخ الاحتياطي فشل في محاولة الصيد، الإجراء المخزن هو اخفاء ذلك.

والآن، على سؤالك .. ما أود القيام به هو عندما استعادة ينجح، وأود أن تحذف فورا أو نقل باك إلى موقع جديد، وبالتالي إزالته من الدليل الذي جاء في المعلمة الخاص بك. بناء على الفشل، ويمكن أن تحتوي على بيان اللحاق بك على GOTO أن تعيدك إلى ما قبل TRY BEGIN ويبدأ تنفيذ النقطة التي توقفت عندها لأنها لن متكرر كشف الملفات التي انتقلت من الدليل.

RUN_AGAIN:
BEGIN TRY
RECURSIVE DIR FOR FILENAMES
RESTORE DATABASE...
ON SUCCEED, DELETE .BAK FILE
END TRY
BEGIN CATCH
ON FAILURE, MOVE .BAK to A SAFE LOCATION FOR LATER ANALYSIS
GOTO RUN_AGAIN
END CATCH

وأنا لا أقول أنها جميلة، ولكنها ستعمل. لا يمكنك وضع إشارة GOTO داخل كتلة TRY / CATCH، لذلك يجب أن يكون خارجها.

وعلى أي حال، أنا بس أود أن أضيف أفكاري في هذا على الرغم من أن مسألة قديمة، فقط لمساعدة الآخرين في نفس الوضع.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top