sql >> Base de Datos >  >> RDS >> Sqlserver

Ejecutar consultas grandes en segundo plano MS SQL

Desde mi perspectiva, su servidor tiene un grave problema de rendimiento. Incluso si asumimos que ninguno de los registros en la consulta

select some_col with (nolock) where id_col between 57000000 and 57001000

estaba en la memoria, no debería tomar 21 segundos leer las pocas páginas secuencialmente desde el disco (su índice agrupado en id_col no debería estar fragmentado si es una identidad automática y no hizo algo estúpido como agregar un "desc" a la definición del índice).

Pero si no puede/no quiere arreglar eso, mi consejo sería hacer la actualización en paquetes pequeños como 100-1000 registros a la vez (dependiendo de cuánto tiempo consuma la función de búsqueda). Una actualización/transacción no debería tomar más de 30 segundos.

Verá que cada actualización mantiene un bloqueo exclusivo en todos los registros que modificó hasta que se complete la transacción. Si no usa una transacción explícita, cada declaración se ejecuta en un solo contexto de transacción automática, por lo que los bloqueos se liberan cuando se realiza la declaración de actualización.

Pero aún puede encontrarse con puntos muertos de esa manera, dependiendo de lo que hagan los otros procesos. Si también modifican más de un registro a la vez, o incluso si recopilan y retienen bloqueos de lectura en varias filas, puede generar interbloqueos.

Para evitar los interbloqueos, su declaración de actualización debe bloquear todos los registros que modificará a la vez. La forma de hacer esto es colocar la declaración de actualización única (con solo unas pocas filas limitadas por id_col) en una transacción serializable como

IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT

Para cada actualización, esto requerirá un bloqueo de rango de clave de actualización/exclusivo en los registros dados (pero solo en ellos, porque limita la actualización a través de la clave de índice agrupado). Esperará a que finalice cualquier otra actualización en los mismos registros, luego obtendrá su bloqueo (lo que provocará el bloqueo de todas las demás transacciones, pero solo para los registros dados), luego actualizará los registros y liberará el bloqueo.

La última declaración adicional es importante, porque tomará un bloqueo de rango de teclas hasta "infinito" y, por lo tanto, evitará incluso las inserciones al final del rango mientras se ejecuta la declaración de actualización.