Avatar billede wolker Nybegynder
17. januar 2004 - 23:29 Der er 18 kommentarer og
2 løsninger

Transaction, til at administrere 2 samtidige kald

Jeg bruger nedenstående procedure til at udvælge et tal, og efter tallet er taget, skal der gå mindst 5 minutter før samme tal må bruges igen.
Men det virker ikke helt, for ud af ca. 40000 kald i døgnet, er der omkring 3 eller 4 gange det samme tal trækkes på præcis samme tidspunkt, det kan jeg se i loggen og det er ret kritisk når det sker.

Mit spørgsmål er så, hvad er der galt, burde proceduren ikke være atomar, sådan at 2 samtidige kald ikke giver det samme nr.

CREATE PROCEDURE dbo.GB_GetNr
@Nr int outPut,
as
begin transaction
  SELECT top 1 @Nr = Nr FROM NrPool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < GETDATE()
    ORDER BY Nr_Timestamp

  if @Nr is not null
    update NrPool set Nr_Timestamp = getdate() where Nr = @Nr

commit
GO
Avatar billede arne_v Ekspert
17. januar 2004 - 23:37 #1
Hvad er transaction isolation level ?
Avatar billede trer Nybegynder
17. januar 2004 - 23:41 #2
Svaret er nej. Når select'en kører får du en read/share lås. Først når din update fyres, får du en ekslusiv skrivelås.

Dermed kan to forskellige kald nå at læse samme nr.

Løsningen er ganske simpel. Angiv et låsehint i din select, eller skriv hele proceduren om, så du undgår at have to sql statements.

Låsehints'ne burde være HOLDLOCK & UPDLOCK - du kan se dem i Books Online (installeret sammen med Enterprise Mgr og Query Analyser).
Avatar billede trer Nybegynder
17. januar 2004 - 23:42 #3
Prøv også at lege med denne

Update nrpool set nr_timestamp=getdate
where nr in (SELECT top 1 Nr FROM NrPool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < GETDATE() and (nr is not null)
    ORDER BY Nr_Timestamp)
Avatar billede arne_v Ekspert
17. januar 2004 - 23:44 #4
trer>

der burde ikke være noget problem ved transaction isolation level serializable
Avatar billede arne_v Ekspert
17. januar 2004 - 23:50 #5
Hvis du bruger default (read committed) så prøv_

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

inden BEGIN TRANSACTION.
Avatar billede trer Nybegynder
17. januar 2004 - 23:51 #6
Næh, men det er sjældent at folk kører andet end standard level, og det har normalt en negativ performance-effekt at skifte til det niveau.
Avatar billede arne_v Ekspert
18. januar 2004 - 00:04 #7
HOLDLOCK har vel præcis samme effekt som TRANSACTION ISOLATION LEVEL SERIALIZABLE
så jeg kan ikke helt se argumentet ?

(og UPDLOCK duer vel ikke i den her kontekst ??)
Avatar billede wolker Nybegynder
18. januar 2004 - 00:24 #8
Interessant diskussion:

er i enige om at SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
vil løse problemet?
Og ved negativ performence, hvad snakker vi om her, altså tidsmæssig, som jeg skrev bliver den 'kun' kaldt ca 40000 gange i døgnet, men der er selvfølgelig nogle timer om dagen hvor der måske er 5000 kald i timen, antal kald stiger forhåbentlig, da det afhænger af antal besøgende på siden.

Så vil det have en mærkbar negativ performence ved f.eks 20000 kald i timen?
Avatar billede arne_v Ekspert
18. januar 2004 - 00:30 #9
Der vil være signifikant overhead uanset hvordan du løser problemet.

20000 TPH = 333 TPM burde ikke give noget problem

Min hypotese er at SET TRANSACTION ISOLATION LEVEL SERIALIZABLE og WITH (HOLDLOCK)
burde gøre det samme og koste det samme i overhead.

Jeg vil argumentere for SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ved
at det er standard og WITH (NOLOCK) ikke er det.

Men man kunne også argumentere for at WITH(NOLOCK) kun gælder for den
ene SELECT mens man kan glemme at sætte TRANSACTION ISOLATION LEVEL
tilbage hvis man genbruger connection og skal lave noget andet
hvor man ikke har brug for SERIALIZABLE.
Avatar billede trer Nybegynder
18. januar 2004 - 22:13 #10
arne_v> Du har ret mht UPDLOCK - jeg huskede forkert og mente en XLOCK.

Mht forskellen på en HOLDLOCK verus SET TRANSACTION ISOLATION osv. De har samme effekt, men SET kommandoer har normalt en ekstra bivirkning; Proceduren skal rekompileres og ny query plan laves for hvert kald når SET kommandoen ændrer indstillningerne i fht session-indstilningerne.

I øvrigt - i dit sidste indlæg; Du mener HOLDLOCK og ikke NOLOCK :-) 

I øvrigt, nu jeg lige kigger i BOL: HOLDLOCK og SERIALIZABLE gør blot at share-låsen holdes - dvs. andre kan stadig læse data - og så har vi ikke løst problemet.

Så skal der bruges et låsehint, bør det være XLOCK - og ellers bør man skrive proceduren om så der kun indgår et update statement (som vist ovenfor).
Avatar billede arne_v Ekspert
18. januar 2004 - 22:31 #11
Ja - WITH(HOLDLOCK) selvfølgelig.
Avatar billede arne_v Ekspert
18. januar 2004 - 22:52 #12
Jeg kender ikke SQLServer i dybden, men jeg ville blive overrasket hvis
problemet kunne opstå ved transaction isolation level serializable.

Problemet er f.eks.:

con 1: begin
con 1: select
con 2: begin
con 2: select
con 1: update
con 1: commit
con 2: update
con 2: commit

serializable indikerer repeatable read og hvis con 1 skal kunne gentage
SELECT så kan con 2 ikke lave commit inden con 1 har lavet commit, men
hvis con 2 skal kunne gentage SELECT efter con 1 har lavet commit
hvad så - vil den returnere gamle data ?
Avatar billede wolker Nybegynder
18. januar 2004 - 23:47 #13
Hmmmmm, hvad bliver i enige om, vil serializable virke eller ikke, jeg har ikke prøvet det endnu, da jeg ikke har adgang til den sql-server hjemmefra, men med mindre i bliver enige om at det ikke virker, prøver jeg i morgen på arbejde at indsætte linjen med serializable før transaction

trer>
Update nrpool set nr_timestamp=getdate
where nr in (SELECT top 1 Nr FROM NrPool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < GETDATE() and (nr is not null)
    ORDER BY Nr_Timestamp)

Ovenstående ser umiddelbar fin ud, men hvordan får jeg fat i det nr. som opdateres

Update nrpool set nr_timestamp=getdate
where nr in (SELECT top 1 @Nr=Nr FROM NrPool .......)

Dette virker ikke. Brokker sig over = tegnet
Avatar billede arne_v Ekspert
18. januar 2004 - 23:49 #14
Men pointen med recompile af SP gør selvfølgelig WITH(XLOCK) tiltrækkende.
Avatar billede wolker Nybegynder
19. januar 2004 - 00:55 #15
Jeg tror jeg selv har fundet løsningen på mit sidste spørgsmål

begin transaction

Update nrpool set @Nr=nr, nr_timestamp=getdate
where nr in (SELECT top 1 Nr FROM NrPool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < GETDATE() and (nr is not null)
    ORDER BY Nr_Timestamp)

commit

Burde det ikke virke?, det virker i hver fald i test med en bruger (mig)

Men jeg tester det i morgen, så finder jeg ret hurtig ud af det.
Avatar billede trer Nybegynder
26. januar 2004 - 09:37 #16
wolker> du laver en select efter din update.

a la

declare @ts datetime
set @ts = getdate()
begin transaction
update NrPool with (holdlock) set Nr_Timestamp = @ts
where  nr in
    (select top 1 nr from nrpool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < @ts
    ORDER BY Nr_Timestamp)

select top 1 @nr = nr from nrpool
WHERE (DATEADD(mi,5, Nr_Timestamp) < @ts
ORDER BY Nr_Timestamp)
commit

Måske skal du have en bedre nøgle til at sikre du får samme nr ud som du lige har brugt - men det er tanken.
Avatar billede wolker Nybegynder
27. januar 2004 - 00:06 #17
trer> virker ikke, det prøvede jeg dagen efter, det skete stadigvæk at 2 bruger fik det samme nummer og jeg gjorde det endda sådan her:

set @@dd=getdate()
update NrPool with (holdlock) set Nr_Timestamp = @dd
where ID in
    (select top 1 ID from nrpool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < @ts
    ORDER BY Nr_Timestamp)

så havde jeg jo ID'en, at selecte på, men stadig, kunne der åbenbart komme en bruger mere ind, og det forstår jeg overhoved ikke.

Men langt om længe, da jeg havde brugt hele dagen på at søge efter en løsning fandt jeg den. Og det er faktisk meget nemt at gøre, man gør således

CREATE PROCEDURE dbo.GB_GetNr
@Nr int outPut,
as
begin transaction
Update NrPool with (holdlock) set @nr=nr, Nr_timestamp = Getdate() where nr in
  (Select top 1 nr from NrPool
    WHERE (DATEADD(mi,5, Nr_Timestamp) < Getdate()
    ORDER BY Nr_Timestamp)
commit

Dejlig kort, og det virker hver gang, det har kørt hele ugen uden fejl.

Men i deler pointene, som tak for jeres bidrag, og fordi i gad at svare, og i hjalp mig trods alt på vej.

arne_v>> lav et svar hvis du vil have nogle point, ellers får trer det hele
Avatar billede arne_v Ekspert
27. januar 2004 - 00:11 #18
svar
Avatar billede trer Nybegynder
27. januar 2004 - 00:29 #19
"Update NrPool with (holdlock) set @nr=nr, Nr_timestamp...."

Ganske interessant - har ikke tidligere set løsninger hvor man sætter en variabel i en update. Så har jeg også fået lært noget her :-)
Avatar billede wolker Nybegynder
27. januar 2004 - 23:58 #20
Ja det er altid rart at lære noget nyt :-)
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