¿Cuál es la forma más rápida de purgar datos?


18

Guión:

Tenemos dos tablas Tbl1y Tbl2en el servidor del suscriptor. Se Tbl1está replicando desde Publisher Server Ay tiene dos desencadenantes: insertar y actualizar. Los desencadenantes son insertar y actualizar los datos en Tbl2.

Ahora, tenemos que purgar (aproximadamente 900 millones de registros) de los Tbl2cuales tiene un total de más de 1000 millones de registros. A continuación se muestra la distribución de datos de un mes a un minuto.

  • Un mes - 14986826 filas
  • Un día - 483446 filas
  • Una hora - 20143 filas
  • Un minuto - 335 filas

Lo que estoy buscando

La forma más rápida de purgar esos datos sin ningún problema de producción, consistencia de datos y posiblemente sin tiempo de inactividad. Entonces, estoy pensando en seguir los pasos a continuación, pero atascado :(

Pasos:

  1. BCP Salida de los datos requeridos de la tabla existente Tbl2 (alrededor de 100 millones de registros, puede tomar aproximadamente 30 minutos).
    • Supongamos que comencé a hacer la actividad el 1Fab2018 10:00 PM, terminó a las 1Fab2018 10:30 PM. Para cuando se complete la actividad, la tabla Tbl2 obtendrá nuevos registros que se convertirán en delta
  2. Cree una nueva tabla en la base de datos con el nombre Tbl3
  3. BCP en los datos exportados en la tabla Tbl3 recién creada (alrededor de 100 millones de registros, puede tardar aproximadamente 30 minutos)
  4. Detener el trabajo de replicación
  5. Una vez que se completa BCP-in, use el script tsql para insertar los nuevos datos delta.

  6. El desafío es: ¿cómo lidiar con la declaración de "actualización" delta?

  7. Comience la replicación

Pregunta adicional:

¿Cuál es la mejor manera de lidiar con el escenario?

Respuestas:


26

Como está eliminando el 90% de las filas, le recomiendo que copie las filas que necesita mantener en una nueva tabla con la misma estructura, luego use ALTER TABLE ... SWITCHpara reemplazar la tabla existente con la nueva tabla, luego simplemente suelte la tabla anterior. Vea esta página de Microsoft Docs para la sintaxis.

Un banco de pruebas simple, sin replicación que muestra el principio general:

Primero, crearemos una base de datos para nuestra prueba:

USE master;
IF (SELECT 1 FROM sys.databases d WHERE d.name = 'SwitchTest') IS NOT NULL
BEGIN
    ALTER DATABASE SwitchTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE SwitchTest;
END
CREATE DATABASE SwitchTest;
ALTER DATABASE SwitchTest SET RECOVERY FULL;
BACKUP DATABASE SwitchTest TO DISK = 'NUL:';
GO

Aquí, creamos un par de tablas, con un disparador para mover filas de la tabla "A" a "B", aproximándose a su configuración.

USE SwitchTest;
GO
CREATE TABLE dbo.A
(
    i int NOT NULL 
        CONSTRAINT PK_A
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , d varchar(300) NOT NULL
    , rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);

CREATE TABLE dbo.B
(
    i int NOT NULL 
        CONSTRAINT PK_B
        PRIMARY KEY CLUSTERED
    , d varchar(300) NOT NULL
    , rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);

GO
CREATE TRIGGER t_a
ON dbo.A
AFTER INSERT, UPDATE
AS
BEGIN
    SET NOCOUNT ON;
    DELETE
    FROM dbo.B
    FROM dbo.B b
        INNER JOIN deleted d ON b.i = d.i
    INSERT INTO dbo.B (i, d, rowdate)
    SELECT i.i
        , i.d
        , i.rowdate
    FROM inserted i;
END
GO

Aquí, insertamos 1,000,000 filas en "A", y debido al disparador, esas filas también se insertarán en "B".

;WITH src AS (
    SELECT i.n
    FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))i(n)
)
INSERT INTO dbo.A (d, rowdate)
SELECT d = CRYPT_GEN_RANDOM(300), DATEADD(SECOND, s6.n + (s5.n * 100000) + (s4.n * 10000) + (s3.n * 1000) + (s2.n * 100) + (s1.n * 10), '2017-01-01T00:00:00.000')
FROM src s1
    CROSS JOIN src s2
    CROSS JOIN src s3
    CROSS JOIN src s4
    CROSS JOIN src s5
    CROSS JOIN src s6;

Borre el registro de transacciones para evitar quedarse sin espacio. NO EJECUTE esto en producción ya que envía los datos del registro de transacciones al dispositivo "NUL".

BACKUP LOG SwitchTest TO DISK = 'NUL:';
GO

Este código crea una transacción para garantizar que no se pueda escribir en ninguna de las tablas afectadas mientras migramos filas:

BEGIN TRANSACTION
EXEC sys.sp_getapplock @Resource = N'TableSwitcher', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = '1000', @DbPrincipal = N'dbo';
BEGIN TRY
    -- create a table to hold the rows we want to keep
    CREATE TABLE dbo.C
    (
        i int NOT NULL 
            CONSTRAINT PK_C
            PRIMARY KEY CLUSTERED
        , d varchar(300) NOT NULL
        , rowdate datetime NOT NULL
    ) ON [PRIMARY]
    WITH (DATA_COMPRESSION = PAGE);

    --copy the rows we want to keep into "C"
    INSERT INTO dbo.C (i, d, rowdate)
    SELECT b.i
        , b.d
        , b.rowdate
    FROM dbo.B
    WHERE b.rowdate >= '2017-01-11T10:00:00';

    --truncate the entire "B" table
    TRUNCATE TABLE dbo.B;

    --"switch" table "C" into "B"
    ALTER TABLE dbo.C SWITCH TO dbo.B;

    --drop table "C", since we no longer need it
    DROP TABLE dbo.C;

    --shows the count of rows in "B" which were retained.
    SELECT COUNT(1)
    FROM dbo.B
    WHERE b.rowdate >= '2017-01-11T10:00:00';

   --look for rows in "B" that should no longer exist.
    SELECT COUNT(1)
    FROM dbo.B
    WHERE b.rowdate < '2017-01-11T10:00:00';

    --release the applock and commit the transaction
    EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    DECLARE @message nvarchar(1000) = ERROR_MESSAGE();
    DECLARE @severity int = ERROR_SEVERITY();
    DECLARE @state int = ERROR_STATE();
    RAISERROR (@message, @severity, @state);
    EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
    ROLLBACK TRANSACTION;
END CATCH
GO

El sp_getapplocky sp_releaseapplockevitar múltiples instancias de este código que se ejecutan al mismo tiempo. Esto sería útil si habilita este código para ser reutilizado a través de una GUI.

(Tenga en cuenta que los bloqueos de aplicaciones solo son efectivos si todos los procesos que acceden al recurso implementan la misma lógica de bloqueo de recursos manual explícitamente; no hay magia que "bloquee" la tabla de la misma manera que SQL Server bloquea automáticamente filas, páginas, etc. durante un operación de inserción / actualización.)

Ahora, probamos el proceso de insertar filas en "A", para asegurarnos de que el disparador las inserte en "B".

INSERT INTO dbo.A (d, rowdate)
VALUES ('testRow', GETDATE());

SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
El | yo | d | rowdate |
+ --------- + --------- + ------------------------- +
El | 1000001 | testRow | 2018-04-13 03: 49: 53.343 |
+ --------- + --------- + ------------------------- +
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.