sql >> Databáze >  >> RDS >> Sqlserver

Spouštění velkých dotazů na pozadí MS SQL

Z mého pohledu má váš server vážný problém s výkonem. I když předpokládáme, že žádný ze záznamů v dotazu

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

bylo v paměti, načtení několika stránek postupně z disku by nemělo trvat 21 sekund (váš seskupený index na id_col by neměl být fragmentován, pokud se jedná o automatickou identitu a neudělali jste něco hloupého, jako je přidání „desc“ k definici indexu).

Ale pokud to nemůžete/nechcete opravit, moje rada by byla provést aktualizaci v malých balíčcích, jako je 100-1000 záznamů najednou (v závislosti na tom, kolik času funkce vyhledávání spotřebuje). Jedna aktualizace/transakce by neměla trvat déle než 30 sekund.

Vidíte, že každá aktualizace uchovává výhradní zámek na všechny záznamy, které upravila, dokud není transakce dokončena. Pokud nepoužijete explicitní transakci, je každý příkaz proveden v jediném kontextu automatické transakce, takže zámky se uvolní po dokončení příkazu aktualizace.

Ale stále se můžete dostat do zablokování, v závislosti na tom, co dělají ostatní procesy. Pokud také upravují více než jeden záznam najednou, nebo i když shromažďují a drží zámky čtení na několika řádcích, můžete uváznout.

Abyste se vyhnuli zablokování, váš aktualizační příkaz musí uzamknout všechny záznamy, které změní, najednou. Způsob, jak toho dosáhnout, je umístit jediný příkaz aktualizace (s pouze několika řádky omezenými id_col) do serializovatelné transakce, jako je

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

Pro každou aktualizaci to bude vyžadovat aktualizační/exkluzivní zámek rozsahu klíčů na dané záznamy (ale pouze na nich, protože aktualizaci omezujete prostřednictvím clusterového indexového klíče). Počká na dokončení všech dalších aktualizací na stejných záznamech, poté se uzamkne (způsobí zablokování pro všechny ostatní transakce, ale stále pouze pro dané záznamy), poté aktualizuje záznamy a uvolní zámek.

Poslední dodatečný příkaz je důležitý, protože zamkne klíčový rozsah až do "nekonečna" a zabrání tak rovnoměrnému vkládání na konec rozsahu, když běží příkaz update.




  1. MySQL s C#, z pohledu vývojáře PHP

  2. Velikost schématu MySQL

  3. Jak vrátit prvky z pole JSON v MariaDB

  4. SQL dotaz, který ze sloupce čísel udělá řetězec