SQL 2016 Afirmación de SQL Server: Archivo: <pageref.cpp>, línea = 951 Afirmación fallida

8

Actualmente estoy actualizando nuestro almacén de datos de SQL 2012 a SQL 2016. Tengo mis DW antiguos y nuevos funcionando de forma paralela.

Mi proceso ETL (un marco desarrollado en SSIS por un tercero) se ejecutó con éxito durante más de 2 años en 2012, pero falla en 2016. Hasta ahora, las bases de datos y el proceso ETL son idénticos.

Ambos servidores son máquinas virtuales que se ejecutan en VMWare. Old Server es Win 2008 con 24 Gb de RAM. SQL 2012 Std. Memoria máxima establecida en 16 Gb. El nuevo servidor es Win 2012 con 64 Gb de RAM. SQL 2016 dev. Memoria máxima establecida en 50 Gb. El nuevo DW ejecuta v13.0.1601.5 RTM Developer Edition (64 bits).

Mientras ejecuto mi proceso ETL, los pasos de carga que usan una combinación SQL en una tabla de dimensiones o hechos fallan con el siguiente error.

Texto completo:

DESCRIPCIÓN: Afirmación de SQL Server: Archivo:, línea = 951 Afirmación fallida = 'IS_OFF (BUF_MINLOGGED, m_buf-> bstat) || pageModifyType! = PageModifyType_Contents || GetPagePtr () -> IsTextPage () '. Este error puede estar relacionado con el tiempo. Si el error persiste después de volver a ejecutar la instrucción, use DBCC CHECKDB para verificar la integridad estructural de la base de datos o reinicie el servidor para asegurarse de que las estructuras de datos en memoria no estén dañadas.

Según lo recomendado, ejecuté DBCC y no se encontraron errores. También he reiniciado SQL. Luego reinicié el proceso ETL y obtuve el mismo error.

Mis búsquedas de este error muestran que se conocía un error en SQL 2008, 2012 y 2014 y se corrigió en revisiones posteriores y actualizaciones acumulativas. así que estoy un poco sorprendido de ver que reaparece en 2016.

Los enlaces que he encontrado dicen que afecta a SSIS cuando intento hacer inserciones si la base de datos está en el modelo de recuperación Simple o Bulk Logged. (Estoy corriendo en el modelo de recuperación simple)

Una solución alternativa sugerida es cambiar el modelo de recuperación de Db a COMPLETO. He intentado esto y funciona, pero no es una gran solución para un Data Warehouse.

¿Alguien más ha encontrado esto con 2016?

¿Alguien puede sugerir soluciones alternativas?

Actualizaciones:

26/7/2016: Apliqué la actualización crítica KB3164398 (v13.0.1708.0) y el problema aún existe.

27/7/2016: He aplicado la actualización acumulativa CU1 KB3164674 (v13.0.2149.0).

8/03/2016: se produjo un error durante la noche en nuestro cubo más pequeño. CU1 no solucionó el problema. Hoy informé el error en MS Connect y también he registrado una llamada de soporte con Microsoft.

8/12/2016: MS-Support respondió inicialmente, pero las respuestas fueron "No tenemos una solución para eso". El tipo de Soporte iba a discutirlo con sus colegas y me respondería. 8 días después no he sabido nada de él.

Aunque no tengo una 'solución', encontramos una solución que nos convenía. Ver mi respuesta publicada.

29/9/2016. Apliqué CU2 la semana pasada. El jueves accidentalmente ejecutamos una versión anterior de la fusión que falló nuevamente con el mismo error. Entonces ... CU2 tampoco lo ha solucionado.

23/1/2017 : Apliqué 2016 SP1 CU1 y creo que esto ha resuelto el problema. Específicamente KB3205964

Sir jura mucho
fuente

Respuestas:

2

Mirando el KB tiene un par de opciones / soluciones:

  1. Cambie al modelo de recuperación COMPLETO. Usted dice "esta no es una gran opción para un almacén", pero en realidad es solo una cuestión de configurar las copias de seguridad del registro de transacciones de forma regular, por ejemplo, 15 minutos y luego deshacerse de ellas. SSIS / Planes de mantenimiento tienen tareas de stock para hacer esto . Perderá las transacciones de registro masivo, pero nunca he encontrado que estas hayan hecho una gran diferencia en los tiempos de ejecución, solo el tamaño del registro. Incluso puede hacer una copia de seguridad del registro en nul, que no describiré aquí. Si no está seguro de qué hacer, pregúntele a su DBA local. El espacio en disco y la retención de la copia de seguridad del registro de transacciones son problemas más fáciles de resolver que los errores fatales. Cuando este problema finalmente se resuelva, puede volver a cambiar.
  2. La KB menciona "múltiples instrucciones BULK INSERT en una transacción de distribución única". No está claro a partir de su pregunta cómo se configuran sus inserciones masivas. ¿Está utilizando SSIS para ejecutar tareas 'Ejecutar SQL' que utilizan elMERGE¿mando? ¿Qué significa 'múltiples INSERTOS A GRANEL' aquí? ¿Hay alguna forma de convertir su enfoque a INSERTOS BULK individuales, uno a la vez, por ejemplo? En SSIS puede establecer 'MaxConcurrentExecutables' en 1 temporalmente, por ejemplo, vea si eso ayuda. Conéctelo a una variable de configuración para que pueda volver a cambiarlo más tarde. Obviamente, ralentizará las cosas, pero prefiere que su ETL termine en lugar de fallar rápidamente. Hacer cosas en paralelo es un buen patrón y una verdadera fortaleza de SSIS, pero solo puede ir tan rápido como su componente más lento; digamos que tiene 10 dimensiones que toman un minuto y un hecho que toma una hora, su ETL termina en una hora en paralelo o 1 hora y 10 minutos en serie.
  3. MERGEes agradable pero tiene algunos problemas. Podría considerar volver a convertir a INSERT/ UPDATE. También debe usar HOLDLOCKcon MERGEsegún aquí . ¿Usas esa pista? ¿Hay alguna diferencia en este problema si lo hace? Tuvimos un problema en una versión anterior de SQL 2014 en la que el uso de MERGEDML OUTPUTcomposable ( cláusula) en el almacén de columnas causaba este tipo de afirmación: les hice quitar los índices del almacén de columnas de las dimensiones, que habían agregado sin avisarme.
  4. ¿Qué tipo de manejo de transacciones estás haciendo? A veces, con ETL, la posición es repetible simplemente volviendo a ejecutar. A veces lo necesitas para fallar y retroceder. ¿Cómo has implementado esto? ¿Se puede modificar para que no sea una "transacción distribuida única"?

Buena suerte.

wBob
fuente
Hola @wBob, gracias por tu respuesta. Para responder tu pregunta. 1. Estamos hablando de extraer tablas de 20-30 Gb en un solo paso. moviendo más de 100 Gb de datos. Me preocupa eliminar los registros de TX y también el almacenamiento. ¿Tal vez podría hacer que la copia de seguridad del registro sea un paso en el ETL después de cada extracto? 2. Sí, SSIS llama a una tarea SQL para realizar una fusión. Cada paso se ejecuta secuencialmente. En el momento en que se ejecuta la fusión, es la única tarea que se ejecuta. 3. Nuestra herramienta ETL es un marco proporcionado por un proveedor. Así es como funciona. No estoy seguro de pistas. comprobará. 4. Sin manipulación de TX. SQL recto
Sir Swears-a-lot
Entonces, si las ejecuciones exitosas no continúan, intente un t-log de 100GB con copias de seguridad de registros después de cada movimiento de la tabla, eso debería ordenarlo. Obviamente tienes que probar. ¿Alguna otra característica que deberíamos saber sobre el objetivo? Almacén de columnas, particionamiento, indexación excesiva?
wBob
Actualmente no hay características que aún no estén en nuestro DW existente en 2012 std. Los nuevos DW / Datamarts son copias exactas de los originales, sin almacenes de columnas ni particiones. Índices mínimos (pero efectivos). Actualmente nos estamos centrando en un movimiento de plataforma como por ejemplo. Finalmente, nuestro nuevo producto DW será 2016 Enterprise. Pero no agregaremos funciones de nivel Enterprise hasta que tengamos los conceptos básicos funcionando.
Sir Swears-a-lot
Hola @ wBob. Hemos implementado tu sugerencia separando la inserción de la fusión. Pero dada la forma en que hice la pregunta, no estoy seguro de qué califica como una respuesta correcta. pero creo que me has llevado a una solución válida y viable.
Sir Swears-a-lot
2

Creo que esto se resolvió en 2016 SP1 CU1.

Específicamente por KB3205964

Sir jura mucho
fuente
1

Esto se soluciona aplicando la actualización acumulativa 1 (CU1) para MSSQL2016; consulte el enlace https://support.microsoft.com/en-us/kb/3164674

Steve Neshausen
fuente
Steve estaba trabajando conmigo en esto. pensamos que lo teníamos resuelto pero sin cigarro. Así que he eliminado el crédito y he votado a favor.
Sir Swears-a-lot
1

Creo que hemos encontrado otra solución. Estoy publicando mi respuesta, ya que creo que puede ser útil, y es lo suficientemente diferente de la sugerencia de wBob.

Hemos cambiado la parte de inserción de la declaración de fusión para que se inserte en una tabla temporal en lugar del objetivo original.

Una vez que se ejecuta la declaración de fusión, luego insertamos desde la tabla # en el objetivo.

No es ideal, pero al menos la fusión aún maneja la complejidad de la 'inserción' al marcar las filas que han sido retiradas / caducadas.

Descubrimos que esta es una compensación aceptable en comparación con la reescritura completa de la fusión como insertos y actualizaciones por separado.

CREATE TABLE #Activity(
[ETL_ActivitySurrogateKey] [int] IDENTITY(1,1) NOT NULL,
[Field1] [varchar](255) NULL,
)

-- Insert statements for procedure here
INSERT INTO #Activity ( [Field1],2,3,etc )

SELECT [Field1],2,3,etc
FROM 
(
    MERGE [DDS_OZ_CC].[dimActivity] AS target 
    USING (
      SELECT [Field1],2,3,etc
      FROM [STAGE_OZ_CC].[Transform_Activity]
      ) as source
    ON
    (
      target.RowIsCurrent = source.RowIsCurrent
         AND target.[Field1] = source.[Field1]
    )
    WHEN MATCHED 
        AND (        
        EXISTS (SELECT target.Level5Id EXCEPT SELECT source.Level5Id)
    )
    THEN
      UPDATE SET 
        ETL_ValidToDateTime = source.ETL_ValidFromDateTime 
       ,ETL_RowIsCurrent = 0 
       ,ETL_LastTouchedBy = source.ETL_LastTouchedBy 
       ,ETL_RowChangeReason = 'SCD2 Retired' 

    WHEN NOT MATCHED THEN 
    INSERT 
    (    
     [Field1],2,3,etc
    )
    VALUES (      
      source.[Field1],2,3,etc
    )
       WHEN NOT MATCHED BY SOURCE AND target.ETL_RowIsCurrent = 1
       THEN UPDATE SET
       ETL_RowIsCurrent = 0 
       ,ETL_RowChangeReason = 'Fact Removed' 
       ,ETL_LastTouchedBy = 'Unknown'

  OUTPUT
    $action      
      ,source.[Field1],2,3,etc    

  ) AS MergeOutput
  (
    action  
    ,[Field1],2,3,etc   
  ) 

  WHERE ACTION = 'UPDATE' AND ETL_RowIsCurrent = 1

    INSERT INTO [DDS_OZ_CC].[dimActivity]
    ( [Field1],2,3,etc  )
    SELECT [Field1],2,3,etc
    FROM #Activity

    END
Sir jura mucho
fuente
Ok gracias Peter. Solo por interés, ¿probaste la sugerencia HOLDLOCK?
wBob
No, no lo hice. Después de leer el artículo de Aaron Bertrands al respecto, entendí que realmente se trataba de problemas de concurrencia con múltiples usuarios. En nuestro caso, el proceso ETL es lo único que se ejecuta.
Sir Swears-a-lot
Creo que valió la pena intentarlo solo para ver si tienes un comportamiento diferente, además es probablemente la mejor práctica, solo decir que no eres concurrente ahora no significa que no puedas serlo en el futuro. De todos modos, bien hecho para encontrar una solución y
pasar
Me hubiera gustado, pero lamentablemente estamos bajo limitaciones de tiempo. Una vez que encontramos una solución viable, el jefe hizo un llamado para seguir adelante.
Sir Swears-a-lot