Lock de registro

22/07/2004 - 23:05 por Giovany | Informe spam
Un Saludo,como puedo realizar un bloqueo de un registro,
del tal manera que otros usuario no pueda leerlo mientras
la transaccion no haya terminado y actualizado dicho
registro, he probado con SET TRANSACTION ISOLATION LEVEL
SERIALIZABLE o poniendo la segerencia de bloque en el
from asi :

BEGIN TRANSACTION

SELECT numero+1
from correlativos WITH (XLOCK)
where aplicacion

Insert into pagos...

Update agenda_pagos
..

Update correlativos set numero=numero+1
where aplicacion

COMMIT TRANSACTION

pero la otra sesion tambien obtiene el mismo
numero, no se donde esta mi problema y seguramente no he
entendido bien forma de hacerlo,
si por favor pueden ayudarme.

le agradezco

Preguntas similare

Leer las respuestas

#6 Gustavo Larriera [MVP SQL]
23/07/2004 - 19:35 | Informe spam
Me parece que estamos hablando de cosas distintas o no nos estamos
entendiendo :-)

Cuando estoy haciendo la linea del UPDATE de la T1, el SELECT de la T2 queda
bloqueado. Que es lo que debe suceder para mantener consistencia. El SELECT
de la T2 recien puede leer cuando llego a la linea COMMIT de la T1.



Gustavo Larriera
Uruguay LatAm
http://sqljunkies.com/weblog/gux/
Este mensaje se proporciona "COMO ESTA" sin garantias y no otorga ningun
derecho / This posting is provided "AS IS" with no warranties, and confers
no rights.
"ulises" wrote in message
news:2cd401c470c7$2c363790$
Pero Gustavo, eso no impide las lecturas, si tomamos tu
ejemplo de transacción, creamos unas tablas de prueba

create table contador (numero int )
create table tablamaestra ( numero int,
descripcion varchar(20) )
insert into contador values (0)

y luego creamos un procedimiento donde usamos WAITFOR para
simular la demora en un proceso :

create procedure insertaregistro ( @proceso varchar(20) )
as
declare @cont int
begin transaction
select @cont = numero from contador
set @cont = @cont + 1
waitfor delay '00:00:03'
insert tablamaestra values (@cont,@proceso)
update contador set numero = @cont
commit transaction

si ejecutas las siguientes sentencias en dos ventanas
diferentes :

insertaregistro 'proceso 01'
insertaregistro 'proceso 02'

obtenemos que la secuencia es la misma para ambos registros

numero descripcion
1 proceso 01
1 proceso 02

(2 row(s) affected)

la mejor manera de controlar la secuencia es leyendo y
actualizando el registro al mismo tiempo

Saludos,
Ulises

El tema no es por las lecturas sino por las escrituras.


Si una transaccion
T1 está leyendo y también escribiendo un dato, otra


transacción T2 NO DEBE
leer el dato hasta que la primera transacción haya dado


COMMIT. A menos que
se permitan operaciones de lecturas sucias (que para


algunas pocas
aplicaciones se desea así, pero no es lo habitual en


OLTP).

Probemos esto:

use tempdb
go
create table numero (idnumero int, numero min)
go
insert numero values (1, 0)


Veamos ahora esto. Abramos una Transacion1 en una ventana:

Transaccion1
begin transaction
select numero from numero
update numero set numero = numero + 1 where idnumero=1
commit transaction
Fin Transaccion1

En otra ventana abramos una Transaccion2:
Transaccion2
select numero from numero
Fin Transaccion2

La Transaccion2 (que solamente intenta leer) queda


esperando hasta el COMMIT
de la Transaccion1. Cuando la T1 finaliza, recien


entonces la T2 lee el
resultado correcto (numero ahora vale 1 en vez de 0).


Gustavo Larriera
Uruguay LatAm
http://sqljunkies.com/weblog/gux/
Este mensaje se proporciona "COMO ESTA" sin garantias y


no otorga ningun
derecho / This posting is provided "AS IS" with no


warranties, and confers
no rights.
"SqlRanger [MVP .NET]" wrote in


message
news:
Perdona que te contradiga Gustavo pero si lees un




registro al comienzo de
una transacción con nivel de aislamiento de lectura




confirmada, el bloqueo
compartido que SQL Server establece sobre la fila (o la




clave del índice)
al
leerlo, se libera inmediátamente después de haber sido




leída la fila, no
manteniéndose dicho bloqueo durante el resto de la




transacción. Lo cual no
impide que otra conexión pueda leer e incluso




actualizar dicho registro
mientras se está ejecutando la transacción.

Con un nivel de aislamiento de lectura repetible, el




bloqueo compartido
sobre la fila leída se mantiene durante toda la




transacción, impidiendo
que
otras conexiones modifiquen el registro mientras esté




activa dicha
transacción, pero no impide que ese registro sea leído.




Como otras
conexiones podrían estar ejecutando el mismo código al




mismo tiempo, esto
daría lugar sin duda a interbloqueos, lo que no resulta




nada deseable.

Con un nivel de aislamiento serializable, ocurriría




algo similar que con
lectura repetible, a diferencia de que además estaría




bloqueada la
inserción
de registros que cumplieran la cláusula where de la




instrucción select,
para
evitar así la aparición de filas fantasma.

Saludos:

Jesús
MVP






.

Respuesta Responder a este mensaje
#7 Giovany
23/07/2004 - 21:15 | Informe spam
Saludos!

agradezco a todos sus comentarios, soy totalmente nuevo
en MSDE o Sqlserver, estoy aprediendo gracias a todos
ustedes.

Realice las pruebas como me dijo Gustavo Larriera y
siempre me permitia que dos usuarios realizaran la misma
lectura al mismo tiempo sobre el registro, pero me ayudo
a darme cuenta que debía pober un BEGIN TRANSACTION y
COMMIT TRANSACTION y probre tal como lo puse en el post o
sea que use la sugerencia de bloque XLOCK y listo me
funciono. Corri el procedimiento desde mi aplicativo
simulando una carga masiva 10000 para cada instacia y
cargue 10 veces mi aplicativo o instancia al mismo
tiempo, ademas puse un primary key en ese campo para
verificar la no duplicidad y estuvo bien.

Mi error fue que en el post lo hice siguiendo la idea
que tenia en mi programa y lo esquematice bien
y no copiando el codigo del programa o sea en conclusion
en el post puse como debia de ser:

BEGIN TRANSACTION
select .. from corre WITH (XLOCK)
Insert..
Update etc
COMMIT TRANSACTION

y en mi procedure no lo tenia y eso hacia que no
funcionara bien, supongo.


Muchas Gracias nuevamente.
Respuesta Responder a este mensaje
#8 Javier Loria
24/07/2004 - 02:43 | Informe spam
Hola Ulises:
El problema del codigo de InsertaRegistro es lo que puede ocurrir entre
el SELECT @Cont y el Update Contador, ya que efectivamente por default no se
bloquean con lecturas.
Una opcion es reescribir:
DECLARE @Cont INT
BEGIN TRANSACTION
UPDATE Contador
SET @Cont=Numero=Numero+1
WAITFOR DELAY '00:00:03'
INSERT TablaMaestra
VALUES(@Cont, @Proceso)
COMMIT
= El Update Selecciona, Incrementa y Bloquea, en una Sentencia. Claro que
la interpretacion de Numero en la Tabla Contador es el Ultimo Numero
ingresado y no el siguiente.
Este codigo el UPDATE debe quedar lo mas tarde posible, ya que hasta que
no ocurra el COMMIT nadie podra tocar dicho contador.


Javier Loria
Costa Rica
Se aprecia la inclusion de DDL (CREATE, INSERTS, etc.)
que pueda ser copiado y pegado al Query Analizer.
La version de SQL y Service Pack tambien ayuda.



ulises escribio:
Pero Gustavo, eso no impide las lecturas, si tomamos tu
ejemplo de transacción, creamos unas tablas de prueba

create table contador (numero int )
create table tablamaestra ( numero int,
descripcion varchar(20) )
insert into contador values (0)

y luego creamos un procedimiento donde usamos WAITFOR para
simular la demora en un proceso :

create procedure insertaregistro ( @proceso varchar(20) )
as
declare @cont int
begin transaction
select @cont = numero from contador
set @cont = @cont + 1
waitfor delay '00:00:03'
insert tablamaestra values (@cont,@proceso)
update contador set numero = @cont
commit transaction

si ejecutas las siguientes sentencias en dos ventanas
diferentes :

insertaregistro 'proceso 01'
insertaregistro 'proceso 02'

obtenemos que la secuencia es la misma para ambos registros

numero descripcion
1 proceso 01
1 proceso 02

(2 row(s) affected)

la mejor manera de controlar la secuencia es leyendo y
actualizando el registro al mismo tiempo

Saludos,
Ulises

El tema no es por las lecturas sino por las escrituras. Si una
transaccion T1 está leyendo y también escribiendo un dato, otra
transacción T2 NO DEBE leer el dato hasta que la primera transacción
haya dado COMMIT. A menos que se permitan operaciones de lecturas
sucias (que para algunas pocas aplicaciones se desea así, pero no es
lo habitual en OLTP).

Probemos esto:

use tempdb
go
create table numero (idnumero int, numero min)
go
insert numero values (1, 0)


Veamos ahora esto. Abramos una Transacion1 en una ventana:

Transaccion1
begin transaction
select numero from numero
update numero set numero = numero + 1 where idnumero=1
commit transaction
Fin Transaccion1

En otra ventana abramos una Transaccion2:
Transaccion2
select numero from numero
Fin Transaccion2

La Transaccion2 (que solamente intenta leer) queda esperando hasta
el COMMIT de la Transaccion1. Cuando la T1 finaliza, recien entonces
la T2 lee el resultado correcto (numero ahora vale 1 en vez de 0).


Gustavo Larriera
Uruguay LatAm
http://sqljunkies.com/weblog/gux/
Este mensaje se proporciona "COMO ESTA" sin garantias y no otorga
ningun derecho / This posting is provided "AS IS" with no
warranties, and confers no rights.
"SqlRanger [MVP .NET]" wrote in message
news:
Perdona que te contradiga Gustavo pero si lees un registro al
comienzo de una transacción con nivel de aislamiento de lectura
confirmada, el bloqueo compartido que SQL Server establece sobre la
fila (o la clave del índice) al leerlo, se libera inmediátamente
después de haber sido leída la fila, no manteniéndose dicho bloqueo
durante el resto de la transacción. Lo cual no impide que otra
conexión pueda leer e incluso actualizar dicho registro mientras se
está ejecutando la transacción.

Con un nivel de aislamiento de lectura repetible, el bloqueo
compartido sobre la fila leída se mantiene durante toda la
transacción, impidiendo que otras conexiones modifiquen el registro
mientras esté activa dicha transacción, pero no impide que ese
registro sea leído. Como otras conexiones podrían estar ejecutando
el mismo código al mismo tiempo, esto daría lugar sin duda a
interbloqueos, lo que no resulta nada deseable.

Con un nivel de aislamiento serializable, ocurriría algo similar
que con lectura repetible, a diferencia de que además estaría
bloqueada la inserción de registros que cumplieran la cláusula
where de la instrucción select, para evitar así la aparición de
filas fantasma.

Saludos:

Jesús
MVP






.
Respuesta Responder a este mensaje
#9 SqlRanger
24/07/2004 - 11:12 | Informe spam
<Gustavo>
El tema no es por las lecturas sino por las escrituras.
</Gustavo>

<SqlRanger>
No es tan simple, todo influye, tanto las lecturas como las escrituras
</SqlRanger>

<Gustavo>
Si una transaccion
T1 está leyendo y también escribiendo un dato, otra transacción T2 NO DEBE
leer el dato hasta que la primera transacción haya dado COMMIT
</Gustavo>

<SqlRanger>
La única forma de que una transacción esté leyendo y escribiendo un dato al
mismo tiempo es mediante:

declare @variable tipo
update tabla
set @variable = campo = expresión
where condición

Si no se hace así, sino que se hace de esta otra forma:
begin tran
select
update
commit

mientras la ejecución se encuentra en el punto1 o el punto2, no se impide
que otra conexión lea o modifique el registro. SQL Server no analiza todas
las intrucciones de una transacción para establecer los bloqueos a priori,
sino que según las va ejecutando establece los bloqueos y esos bloqueos los
mantiene durante toda la transacción o no, dependiendo del tipo de bloqueo y
del nivel de aislamiento.

</SqlRanger>



Saludos:

Jesús López
MVP Microsoft .NET
Mentor Asociado Solid Quality Learning
www.SolidQualityLearning.com
¡No sólo formación!

"No darás tropezón ni desatino que no te haga adelantar camino"
Bernardo de Balbuena (1568-1627); escritor español
email Siga el debate Respuesta Responder a este mensaje
Ads by Google
Help Hacer una pregunta AnteriorRespuesta Tengo una respuesta
Search Busqueda sugerida