¿Las actualizaciones de SQL Server en el lugar están tan mal aconsejadas como solían ser?

78

He estado trabajando con el servidor SQL de forma intermitente desde SQL Server 6.5, el viejo consejo que aún resuena en mi cabeza era nunca hacer una actualización en el lugar.

Actualmente estoy actualizando mis sistemas 2008 R2 DEV y TEST a SQL Server 2012 y necesito usar el mismo hardware. La idea de no tener que restaurar la configuración de mis servicios de informes es muy atractiva y estoy realmente en contra del tiempo. No hay servicios de análisis involucrados ni nada inusual o no estándar: solo se instalan el motor de base de datos y los servicios de informes.

¿Alguien ha tenido problemas serios con las actualizaciones en el lugar? ¿O debería reevaluar mi posición sobre las actualizaciones en el lugar?

Bienes dañados
fuente
Opté por hacer una actualización in situ en 1 servidor con la instalación de servicios de informes. El único problema que encontré fue intentar utilizar la herramienta de importación y exportación en SSMS con el cliente nativo 11. Intentar la transformación falló con un error sobre los tipos de datos no reconocidos. La solución que utilicé fue guardar el paquete y ejecutarlo en Herramientas de datos SQL (reemplazo de BIDS) que funcionó bien. Creo que esto tiene que ver con que el archivo de configuración para SSIS 2008 no se sobrescriba. Se me ocurrió más tarde, es posible que simplemente pueda volver a cambiar el cliente nativo a 10.
DamagedGoods

Respuestas:

92

Respuesta realmente corta : en el lugar está bien. Después puede revisar su configuración e implementar las mejores prácticas para SQL Server 2012.

Una respuesta más larga sobre actualizaciones / migraciones de SQL Server

Entonces, esto es una cuestión de opinión y no hay una respuesta necesariamente incorrecta o correcta, pero prefiero las actualizaciones del estilo de migración en lugar de las in situ por muchas razones. Dicho esto, algunos de mis clientes, por diversas razones, no han tenido más remedio que hacer una instalación in situ y, realmente, desde SQL Server 2005, las actualizaciones in situ no han sido tan malas como solían ser.

Por qué prefiero una migración a una actualización in situ

  • Retroceso más fácil : si algo sale mal, puede retroceder simplemente diciendo "abortamos la actualización ... Cambie las cadenas de conexión al servidor anterior mientras resolvemos esto". Con un in situ lo está arreglando o está caído.
  • Actualizar hardware : el hardware cambia rápidamente. Puede atascarse fácilmente en el hardware adecuado para su empresa hace 4 años, pero no para hoy y los próximos cuatro años con una actualización en el lugar. Es probable que tenga que hacer una migración en algún momento de todos modos para el nuevo hardware.
  • Siéntete mejor - Claro ... Este es subjetivo, pero se siente bien saber que estás comenzando con una nueva instalación del sistema operativo, una nueva instalación de SQL sin telarañas de la persona en el trabajo antes que tú (o antes de que supieras lo que sabías) hoy) que posiblemente podría causarle dolores de cabeza en el futuro.
  • Nuevo sistema operativo : una migración le brinda la oportunidad de comenzar con una nueva versión del sistema operativo si no está en la última y mejor versión de hoy.
  • Puedes probarlo : ¿alguna vez quisiste obtener un conjunto de líneas de base en una nueva máquina antes de instalar SQL y nublarlo con bases de datos y uso? Puedes hacer eso ahora.
  • A veces es más fácil colarse en las mejores prácticas : tal vez la cuenta de servicio de SQL Server era un administrador local. Quizás Builtin Administrators está en el rol de servidor SA. Tal vez las cosas se han pirateado juntas para que funcione antes. Puede arreglar todo eso y comenzar de nuevo.
  • Entorno de prueba gratuito y sueño adicional : es un gran beneficio tener un entorno en el que pueda trabajar antes del día de corte real cuando haga que este nuevo entorno viva. Hacer una migración a un nuevo entorno significa que puede construirlo durante el horario comercial, mucho antes de su día de corte real y probarlo de muchas maneras con anticipación. Puede ejecutar pruebas de regresión completas en todas las aplicaciones y sistemas durante días y tener una gran tranquilidad antes de realizar el conjunto final de restauraciones / adjuntos y traspasar todas las aplicaciones y acceder al nuevo entorno.
  • No tiene que hacerlo todo de una vez : una situación muy común con la que me encuentro es un entorno que está tratando de consolidarse en unos pocos casos. Quizás uno por versión, quizás uno por "nivel" y versión. Muchos de estos proyectos tienen plazos diferentes para diversas aplicaciones y bases de datos basadas en pruebas, planes de proyectos y plazos de certificación de proveedores. Hacer la migración significa que puede mover esas bases de datos que están listas, cuando estén listas y aún manejar las solicitudes de esas bases de datos que no pueden moverse por una razón u otra.

Eso sí, no digo que tengas que hacer esto como migración. In-Place funciona y funciona bien si no planea comprar hardware nuevo en su presupuesto y no puede hacerlo para esta actualización. El soporte en el proceso de actualización es mucho mejor de lo que era en los 6.5 días, por lo que no se está poniendo en una mala posición al hacer esto.

Si planea hacer in situ para desarrollo / prueba pero desea realizar una migración para producción, puede considerar realizar al menos una migración antes de la producción. De esta manera, puede elaborar su lista de verificación con anticipación y abordar cualquier problema potencial en el que no estaba pensando.

Adjuntar / Separar vs. Copia de seguridad / Restaurar para migraciones

Si decide seguir con el enfoque de migración, todavía hay una decisión más sobre la que aún puede tener un debate y así es como mueve su base de datos al nuevo entorno. Puede separar su base de datos del servidor anterior y adjuntarla al nuevo o hacer una copia de seguridad y restaurarla allí.

Prefiero copia de seguridad / restaurar. La mayor ventaja que escucho sobre separar / adjuntar es que ahorra algo de tiempo. Para mí, la copia de seguridad / restauración gana por varias razones:

  • Mantenga el antiguo accesible : esto le permite tener una base de datos accesible en el servidor de origen. detach / attach debería hacer lo mismo, pero requerirá algunos pasos y hay espacio para errores humanos con detach / attach que podrían complicar esto.
  • Está garantizando que tiene una copia de seguridad : en lugar de simplemente tomar una base de datos de una separación y potencialmente olvidar un paso de copia de seguridad, se ha asegurado de haber realizado esa copia de seguridad.
  • Error humano : si elimina el archivo incorrecto, olvida dónde está enviando algo o arruina sus pasos, arriesga mucho moviendo los datos y los archivos de registro para su base de datos. Ahora puede mitigar esto copiando en lugar de cortar (y si lo separa, debería salir del hábito de cortar y pegar) pero aún podría arruinarlo. SQL Server ya no bloquea esos archivos y es demasiado fácil eliminar un archivo accidentalmente para que me arriesgue.
  • Realmente no es tan lento : tomar una copia de seguridad y copiarla es un poco más de tiempo, pero no es tanto lo que estoy dispuesto a pagar el riesgo adicional. De hecho, al usar el modelo de recuperación completa y las copias de seguridad de registros, puede reducir el tiempo de inactividad aún más para los cortes como se describe a continuación en "Cómo hacer que el enfoque de migración sea más rápido"

Si decide hacer la copia de seguridad / restauración, eso significa que su base de datos fuente anterior seguirá estando en línea. Me gusta poner esa base de datos fuera de línea después de tomar la copia de seguridad. A veces voy un paso más allá y desconecto toda la instancia de SQL después de haber escrito la seguridad, los trabajos, el servidor vinculado, los certificados, la configuración del correo de la base de datos y otra información de toda la instancia. Esto evita un problema durante las pruebas en el que alguien dice "¡Todo se ve genial!" solo para darse cuenta uno o dos días después de que han estado hablando con la base de datos anterior en el servidor anterior. Si desconecta esas bases de datos o desconecta toda la instancia, puede evitar esos falsos positivos y el desorden que generan.

Cómo hacer que el enfoque de migración sea más rápido

Puede minimizar el tiempo de inactividad requerido para la transición de un entorno antiguo a uno nuevo para un entorno de producción ocupado con poco tiempo de inactividad utilizando el modelo de recuperación completa. Básicamente, organice el entorno al que está migrando restaurando la última copia de seguridad completa, cualquier copia de seguridad diferencial y cualquier copia de seguridad de registro ya especificada NORECOVERY, todo lo que tendrá que hacer para el corte final es restaurar las copias de seguridad de registro que aún no se restauraron y la copia de seguridad del registro final que desea restaurar especificando WITH RECOVERY. De esta manera, para una base de datos grande, la ventana de tiempo de inactividad de corte real puede minimizarse drásticamente pagando el costo de las restauraciones completas, diferenciales y de la mayoría de los registros antes de la ventana de tiempo de inactividad. ¡Gracias a Tao por señalar esto en los comentarios!

Cómo hacer que la actualización en el lugar sea más segura

Algunas cosas que puede hacer para mejorar su experiencia y resultados al elegir el enfoque en el lugar.

  • Copia de seguridad : realice copias de seguridad apropiadas de todas las bases de datos de usuarios y sistemas de su entorno con anticipación y asegúrese de que sean buenas (soy paranoico ... De hecho, las restauraría en algún lugar primero para saber realmente que son buenas ... Puede estar malgastando su tiempo. Pero puede agradecerle en caso de un desastre). Escriba cualquier información de configuración sobre la instalación de SQL y OS en ese entorno.
  • Pruebe bien las cosas antes de comenzar : compruebe que tiene un buen entorno y buenas bases de datos. Debería hacer cosas como mirar los registros de errores y ejecutar DBCC CHECKDB de forma regular, pero antes de realizar una actualización en el lugar es un buen momento para comenzar. Solucione cualquier problema con anticipación.
  • Asegure la salud del sistema operativo : no solo asegúrese de que SQL esté en buen estado, asegúrese de que su servidor esté en buen estado. ¿Algún error retorcido en los registros de eventos de error de su sistema o aplicación? ¿Cómo es tu espacio libre?
  • Prepárese para lo peor : hace un tiempo tuve una serie de publicaciones de blog que partía de la premisa de que si no se está preparando para el fracaso, en realidad se está preparando para fallar ... Todavía lo creo. Por lo tanto, piense detenidamente en los problemas que pueda tener y lidie con ellos en consecuencia. Póngase en la mentalidad de "fracaso" y pensará en cosas que de otro modo no tendría.

La importancia de las listas de verificación de actualización o migración

Si decide hacer una actualización (ya sea en lugar o migración), debería considerar seriamente crear una lista de verificación y usar esta lista de verificación en cada entorno. Debe incluir un montón de cosas en esta lista de verificación, entre las cuales se incluyen:

  1. Al inicio : haga algunas cosas, como realizar una actualización de prueba, pruebe sus aplicaciones en el último nivel de Compatibilidad de la base de datos y considere ejecutar una herramienta como el Asesor de actualización de SQL Server con anticipación para ver qué tipo de tareas debe completar antes de realizar el SQL Actualización o migración del servidor.
  2. Pasos previos : limpieza, tareas del sistema operativo, parches antes de tiempo, preparación de aplicaciones para la actualización (cortes limpios, trabajo de cadena de conexión), copias de seguridad , etc.
  3. Pasos de actualización / migración : todo lo que tiene que hacer para que la actualización o migración tenga éxito y en el orden correcto. Instalación, cambio (o no cambio dependiendo de su prueba y enfoque) cambios de modo de compatibilidad a bases de datos, etc.
  4. Pasos posteriores a la migración / actualización : varias pruebas, publicación de una nueva versión o nuevas opciones de configuración del servidor, implementación de mejores prácticas, cambios de seguridad, etc.
  5. Pasos de reversión : durante todo el proceso, debe tener pasos de reversión e hitos. Si llegas tan lejos y esto sucede, ¿qué harás? ¿Cuáles son los criterios de "hacer una reversión completa"? ¿Y cómo hace esa reversión?

Y luego haga que la persona que realizará la actualización de producción siga la lista de verificación en algún entorno que no sea la producción, especialmente uno que cierre, si es posible, se asemeja a la producción ("South of prod", como digo ...) y tenga en cuenta cualquier problema o punto donde tuvieron que desviarse de la lista de verificación o improvisar debido a una falta en la lista de verificación. Luego, combine los cambios y diviértase con su cambio de producción.

No puedo enfatizar demasiado la importancia de realizar pruebas exhaustivas después de la migración o actualización y antes de su migración lo suficiente. Tomar una decisión de reversión en medio de una actualización debería ser fácil, especialmente durante una migración. Si hay algo incómodo, retroceda y averigüe si no puede solucionarlo de manera efectiva y confiable en el fragor de la migración. Una vez que esté vivo en este nuevo entorno y los usuarios se conecten, la reversión se convierte en una tarea difícil. No puede restaurar una base de datos de SQL Server a una versión anterior. Eso significa trabajo manual y migraciones de datos. Siempre espero un par de semanas para eliminar el entorno anterior, pero debes hacer todo lo posible para evitar necesitar ese entorno anterior encontrando todos tus problemas antes de que tus usuarios activos toquen el nuevo entorno. Preferiblemente antes de comenzar la actualización / migración.

Nota rápida sobre la migración / actualización de SQL Server Reporting Services La migración de una instalación de SSRS no es una tarea hercúlea que muchos piensan que es. Este artículo en línea de technet / books es bastante útil . Una de las advertencias más importantes en ese artículo es "Hacer una copia de seguridad de las claves de cifrado", especialmente si tiene una gran cantidad de información confidencial guardada, como direcciones de correo electrónico de destinatario de correo electrónico de informes programados, información de conexión para una multitud de conexiones, etc. puedo preguntarle a uno de mis clientes desde hace un tiempo qué tan importante es eso. Lo saben porque me equivoqué con ese paso y pasé mucho tiempo modificando los cronogramas de informes y los permisos de la cadena de conexión.

Mike Walsh
fuente
14

En mi experiencia, el mismo proceso de toma de decisiones debe hacerse como antes. AFAIK no ha habido ningún 'cambio mundial' con la instalación de SQL Server, dentro del producto MS SQL Server en sí mismo, y los posibles problemas que tiene al implementar software con millones de líneas de código. Algo malo podría pasar y ahora estás atrapado sin la opción 'ROLLBACK'.

Sin embargo, tiene otras alternativas en su lugar. Podría considerar hacer una instantánea del sistema, restaurar en otro lugar, realizar la actualización y ver qué sucede. Esta prueba debería brindarle mucha comodidad, pero no garantiza absolutamente que no surjan problemas en la caja de productos. Sin embargo, esta es una opción que no estaba disponible en SQL 6.5 días.

Simplemente asumiría el peor de los casos. Haces una actualización en el lugar y falla miserablemente. Luego debe recuperarse de esto dentro de su RTO y RCO. ¿Entiende el negocio los riesgos y tiene planes para mitigarlo?

Si el negocio no está bien con esto, entonces no lo hagas, sería mi consejo.

Ali Razeghi
fuente
2

Si tiene sus servidores ejecutándose en un entorno virtual, puede realizar una instantánea en un clon y luego aplicar la actualización en el lugar y probar la instancia para verificar que la actualización fue exitosa. Si funciona, puede aplicar la instantánea y hacer que el clon sea el servidor de producción. Si sale mal, puede eliminar la instantánea y volver a la imagen previa a la actualización para volver a intentarlo, o eliminar el clon y realizar una migración completa.

Troya
fuente
1
Solo si el almacenamiento también está virtualizado y forma parte de la instantánea. Si el almacenamiento está conectado directamente a la VM, no se 'revertirá' cuando se restablezca la instantánea ...
Remus Rusanu
1

Debido a una gran inversión en hardware, se nos exigió actualizar solo el sistema operativo manteniendo la versión actual de SQL Server (2012, 3 servidores, 22 instancias, ~ 300 bases de datos). Sin configuraciones complejas como la duplicación, etc.

Este ejemplo no coincide exactamente con la pregunta ya que SQL Server no se está actualizando. Creo que esta sigue siendo una buena respuesta porque los pasos mostrados en realidad serían más simples que una verdadera migración in situ.

Descripción general: se conectó una unidad externa para realizar copias de seguridad completas principalmente como medida de precaución. Solo el modelo y msdb se restaurarán realmente desde la unidad externa. Los ldf / mdf se dejaron en su lugar para separar / adjuntar. Algunas cuentas locales fueron referenciadas dentro de los DB. Después de que se volvieron a crear en el sistema operativo, las referencias dentro de la base de datos se volvieron a crear (ya que los SID pueden cambiar).

Luego, estos fueron los pasos que nos funcionaron:

1) Tome nota de la configuración del nivel del servidor que se restaurará en los pasos 12 (Roles del servidor) y 18 a 23.

2) Parche SQL Server 2012 a SP3 (se requiere coherencia si queremos restaurar cualquier sistema dbs).

3) Verifique que las versiones coincidan en cada instancia. "Seleccione @@ versión"

4) Genere estos 6 scripts ejecutando este script. Redgate SQL Multiscript es un gran ahorro de tiempo si hay muchas instancias (Ajuste las Herramientas -> Opciones => Longitud de línea al máximo (8192) y luego use la Salida de texto).

  • Apoyo
  • Restaurar
  • Despegar
  • Adjuntar
  • Recrear inicios de sesión
  • Vuelva a vincular usuarios a inicios de sesión

    -- (1) BACKUP / (2) RESTORE
    --    
    --*** SET THESE to external drive location
    --*** and create the Destination Directories
    declare 
        @backupInstanceDir  varchar(300) = 'F:\ExternalDriveBackups\' + replace(@@servername, '\', '_'),
        @dateSuffix         varchar(100) = '2015-12-14'; 
    
    if (object_id('tempdb..DatabaseStatus') is not null)
    drop table #DAtabseSTatus;
    
    select 
        d.name DbName, 
        d.state_desc DbState,
        d.user_access_desc UserMode,
        convert(bit, (d.is_read_only * -1 + 1)) as IsWritable,
        d.is_trustworthy_on as IsTrustWorthy,
        d.is_in_standby IsInStandby,
        d.recovery_model_desc RecoveryModel,
        suser_sname(d.owner_sid) as Owner,
        convert(bit, 
            case when d.database_id <= 4 or d.is_distributor = 1
                then 1
                else 0
            end) as IsSystemDb,
        mf.type_desc as FileType,
        mf.name FileName,
        mf.state FileState,
        mf.state_desc FileStatDesc,
        mf.physical_name PhysicalName,
        mf.type as FileTypeId    
    into #DatabaseStatus
    from
        sys.master_files AS mf
    join sys.databases AS d
    ON  mf.database_id = d.database_id
    where
        1=1
    order by
        d.name,
        mf.physical_name;
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutBU
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutRE
    
    create table #sqlOutBU
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    create table #sqlOutRE
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutBU select char(10) + '-- BACKUP SCRIPT' + char(10);
    insert into #sqlOutRE select char(10) + '-- RESTORE SCRIPT' + char(10);
    
    
    insert into #sqlOutBU select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    insert into #sqlOutRE select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';        
    
    PRINT '--Script for Backing up all DBs in a SQL Server Instance to a specific location' 
    
    SET nocount ON 
    
    insert into #sqlOutBU select char(10) + 
    '--' + char(10) + '-- BACKUP ' + @@servername + '--' + char(10) + 
    'use [Master]; set deadlock_priority high;' + char(10);
    
    insert into #sqlOutRE select '
    -- RESTORE
    --
    -- BE SURE TO BACKUP SYSTEM DBS TO AN ALTERNATE LOCATION JUST BEFORE RESTORING!
    --
    use [Master]; set deadlock_priority high;' + char(10);
    
    DECLARE @dbname nvarchar(128) 
    declare dblist_cursor cursor fast_forward for 
    select [name] from master.sys.databases where [name] != 'tempdb'
    order by iif(database_id <= 4, '0', '1') + [name]
    
    open dblist_cursor 
    fetch next from dblist_cursor into @dbname 
    
    while @@fetch_status = 0 
    begin 
    
        declare @bak nvarchar(300) = @backupInstanceDir + '\' + @dbname + '_' + @dateSuffix + '.bak';
    
        insert into #sqlOutBU select char(10) + 'backup database [' + @dbname + '] to disk = ''' + @bak + ''' WITH COPY_ONLY, NOFORMAT, NOINIT, ' + char(10) + 
            'NAME = N''' + @dbName + '-Full'', SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 25;';
    
        insert into #sqlOutRE select 'restore database [' + @dbName + '] from disk = ''' + @bak + ''' WITH FILE = 1,' + char(10) +
        (
            select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
            where FileType = 'Rows' and DbName = @dbName
        ) + ',' + char(10) +
        (
            select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
            where FileType = 'Log' and DbName = @dbName
        ) + ',' + char(10) +
        '    NOUNLOAD, REPLACE, STATS = 25;' + char(10);               
    
        fetch next from dblist_cursor into @dbname 
    end 
    
    close dblist_cursor 
    deallocate dblist_cursor 
    
    insert into #sqlOutBU select char(10) + 'go' + char(10);
    insert into #sqlOutRE select char(10) + 'go' + char(10);
    
    select Command from #sqlOutBU order by Row; -- BACKUP SCRIPT
    select Command from #sqlOutRE order by Row; -- RESTORE SCRIPT
    
    go
    
    
    
    --
    -- (3) DETACH  -  Org Author: Artemakis Artemiou
    --      
    
    if object_id('tempdb..#sqlOutDT') is not null
        drop table #sqlOutDT
    
    create table #sqlOutDT
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutDT select char(10) + '-- DETACH all DBs from a SQL Server Instance' + char(10);      
    
    insert into #sqlOutDT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    SET nocount ON 
    
    insert into #sqlOutDT select char(10) + '--' + char(10) + '-- DETACH ' + @@servername + char(10) + '--' + char(10) + '
    use MAster; set deadlock_priority high;' + char(10) + char(10);
    
    DECLARE @dbname nvarchar(128) 
    DECLARE dblist_cursor CURSOR fast_forward FOR 
    SELECT [name] 
    FROM   master.sys.databases 
    WHERE  database_id > 4 
    
    OPEN dblist_cursor 
    FETCH next FROM dblist_cursor INTO @dbname 
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
        insert into #sqlOutDT select
        'alter database ' + @dbname + ' set single_user with rollback immediate;' + char(10) +
        'EXEC sp_detach_db ''' + @dbname + ''', ''true'';' + char(10);
        FETCH next FROM dblist_cursor INTO @dbname 
    END 
    
    CLOSE dblist_cursor 
    DEALLOCATE dblist_cursor 
    
    insert into #sqlOutDT select char(10) + 'go' + char(10);
    select Command from #sqlOutDT order by Row;
    
    go
    
    
    
    --
    -- (4) ATTACH  -  Org Author: Artemakis Artemiou
    --    
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutAT
    
    create table #sqlOutAT
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutAT select char(10) + '-- ATTACH ALL DBs to a SQL Server Instance' + char(10);
    
    insert into #sqlOutAT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    SET NOCOUNT ON
    
    insert into #sqlOutAT select char(10) + '--' + char(10) + '-- ATTACH ' + @@servername + char(10) + '--' + char(10) + 
    'use MAster;' + char(10) + char(10);
    
    DECLARE @dbname nvarchar(128);
    
    DECLARE DBList_cursor CURSOR fast_forward FOR 
    select [name] from master.sys.databases where database_id > 4
    order by name;
    
    OPEN DBList_cursor
    
    FETCH NEXT FROM DBList_cursor 
    INTO @dbname
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
    declare @attach_TSQL_script varchar(max)
    set @attach_TSQL_script=''
    set @attach_TSQL_script=@attach_TSQL_script+'CREATE DATABASE ' + @dbname +' ON ' 
    
    declare @tsql varchar(max),@filename varchar(max)
    set @tsql='DECLARE DBFiles_cursor CURSOR FOR select [filename] from '+ @dbname + '.sys.sysfiles'
    
    execute (@tsql) 
    
    PRINT '--'+@dbname 
    
    OPEN DBFiles_cursor
    FETCH NEXT FROM DBFiles_cursor INTO @filename
    
    WHILE @@FETCH_STATUS = 0
    BEGIN   
    set @attach_TSQL_script=@attach_TSQL_script+ char(10)+'    (FILENAME = '''+ @filename +'''),' 
    FETCH NEXT FROM DBFiles_cursor INTO @filename
    END
    
    set @attach_TSQL_script=SUBSTRING(@attach_TSQL_script,0,len(@attach_TSQL_script))
    set @attach_TSQL_script=@attach_TSQL_script+ char(10) +'    FOR ATTACH;';
    
    insert into #sqlOutAT select @attach_TSQL_script + char(10);
    
    PRINT @attach_TSQL_script 
    PRINT ''
    
    CLOSE DBFiles_cursor
    DEALLOCATE DBFiles_cursor
    
    FETCH NEXT FROM DBList_cursor 
    INTO @dbname
    
    END 
    
    CLOSE DBList_cursor
    DEALLOCATE DBList_cursor
    
    insert into #sqlOutAT select char(10) + 'go' + char(10);
    select Command from #sqlOutAT order by Row;
    go
    
    
    
    --
    -- (5) GENERATE A 'RE-CREATE LOGINS' SCRIPT
    --
    -- This script was modified from a version that was designed to copy from one server to another:
    --      http://stackoverflow.com/a/5983773/538763
    --
    
    
    USE [master]
    
    if object_id('tempdb..#sqlOut') is not null
    drop table #sqlOut;
    
    create table #sqlOut
    (
    Command nvarchar(max) not null,
    Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOut select char(10) + '-- RECREATE LOGINS' + char(10);
    
    
    insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    insert into #sqlOut select 'use Master;' + char(10);
    go
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    declare @Debug bit = 0;
    declare @PartnerServer varchar(100) = @@SERVICENAME;  -- use current server before it is shutdown (disabled below)
    
    declare
        @MaxID int,
        @CurrID int,
        @SQL nvarchar(max),
        @LoginName sysname,
        @IsDisabled int,
        @Type char(1),
        @SID varbinary(85),
        @SIDString nvarchar(100),
        @PasswordHash varbinary(256),
        @PasswordHashString nvarchar(300),
        @RoleName sysname,
        @Machine sysname,
        @PermState nvarchar(60),
        @PermName sysname,
        @Class tinyint,
        @MajorID int,
        @ErrNumber int,
        @ErrSeverity int,
        @ErrState int,
        @ErrProcedure sysname,
        @ErrLine int,
        @ErrMsg nvarchar(2048);
    
    declare @Logins Table (LoginID int identity(1, 1) not null primary key,
                        [Name] sysname not null,
                        [SID] varbinary(85) not null,
                        IsDisabled int not null,
                        [Type] char(1) not null,
                        PasswordHash varbinary(256) null)
    declare @Roles Table (RoleID int identity(1, 1) not null primary key,
                    RoleName sysname not null,
                    LoginName sysname not null)
    declare @Perms Table (PermID int identity(1, 1) not null primary key,
                    LoginName sysname not null,
                    PermState nvarchar(60) not null,
                    PermName sysname not null,
                    Class tinyint not null,
                    ClassDesc nvarchar(60) not null,
                    MajorID int not null,
                    SubLoginName sysname null,
                    SubEndPointName sysname null)
    
    Set NoCount On;
    
    If CharIndex('\', @PartnerServer) > 0
    Begin
    Set @Machine = LEFT(@PartnerServer, CharIndex('\', @PartnerServer) - 1);
    End
    Else
    Begin
    Set @Machine = @PartnerServer;
    End
    
    -- Get all Windows logins from principal server
    Set @SQL = 'Select P.name, P.sid, P.is_disabled, P.type, L.password_hash' + CHAR(10) +
        'From ' /*+ QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals P' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.sql_logins L On L.principal_id = P.principal_id' + CHAR(10) +
        'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And P.name <> ''sa''' + CHAR(10) +
        'And P.name Not Like ''##%''' + CHAR(10) +
        'and P.Name Not like ''NT SERVICE%''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', P.name) = 0;';
    
    Insert Into @Logins (Name, SID, IsDisabled, Type, PasswordHash)
    Exec sp_executesql @SQL;
    
    -- Get all roles from principal server
    Set @SQL = 'Select RoleP.name, LoginP.name' + CHAR(10) +
        'From '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_role_members RM' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + .*/ +'master.sys.server_principals RoleP' +
        CHAR(10) + char(9) + 'On RoleP.principal_id = RM.role_principal_id' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals LoginP' +
        CHAR(10) + char(9) + 'On LoginP.principal_id = RM.member_principal_id' + CHAR(10) +
        'Where LoginP.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And LoginP.name <> ''sa''' + CHAR(10) +
        'And LoginP.name Not Like ''##%''' + CHAR(10) +
        'And LoginP.name Not Like ''NT SERVICE%''' + CHAR(10) +
        'And RoleP.type = ''R''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', LoginP.name) = 0;';
    
    Insert Into @Roles (RoleName, LoginName)
    Exec sp_executesql @SQL;
    
    -- Get all explicitly granted permissions
    Set @SQL = 'Select P.name Collate database_default,' + CHAR(10) +
        '   SP.state_desc, SP.permission_name, SP.class, SP.class_desc, SP.major_id,' + CHAR(10) +
        '   SubP.name Collate database_default,' + CHAR(10) +
        '   SubEP.name Collate database_default' + CHAR(10) +
        'From '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals P' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_permissions SP' + CHAR(10) +
        CHAR(9) + 'On SP.grantee_principal_id = P.principal_id' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals SubP' + CHAR(10) +
        CHAR(9) + 'On SubP.principal_id = SP.major_id And SP.class = 101' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.endpoints SubEP' + CHAR(10) +
        CHAR(9) + 'On SubEP.endpoint_id = SP.major_id And SP.class = 105' + CHAR(10) +
        'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And P.name <> ''sa''' + CHAR(10) +
        'And P.name Not Like ''##%''' + CHAR(10) +
        'And P.name Not Like ''NT SERVICE%''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', P.name) = 0;'
    
    Insert Into @Perms (LoginName, PermState, PermName, Class, ClassDesc, MajorID, SubLoginName, SubEndPointName)
    Exec sp_executesql @SQL;
    
    --select * from @Logins;
    --select * from @Roles;
    --select * from @perms;
    
    
    Select @MaxID = Max(LoginID), @CurrID = 1
    From @Logins;
    
    While @CurrID <= @MaxID
    Begin
    Select @LoginName = Name,
        @IsDisabled = IsDisabled,
        @Type = [Type],
        @SID = [SID],
        @PasswordHash = PasswordHash
    From @Logins
    Where LoginID = @CurrID;
    
    --    If Not Exists (Select 1 From sys.server_principals
    --              Where name = @LoginName)
    Begin
    
        set @sql = char(10);
        set @sql += 'If Not Exists (Select 1 From sys.server_principals Where name = ''' + @LoginName + ''')' + char(10);
        set @sql += 'begin' + char(10) + '    ';
    
        Set @SQL += 'Create Login ' + quotename(@LoginName)
        If @Type In ('U', 'G')
        Begin
            Set @SQL = @SQL + ' From Windows;'
        End
        Else
        Begin
            Set @PasswordHashString = '0x' +
                Cast('' As XML).value('xs:hexBinary(sql:variable("@PasswordHash"))', 'nvarchar(300)');
    
            Set @SQL = @SQL + ' With Password = ' + @PasswordHashString + ' HASHED;  --, ';
    
            Set @SIDString = '0x' +
                Cast('' As XML).value('xs:hexBinary(sql:variable("@SID"))', 'nvarchar(100)');
            Set @SQL = @SQL + 'SID = ' + @SIDString + ';' + char(10);
        End
    
        set @sql += char(10) +
            '    print ''Created Login ' + @loginName  + ''';' + char(10) +
            'end' + char(10) +
            'else' + char(10) +
            convert(nvarchar(max), '    print ''Login ' + @loginName + ' already existed. '';') + char(10);
    
        If @Debug = 0
        insert into #sqlOut select @SQL;                      
        Else
        Print @SQL;
    
        If @IsDisabled = 1
        Begin
            Set @SQL = 'Alter Login ' + quotename(@LoginName) + ' Disable;'
            If @Debug = 0
                insert into #sqlOut select @SQL;                              
            Else              
                Print @SQL;              
        End
        End
    Set @CurrID = @CurrID + 1;
    End
    
    
    insert into #sqlOut select char(10) + 'use Master;' + char(10);
    
    Select @MaxID = Max(RoleID), @CurrID = 1
    From @Roles;
    
    While @CurrID <= @MaxID
    Begin
    Select @LoginName = LoginName,
        @RoleName = RoleName
    From @Roles
    Where RoleID = @CurrID;
    
    /*  If Not Exists (Select 1 From sys.server_role_members RM
                Inner Join sys.server_principals RoleP
                    On RoleP.principal_id = RM.role_principal_id
                Inner Join sys.server_principals LoginP
                    On LoginP.principal_id = RM.member_principal_id
                Where LoginP.type In ('U', 'G', 'S')
                And RoleP.type = 'R'
                And RoleP.name = @RoleName
                And LoginP.name = @LoginName)*/
    Begin
        If @Debug = 0
        Begin          
            insert into #sqlOut select 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''', @loginame = ''' + @LoginName + ''';';
        End
        Else
        Begin
            Print 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''',';
            Print '     @loginame = ''' + @LoginName + ''';';
        End
    End
    
    Set @CurrID = @CurrID + 1;
    End
    
    
    insert into #sqlOut select char(10) + 'use Master;' + char(10);
    
    
    Select @MaxID = Max(PermID), @CurrID = 1
    From @Perms;
    
    While @CurrID <= @MaxID
    Begin
    Select @PermState = PermState,
        @PermName = PermName,
        @Class = Class,
        @LoginName = LoginName,
        @MajorID = MajorID,
        @SQL = PermState + space(1) + PermName + SPACE(1) +
            Case Class When 101 Then 'On Login::' + QUOTENAME(SubLoginName)
                    When 105 Then 'On ' + ClassDesc + '::' + QUOTENAME(SubEndPointName)
                    Else '' End +
            ' To ' + QUOTENAME(LoginName) + ';'
    From @Perms
    Where PermID = @CurrID;
    
    /*If Not Exists (Select 1 From sys.server_principals P
                Inner Join sys.server_permissions SP On SP.grantee_principal_id = P.principal_id
                Where SP.state_desc = @PermState
                And SP.permission_name = @PermName
                And SP.class = @Class
                And P.name = @LoginName
                And SP.major_id = @MajorID)*/
    Begin
        If @Debug = 0
                insert into #sqlOut select @sql;                      
        Else          
            Print @SQL;          
    End
    
    Set @CurrID = @CurrID + 1;
    End
    
    
    select Command from #sqlOut as SqlOut order by Row;
    go
    
    
    --
    -- (6) Generate a script to Re-link all users to logins based on current state (before shutdown)
    --
    
    use Master;
    
    if object_id('tempdb..#sqlOut') is not null
    drop table #sqlOut;
    
    create table #sqlOut
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOut select char(10) + '-- RELINK USERS TO LOGINS' + char(10);
    
    insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    declare @dbCmd varchar(8000) = '
    use ?;
    
    insert into #sqlOut select char(10) + ''use ?;'' + char(10);  
    
    with links as
    (
    select u.name as UserName,
        l.loginname as LoginName
        from sysusers u 
        join master..syslogins l
        on u.sid = l.sid        
    where u.name != ''dbo''
        and u.isSqlUser = 1 or l.isNtName = 1 or l.isNtGroup = 1
    )
    insert into #sqlOut 
    select ''alter user ['' + UserName + ''] with name = ['' + UserName + ''], login = ['' + LoginName + '']''
    from links
    ';    
    
    exec sp_MSforeachdb @dbCmd;
    
    select Command from #sqlOut order by Row;
    
    go

5) Ejecute el script para hacer una copia de seguridad de todos los DB, incluido el sistema (maestro, msdb, modelo) en la unidad externa.

6) Ejecutar script para separar todos los DB

7) C Drive se volverá a formatear. Preserve los LDF / MDF si NO estaban en C.

8) Windows Server 2012 está instalado en C

9) Mueva el LDF / MDF para los archivos del sistema original fuera del camino si no estaban en la Unidad C.

10) SQL Server 2012 se reinstalará y aplicará un parche al SP3 a. Vuelva a crear cuentas de usuario / grupo del sistema

11) Respalde las bases de datos del sistema en una NUEVA ubicación o nombre de archivo (¡tenga cuidado de no sobrescribir los originales!).

12) Ejecute el fragmento de roles de recreación. Algo como:

USE [master]
CREATE SERVER ROLE [SomeServerRole]
--ALTER SERVER ROLE [dbcreator] ADD MEMBER [SomeServerRole]
--ALTER SERVER ROLE [bulkadmin] ADD MEMBER [SomeServerRole]
-- ALTER SERVER ROLE [SomeServerRole] ADD MEMBER [SomeMemberOrRole]

13) Ejecute el script de inicio de sesión de recreate (no hace nada si se restauraron los inicios de sesión)

14) Detener el AGENTE SQL.

(Podría restaurar al Maestro aquí, nos acobardamos).

15) Adjunte mdf / ldf usando el script de arriba. a. Si falla, restaure manualmente desde bak usando el script de arriba.

16) Intento de restauración del modelo

17) Asegúrese de que el Agente SQL esté detenido. Restaurar MSDB (enlace) a. Si falla, debe volver a crear trabajos + plan de mantenimiento + configuración de correo + operadores

18) Abrir script de usuario para iniciar sesión ...

    a. If there are master users (rare?) then First Re-Create users for master since it was not restored:
        use master;       
        CREATE USER [ABC] FOR LOGIN [machine\ABC]

    b. Run the rest of the script

19) Habilite el intermediario de servicios para que coincida con el nombre SELECT del valor original, is_broker_enabled FROM sys.databases;

    alter database MSDB set single_user with rollback immediate;
    ALTER DATABASE [MSDB] SET ENABLE_BROKER;
    alter database MSDB set multi_user;

20) Inicie el Agente SQL

21) Establecer el umbral de paralelismo al valor original

22) Ajuste la configuración de la base de datos a sus valores originales:

 declare @dbCmd varchar(8000) = '
      use ?;
      if db_name() not in (''master'', ''model'', ''tempdb'', ''msdb'')
      begin
             print ''Adjusting [?]...'';    
            alter database [?] set single_user with rollback immediate;
             aLTER AUTHORIZATION ON DATABASE::[?] to [sa];
            -- alter database [?] set trustworthy on;
            ALTER DATABASE [?] SET AUTO_CLOSE OFF WITH NO_WAIT;     
            alter database [?] set multi_user;
      end     
      else
             print ''Skipping [?]...'';
    ';    

    exec sp_MSforeachdb @dbCmd;

23) Verifique la propiedad del trabajo:

select s.name as JobName, l.name as login, SUSER_SNAME(s.owner_sid) AS login2
from  msdb..sysjobs s 
left join master.sys.syslogins l on s.owner_sid = l.sid

Si la versión de SQL Server también se hubiera actualizado, no creo que el modelo y las bases de datos msdb pudieran haberse restaurado, por lo que los trabajos se habrían perdido debido a https://support.microsoft.com/en-us/kb/264474

Lo que falta:

  • Usuarios originales en la base de datos maestra (¿raro?)
  • Roles del servidor
  • ?
crokusek
fuente
0

No hay nada malo con ninguno de los enfoques per se: he hecho ambos y los dos resultados son generalmente buenos.

Si hay un problema con el enfoque de migración, no es técnico: es pereza. Demasiado a menudo me parece que la razón por la que una empresa aún no ha optado por la versión xxxx es porque eligieron una migración oscilante y nunca llegaron a hacer el trabajo duro para moverse por completo. Ahora tienen dos o más conjuntos de servidores en lugar de uno.

RowlandG
fuente