Avatar billede hojgaard Nybegynder
17. marts 2005 - 10:12 Der er 17 kommentarer og
2 løsninger

Lås rækker med SELECT

Hej eksperter :)

Jeg er rendt ind i et sql problem. Jeg ønsker i en transaktion at starte med at låse alle rækker med samme deviceid ud fra et SELECT statement. Grunden til jeg ønsker at låse alle rækker på denne måde er at flere tråde vil tilgå databasen for at tjekke efter nye rækker.

Her er et eksempel, hvor jeg viser hvad jeg ønsker at gøre.


BEGIN TRANSACTION

-- Markere og låse alle rækker med samme deviceid.
-- Låsen skal kunne holdes så længe transaktionen stadig er i live.
SELECT id, deviceid FROM Table1 WITH (HOLDLOCK, ROWLOCK) WHERE deviceid IN (SELECT TOP 1 deviceid FROM Table1 WITH (READPAST)) ORDER BY id

-- Herefter ønsker jeg evt. at slette nogle af rækkerne - dette er dog ikke problemet :)

COMMIT TRANSACTION


Jeg er godt klar over at sql-statementet ikke er helt gyldigt, men håber det giver en ide om hvad jeg gerne vil lave :)
Avatar billede arne_v Ekspert
17. marts 2005 - 10:22 #1
Hvilket transaction isolation level bruger du ?
Avatar billede arne_v Ekspert
17. marts 2005 - 10:27 #2
Hvis det var serializable så behøvede du jo ingen WITH (HOLDLOCK, ROWLOCK)
Avatar billede hojgaard Nybegynder
17. marts 2005 - 10:28 #3
Jeg har endnu ikke lagt mig fast på isolation level, men har kig på READ COMMITTED - (er vist også default).
Avatar billede arne_v Ekspert
17. marts 2005 - 10:29 #4
Men iøvrigt er jeg lidt forvirret over at du bruger WITH (HOLDLOCK, ROWLOCK)
og WITH (READPAST) sammen - den første er serializable - den anden virker
kun ved readcommitted ??
Avatar billede hojgaard Nybegynder
17. marts 2005 - 10:36 #5
Det var for at vise hvad jeg ønsker at gøre.
Jeg benytter READPAST for at være sikker på at låste rækker ikke bliver læst.
HOLDLOCK, ROWLOCK var for at vise at de rækker der bliver selected og skal låses.

Giver det nogen mening.

Vil følgende kunne løse problemet?
Jeg er usikker på om den rent faktisk låser rækkerne.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
SELECT id, deviceid FROM Table1 WHERE deviceid IN (SELECT TOP 1 deviceid FROM Table1) ORDER BY id

-- Evt. slette her

COMMIT TRANSACTION
Avatar billede arne_v Ekspert
17. marts 2005 - 10:39 #6
Den sidste vil ikke have nogen samtidigheds problemer.

Performance vil måske lide lidt da kun en tråd af gangen vil udføre det SQL
(de andre tråde vil venteindtil en tråd er færdig).
Avatar billede hojgaard Nybegynder
17. marts 2005 - 10:40 #7
hmm... det skulle gerne være således at flere tråde/connections kunne arbejde på tabellen samtidigt. Så er jeg vel tilbage til READ COMMITED.
Avatar billede arne_v Ekspert
17. marts 2005 - 10:41 #8
Jeg kender ikke nok til WITH (HOLDLOCK, ROWLOCK) semantik til at kunne sige
om det virker. Umiddelbart ville jeg tro at det også ville serialisere
uanset brugen af READPAST. Men jeg er ikke sikker.
Avatar billede arne_v Ekspert
17. marts 2005 - 10:44 #9
Du kan sagtens have flere tråde & connections med serializable. Men databasen
garanterer at resulatet bliver ligesom hvis SQL koden mellem BEGIN og COMMIt
kun udføres af en tråd/connection ad gangen.
Avatar billede arne_v Ekspert
17. marts 2005 - 10:47 #10
HOLDLOCK
Equivalent to SERIALIZABLE. (See SERIALIZABLE for more information.)

SERIALIZABLE
Equivalent to HOLDLOCK. Makes shared locks more restrictive by holding them until the completion of a transaction (instead of releasing the shared lock as soon as the required table or data page is no longer needed, whether or not the transaction has been completed). The scan is performed with the same semantics as a transaction running at the SERIALIZABLE isolation level.

Den siger "the scan" ikke "selected rows".
Avatar billede hojgaard Nybegynder
17. marts 2005 - 10:57 #11
Så SERIALIZABLE låser mere end bare selected rækker?
Hvis ja, er det ikke den optimale løsning. Så må jeg tilbage til READ COMMITTED:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRANSACTION
SELECT id, deviceid FROM Table1 WITH (ROWLOCK) WHERE deviceid IN (SELECT TOP 1 deviceid FROM Table1 WITH (READPAST)) ORDER BY id
COMMIT TRANSACTION
Avatar billede janus_007 Nybegynder
17. marts 2005 - 17:38 #12
Må jeg spørge om hvorfor du ønsker at låse dine rækker? Er det bare pga. andre ikke må læse dem samtidigt eller fordi du ønsker at udføre en update på dem?
Avatar billede hojgaard Nybegynder
17. marts 2005 - 17:45 #13
Det er fordi jeg vil forhindre flere connections i at kunne læse samme rækker. Hver connection må kun læse de rækker, som indeholde samme deviceid.

Grunden er jeg skal have flere windows services til at holde øje med databasen. Så snart der bliver synkroniseret nye poster ind i tabellen, skal servicen submitte dem videre til en web service. Hvis web servicen accepterer dem, skal windows servicen slette dem fra tabellen.

Den største grund til flere connections ikke må læse de samme poster er fordi det er vigtigt at poster bliver videresendt i en bestemt rækkefølge.

Puha... håber det er nogenlunde til at forstå ;)
Avatar billede janus_007 Nybegynder
17. marts 2005 - 19:35 #14
Aha... jamen så er det bare at gøre sådan her, nu har jeg bare brugt Northwind

use Northwind
begin tran

select * from orders with (UPDLOCK ROWLOCK)
where orderid between 10248 and 10250
commit

I din queryanalyser kan kan du får at lave locken køre begin tran og selecten, vent med commit. Nu skal du så åbne en anden process for at teste at det virkelig funker:

select * from orders with (UPDLOCK ROWLOCK)
where orderid = 10248
Her vil du se at orderid 10248 ikke kommer frem!!!

Gå så tilbage din oprindelige process der har lavet locken og udfør commit... og vupti så kan du se at process nr. 2 giver dig orderid 10248
Avatar billede hojgaard Nybegynder
18. marts 2005 - 08:10 #15
Tak for dit svar janus_007 :)

Jeg har prøvet dit eksempel af med Northwind databasen, men kan ikke helt få det til at virke. Efter jeg låser rækkerne 10248 - 10250, har jeg i en anden connection prøvet følgende:

Denne giver slet ingen resultater, før jeg committer den først proces:
select * from orders with (UPDLOCK ROWLOCK) where orderid = 10248

Denne giver alle resultater:
select * from orders

Denne giver alle resultater:
select * from orders with (readpast)

Jeg har brug for at få låse x antal rækker, så andre connections kan ignorere dem.. og dermed frit kan arbejde på de andre rækker.
Avatar billede janus_007 Nybegynder
20. marts 2005 - 23:05 #16
Den med 10248 giver nemlig først at resultat efter at den første bliver committed, det er for at demonstrere locken. Jeg kan godt forstå nu hvad det er u vil, og det er ikke lige det man normalt vil opnå ved locking. Anyway...

Så ville jeg løse det vha. en separet tabel hvori alle id'er som var i brug blev overført til.

Denne tabel ville jeg ajourføre igennem den procedure der giver rækker til brugeren og left joine op imod den for ikke at få dde id'er der er i brug med ud i en select.

Noget ala, hvis din tabel som du vil styre selects på hedder Table

select t.* from Table t
left outer join IDinUse i
i.id = t.id
where i.id is null

Du skal naturligvis lave en lille procedure der kan vedligeholde id'erne i tabellen IDinUse osv... Men det er en relativ nem opgave.
Avatar billede hojgaard Nybegynder
23. marts 2005 - 16:54 #17
Jeg ved godt det er en smule kreativ måde at benytte locking på... men havde håbet det kunne løse mit problem :)

Ideen med en separat tabel har jeg haft i tankerne. Problemet med denne løsning er at få "frigjort" rækkerne igen, hvis en connection fx skulle gå ned.
Avatar billede janus_007 Nybegynder
23. marts 2005 - 17:15 #18
Ja der er selvfølgelig lidt ekstra arbejde med den slags specielle tilfælde.
Du kan sammen med de id'er som du vil låse også gemme processid

Den kan du få ud vha. select @@spid og evt. lave lidt connectionstat på den. En anden ting du kunne gøre var måske at sætte en max lock på. Og så sige at hvis id'en havde ligget i mere end 60min i tabellen var skulle den frigøres.
Avatar billede hojgaard Nybegynder
25. marts 2005 - 12:43 #19
Tak for hjælpen! Jeg tror den rigtige løsning er at benytte "låsningstabellen".
Det andet var måske en smule for kreativt :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester