Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Uruchamianie dużych zapytań w tle MS SQL

Z mojej perspektywy twój serwer ma poważny problem z wydajnością. Nawet jeśli założymy, że żaden z rekordów w zapytaniu

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

był w pamięci, odczytanie kilku stron sekwencyjnie z dysku nie powinno zająć 21 sekund (twój klastrowany indeks w id_col nie powinien być pofragmentowany, jeśli jest to tożsamość automatyczna i nie zrobiłeś czegoś głupiego, takiego jak dodanie „opisu” do definicji indeksu).

Ale jeśli nie możesz / nie chcesz tego naprawić, radzę, aby aktualizować małe pakiety, takie jak 100-1000 rekordów na raz (w zależności od tego, ile czasu zajmuje funkcja wyszukiwania). Jedna aktualizacja/transakcja nie powinna zająć więcej niż 30 sekund.

Widzisz, że każda aktualizacja utrzymuje wyłączną blokadę na wszystkich zmodyfikowanych rekordach do czasu zakończenia transakcji. Jeśli nie używasz jawnej transakcji, każda instrukcja jest wykonywana w pojedynczym, automatycznym kontekście transakcji, więc blokady są zwalniane po zakończeniu instrukcji aktualizacji.

Ale w ten sposób nadal możesz napotkać impas, w zależności od tego, co robią inne procesy. Jeśli zmodyfikują jednocześnie więcej niż jeden rekord lub nawet jeśli gromadzą i utrzymują blokady odczytu w kilku wierszach, możesz uzyskać zakleszczenia.

Aby uniknąć zakleszczeń, instrukcja aktualizacji musi blokować jednocześnie wszystkie rekordy, które będzie modyfikować. Sposobem na to jest umieszczenie pojedynczej instrukcji aktualizacji (z tylko kilkoma wierszami ograniczonymi przez id_col) w transakcji możliwej do serializacji, takiej jak

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

Dla każdej aktualizacji będzie to wymagać aktualizacji/wyłącznej blokady zakresu kluczy dla danych rekordów (ale tylko dla nich, ponieważ ograniczasz aktualizację za pomocą klucza indeksu klastrowego). Poczeka na zakończenie wszelkich innych aktualizacji tych samych rekordów, a następnie zablokuje (powoduje blokowanie wszystkich innych transakcji, ale nadal tylko dla danych rekordów), a następnie zaktualizuje rekordy i zwolni blokadę.

Ostatnia dodatkowa instrukcja jest ważna, ponieważ zabierze blokadę zakresu kluczy do „nieskończoności”, a tym samym zapobiegnie nawet wstawianiu na końcu zakresu podczas działania instrukcji update.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server 2016:Utwórz relację

  2. Zmienianie kolumny tabeli w celu zaakceptowania większej liczby znaków

  3. Wykonywanie skryptu SQL Server z pliku wsadowego

  4. Jak zidentyfikować duplikację klucza podstawowego z kodu błędu programu SQL Server 2008?

  5. Konwertuj nazwę miesiąca na numer miesiąca w programie SQL Server (T-SQL)