|
| [SQL2k] Kapløb Fra : Torben Frandsen |
Dato : 24-03-04 10:52 |
|
Hej
Jeg har en tabel hvor en applikation inserter en række en gang i mellem. En
anden applikation poller med jævne mellemrum tabellen med en sp for at se om
der skulle være kommet noget nyt. Denne applikation skal køre i flere
instanser, og derfor er det vigtigt at to eller flere instanser ikke kommer
til at tage den samme række. Foreløbig ser min sp i princippet sådan ud:
IF EXISTS(
SELECT ID
FROM Job
WHERE Taken = 0)
BEGIN
SELECT TOP 1 @GUID = ID FROM Job WHERE Taken = 0
UPDATE Job SET Taken = 1 WHERE ID = @GUID
RETURN 1
END
ELSE
RETURN 0
Spørgsmålet er, om jeg kan være sikker på om caller2 kan nå at evaluere
(Taken = 0) i det splitsekund der vel må gå før caller1 kan sige SET Taken =
1.
Torben
| |
Torben Frandsen (24-03-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 24-03-04 11:08 |
|
Torben Frandsen wrote:
> Spørgsmålet er, om jeg kan være sikker på om caller2 kan nå at
> evaluere (Taken = 0) i det splitsekund der vel må gå før caller1 kan
> sige SET Taken = 1.
Nej det er ej. Spørgsmålet er, om jeg kan være sikker på at caller 2 /ikke/
kan nå at evaluere (Taken = 0) til true i det splitsekund osv.
Torben
| |
Kristian Damm Jensen (24-03-2004)
| Kommentar Fra : Kristian Damm Jensen |
Dato : 24-03-04 12:09 |
|
Torben Frandsen wrote:
> Torben Frandsen wrote:
>
>> Spørgsmålet er, om jeg kan være sikker på om caller2 kan nå at
>> evaluere (Taken = 0) i det splitsekund der vel må gå før caller1 kan
>> sige SET Taken = 1.
>
> Nej det er ej. Spørgsmålet er, om jeg kan være sikker på at caller 2
> /ikke/ kan nå at evaluere (Taken = 0) til true i det splitsekund osv.
Det kan du ikke. Læs op på transaktionsstyring.
--
Kristian Damm Jensen damm (at) ofir (dot) dk
I hate to advocate drugs, alcohol, violence, or insanity to anyone, but
they've always worked for me. -- Hunter S. Thompson
| |
Torben Frandsen (24-03-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 24-03-04 16:01 |
|
Kristian Damm Jensen wrote:
> Torben Frandsen wrote:
>> Torben Frandsen wrote:
>>
>>> Spørgsmålet er, om jeg kan være sikker på om caller2 kan nå at
>>> evaluere (Taken = 0) i det splitsekund der vel må gå før caller1 kan
>>> sige SET Taken = 1.
>>
>> Nej det er ej. Spørgsmålet er, om jeg kan være sikker på at caller 2
>> /ikke/ kan nå at evaluere (Taken = 0) til true i det splitsekund osv.
>
> Det kan du ikke. Læs op på transaktionsstyring.
Takker. Hvis de to statements (men ikke IF EXISTS) sker mellem BEGIN TRAN og
COMMIT, og hvis SELECT gøres med et READPAST-hint, så nærmer vi os vel noget
brugbart?
Torben
| |
Kristian Damm Jensen (24-03-2004)
| Kommentar Fra : Kristian Damm Jensen |
Dato : 24-03-04 21:52 |
|
Torben Frandsen wrote:
> Kristian Damm Jensen wrote:
>> Torben Frandsen wrote:
>>> Torben Frandsen wrote:
>>>
>>>> Spørgsmålet er, om jeg kan være sikker på om caller2 kan nå at
>>>> evaluere (Taken = 0) i det splitsekund der vel må gå før caller1
>>>> kan sige SET Taken = 1.
>>>
>>> Nej det er ej. Spørgsmålet er, om jeg kan være sikker på at caller 2
>>> /ikke/ kan nå at evaluere (Taken = 0) til true i det splitsekund
>>> osv.
>>
>> Det kan du ikke. Læs op på transaktionsstyring.
>
> Takker. Hvis de to statements (men ikke IF EXISTS) sker mellem BEGIN
> TRAN og COMMIT, og hvis SELECT gøres med et READPAST-hint, så nærmer
> vi os vel noget brugbart?
Jeg er ikke ekspert i MsSQL så jeg aner ikke hvad READPAST gør. Men det
øvrige lyder rimeligt. Tilføj, at din fejlbehandling, hvis du har en sådan
skal benytte rollback tran. Og hvis du så vil være på den sikre side laver
du en trigger, der forhindrer at man taken fra 1 til 1!
--
Kristian Damm Jensen damm (at) ofir (dot) dk
....See their swords? They glow blue in the presence of lawyers. --
Terry Pratchett
| |
Jacob Buus Smidt (25-03-2004)
| Kommentar Fra : Jacob Buus Smidt |
Dato : 25-03-04 19:55 |
|
"Torben Frandsen" <torben@rem.airsupport.dk> skrev i en meddelelse
news:40615a2e$0$217$edfadb0f@dread16.news.tele.dk...
>
> IF EXISTS(
> SELECT ID
> FROM Job
> WHERE Taken = 0)
>
> BEGIN
> SELECT TOP 1 @GUID = ID FROM Job WHERE Taken = 0
> UPDATE Job SET Taken = 1 WHERE ID = @GUID
> RETURN 1
> END
> ELSE
> RETURN 0
>
En alternativ metode kunne være følgende:
/* Marker "ledige jobs" */
UPDATE Job
SET Taken = @@SPID
WHERE Taken = 0
/* Hvis nogen blev markeret */
IF @@ROWCOUNT > 0
BEGIN
/* Find de netop markerede
SELECT *
FROM Job
WHERE Taken = @@SPID
/* Gør det, du havde tænkt dig at gøre med posterne... */
RETURN 1
END
ELSE
RETURN 0
I øvrigt skal du i ovenstående finde en måde at markere de poster, der
allerede har været valgt, idet "SELECT *..."-sætningen jo udvælger alle
poster, som den pågældende proces tidligere har "taget ejerskab" af. Måske,
du skulle erstatte @@SPID med din @GUID fra før, så er du på den sikre side.
Ideen er, at du finder og markerer i samme operation, hvorved du undgår at
skulle opfinde din egen semafor / kritisk region via. transaktionsstyring.
Det er muligt, at du skal finde en anden variant på samme idegrundlag, som
passer til netop din situation.
--
Hilsen Jacob
| |
Torben Frandsen (26-03-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 26-03-04 09:57 |
|
Jacob Buus Smidt wrote:
> Ideen er, at du finder og markerer i samme operation, hvorved du
> undgår at skulle opfinde din egen semafor / kritisk region via.
> transaktionsstyring.
Din løsning skal helt klart have et par point for elegance. Hvad jeg ikke
skrev var, at grunden til at der ville være flere instanser af den kaldende
applikation, er at der skal være en simpel load balancing. Umiddelbart ville
man med din løsning kunne risikere at kalder1 får en helt masse jobs og
kalder2, som kommer forbi en jiffy senere får nul.
Det kunne man selvfølgelig fixe ved at skrive UPDATE TOP 1 (...) (Hwa? Ka
man æ? Nå men så en IN-klausul da.)
Nu spekulerer jeg bare på, hvad performer bedst, en samlet transaktion eller
din metode med en IN(SELECT TOP (...))-klausul på?
Torben
| |
Jacob Buus Smidt (26-03-2004)
| Kommentar Fra : Jacob Buus Smidt |
Dato : 26-03-04 18:24 |
|
>
> Din løsning skal helt klart have et par point for elegance. Hvad jeg ikke
> skrev var, at grunden til at der ville være flere instanser af den
kaldende
> applikation, er at der skal være en simpel load balancing. Umiddelbart
ville
> man med din løsning kunne risikere at kalder1 får en helt masse jobs og
> kalder2, som kommer forbi en jiffy senere får nul.
>
> Det kunne man selvfølgelig fixe ved at skrive UPDATE TOP 1 (...) (Hwa? Ka
> man æ? Nå men så en IN-klausul da.)
>
Ok, men så lad os se, hvordan det ser ud:
DECLARE @myid uniqueidentifier
SET @myid = NEWID()
/* Marker et ledigt job */
UPDATE Job
SET Taken = @myid
WHERE ID = (SELECT TOP 1 J.ID FROM Job J WHERE J.Taken IS NULL)
/* Hvis noget blev markeret */
IF @@ROWCOUNT > 0
BEGIN
/* Find det netop markerede
SELECT *
FROM Job
WHERE Taken = @myid
/* Gør det, du havde tænkt dig at gøre med posten... */
RETURN 1
END
ELSE
RETURN 0
> Nu spekulerer jeg bare på, hvad performer bedst, en samlet transaktion
eller
> din metode med en IN(SELECT TOP (...))-klausul på?
>
Jeg tror, at ovenstående er mindst ligeså optimalt, som din endelige
løsning, som du postede i en anden tråd. Især, hvis du har mange samtidige
operationer i job-tabellen, da SQL servers proces-scheduler ikke kan stille
noget op over for dine explicitte låse. Husk på, at "(SELECT TOP 1 J.ID FROM
Job J WHERE J.Taken = 0)" kun evalueres én gang.
Hilsen Jacob
| |
Torben Frandsen (30-03-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 30-03-04 12:59 |
|
Jacob Buus Smidt wrote:
> Jeg tror, at ovenstående er mindst ligeså optimalt, som din endelige
> løsning, som du postede i en anden tråd. Især, hvis du har mange
> samtidige operationer i job-tabellen, da SQL servers proces-scheduler
> ikke kan stille noget op over for dine explicitte låse. Husk på, at
> "(SELECT TOP 1 J.ID FROM Job J WHERE J.Taken = 0)" kun evalueres én
> gang.
Nu prøvede jeg lige at hakke det ind i Query Analyzer og lave en Estimated
Execution Plan. Scoren blev:
Dig: 57,14%
Mig: 42,86%
Jeg er ikke i tvivl om at det tipper i din favør, når der ad åre kommer
mange samtidige operationer. Men hvor meget? Lige nu er jeg !(nysgerrig nok
&& nervøs nok && ledig nok) til at generere et gennemtestet svar.
Torben
| |
Stig Johansen (29-03-2004)
| Kommentar Fra : Stig Johansen |
Dato : 29-03-04 04:09 |
|
Torben Frandsen wrote:
> Hvad jeg ikke
> skrev var, at grunden til at der ville være flere instanser af den
> kaldende applikation, er at der skal være en simpel load balancing.
[snip]
> Nu spekulerer jeg bare på, hvad performer bedst ...
Hvis du er nervøs for performance(systemet), skal du slet ikke lave det på
den måde.
Kan du i forhold til din OP uddybe:
....inserter en række en gang i mellem. -> Hvor tit?
....poller med jævne mellemrum -> Hvor tit?
--
Med venlig hilsen
Stig Johansen
| |
Torben Frandsen (30-03-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 30-03-04 10:57 |
|
Stig Johansen wrote:
> Hvis du er nervøs for performance(systemet), skal du slet ikke lave
> det på den måde.
Nervøs er måske så meget sagt. Men serveren løser mange opgaver, og med
tanke på mange-bække-små-princippet bør man nok forsøge at undgå indlysende
performance-spild.
> Kan du i forhold til din OP uddybe:
> ...inserter en række en gang i mellem. -> Hvor tit?
Det ved jeg først når systemet går i produktion. Men forventeligt omkring
500 gange i døgnet.
Det skal måske med at et af felterne i tabellen er af typen 'image' og kan
fylde op til 2MB, og det er den eksterne behandling af dette, der tager tid
(op til 20 minutter.) Når jeg siger load balancing, er det altså denne
behandling der skal fordeles over flere maskiner.
> ...poller med jævne mellemrum -> Hvor tit?
5s, hvilket kan accepteres fordi klienterne tilgår systemet fra et asynkront
miljø. Brugernes indtryk af responsiveness er alligevel rent snyd.
Torben
| |
Stig Johansen (05-04-2004)
| Kommentar Fra : Stig Johansen |
Dato : 05-04-04 04:27 |
|
Torben Frandsen wrote:
> Stig Johansen wrote:
>
>> Hvis du er nervøs for performance(systemet), skal du slet ikke lave
>> det på den måde.
>
> Nervøs er måske så meget sagt. Men serveren løser mange opgaver, og med
> tanke på mange-bække-små-princippet bør man nok forsøge at undgå
> indlysende performance-spild.
Enig, uanset hvad, er det altid en god ide, at spare mest muligt på
ressourcer.
>> Kan du i forhold til din OP uddybe:
>> ...inserter en række en gang i mellem. -> Hvor tit?
>
> Det ved jeg først når systemet går i produktion. Men forventeligt omkring
> 500 gange i døgnet.
Det lyder jo ikke af særligt meget, ca 20 i timen.
> Det skal måske med at et af felterne i tabellen er af typen 'image' og kan
> fylde op til 2MB, og det er den eksterne behandling af dette, der tager
> tid (op til 20 minutter.) Når jeg siger load balancing, er det altså denne
> behandling der skal fordeles over flere maskiner.
Det lyder heller ikke af meget, men hov - 20 minutters behandlingstid, 20 i
timen, det må jo give hele 7 maskiner til din externe behandling, er det
korrekt?
>
>> ...poller med jævne mellemrum -> Hvor tit?
>
> 5s, hvilket kan accepteres fordi klienterne tilgår systemet fra et
> asynkront miljø. Brugernes indtryk af responsiveness er alligevel rent
> snyd.
Det lyder også acceptabelt, men hvis der er 7 maskiner, er det mere end 1
pr. sek.
Det gan godt være, jeg har misforstået det, men for mig ser dit setup noget
ala:
(Bruger)Data -> SQLServer -> Data behandles på Extern server(e) -> Data
opdateres SQLServer -> (Bruger)Data færdig.
Er det noget i den stil, du har gang i?
Hvis det er sådan noget, er det nok smartere at lave en trigger på insert,
der kalder en xp, der håndterer loadbalancing med noget broadcast.
--
Med venlig hilsen
Stig Johansen
| |
Torben Frandsen (13-04-2004)
| Kommentar Fra : Torben Frandsen |
Dato : 13-04-04 12:01 |
|
Stig Johansen wrote:
> Det lyder heller ikke af meget, men hov - 20 minutters
> behandlingstid, 20 i timen, det må jo give hele 7 maskiner til din
> externe behandling, er det korrekt?
Jaeh, eller i hvert fald 7 eksterne processer.
> Det lyder også acceptabelt, men hvis der er 7 maskiner, er det mere
> end 1 pr. sek.
Det kan vi vist ikke blive uenige om :)
> Det gan godt være, jeg har misforstået det, men for mig ser dit setup
> noget ala:
>
> (Bruger)Data -> SQLServer -> Data behandles på Extern server(e) ->
> Data opdateres SQLServer -> (Bruger)Data færdig.
> Er det noget i den stil, du har gang i?
Jeps, og så er der nogle side effects undervejs.
> Hvis det er sådan noget, er det nok smartere at lave en trigger på
> insert, der kalder en xp, der håndterer loadbalancing med noget
> broadcast.
Det har du muligvis ret i, men så kommer jeg vist for langt ud i uncharted
territory :)
Torben
| |
|
|