sql >> Database teknologi >  >> RDS >> Sqlserver

Kører store forespørgsler i baggrunden MS SQL

Fra mit perspektiv har din server et alvorligt ydeevneproblem. Selv hvis vi antager, at ingen af ​​posterne i forespørgslen

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

var i hukommelsen, skulle det ikke tage 21 sekunder at læse de få sider sekventielt fra disken (dit clustered index på id_col bør ikke være fragmenteret, hvis det er en auto-identitet, og du ikke gjorde noget dumt som at tilføje en "desc" til indeksdefinitionen).

Men hvis du ikke kan/vil ordne det, vil mit råd være at lave opdateringen i små pakker som 100-1000 poster ad gangen (afhængig af hvor meget tid opslagsfunktionen bruger). En opdatering/transaktion bør ikke tage mere end 30 sekunder.

Du kan se, at hver opdatering har en eksklusiv lås på alle de poster, den har ændret, indtil transaktionen er fuldført. Hvis du ikke bruger en eksplicit transaktion, udføres hver erklæring i en enkelt, automatisk transaktionskontekst, så låsene frigives, når opdateringssætningen er færdig.

Men du kan stadig løbe ind i dødvande på den måde, alt efter hvad de andre processer gør. Hvis de også ændrer mere end én post ad gangen, eller selvom de samler og holder læselåse på flere rækker, kan du få deadlocks.

For at undgå deadlocks skal din opdateringserklæring låse alle de poster, den vil ændre på én gang. Måden at gøre dette på er at placere den enkelte opdateringssætning (med kun de få rækker begrænset af id_col) i en serialiserbar transaktion som

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

For hver opdatering vil dette tage en opdatering/eksklusiv nøgleområdelås på de givne poster (men kun dem, fordi du begrænser opdateringen gennem den klyngede indeksnøgle). Den vil vente på, at eventuelle andre opdateringer på de samme poster er færdige, og derefter låse den (forårsager blokering for alle andre transaktioner, men stadig kun for de givne poster), derefter opdatere posterne og frigive låsen.

Den sidste ekstra sætning er vigtig, fordi den vil tage en nøgleområdelås op til "uendeligt" og dermed forhindre lige indsættelser i slutningen af ​​intervallet, mens opdateringssætningen kører.




  1. MySQL Hosting på Azure, Fuldt administreret Cloud Database Service lanceres på ScaleGrid

  2. Svarer til unpivot() i PostgreSQL

  3. Spark SQL/Hive-forespørgsel tager evigheder med Join

  4. java.lang.ArrayIndexOutOfBoundsException:for jdbc-forbindelse