Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Concurrencia
Atomicity: que las transacciones sean atmicas, se vean como una sola unidad Consistency: que los datos realmente estn bien relacionados y que no existan problemas de falta de informacin o falta de confiabilidad en los datos Isolation: aislamiento, separar las operaciones de distintas conexiones/usuarios unos de otros Durability: que se garantice la persistencia de los datos.
Durante esta seccin se tratan de abordar los conceptos de atomicity, consistency e isolation.
10.2 Transacciones
10.2.1 Definicin
Una transaccin es una unidad de programa que accesa y posiblemente actualiza varios elementos de datos.
Active, el estado inicial y permanece durante toda la ejecucin Partially committed, despus de que se ha ejecutado el ltimo statement Failed, despus de algun error, no se puede continuar Aborted, se hace un "rollback" hacia un estado anterior consistente Committed, despus del xito
10.2.3 Programacin
Crear Conexin
Abrir Conexin (Openconnection) Iniciar Transaccin (Begin Transaction) setAutoCommit(0/false) Queries... (Insert, Select, Update, Delete...)
-Error (Abort Transaction) rollback
10. Concurrencia
Notas:
Por default toda transaccin tiene autocommit = 1 El rollback generalmente se usa en el catch de los programas, aunque puede ir en cualquier parte. Una transaccin que no llega al commit automticamente hace el rollback
Scheduler del DBMS El calendarizador crea agendas, secuencias ordenadas de las acciones tomadas por una o ms transacciones. El siguiente ejemplo muestra 2 transacciones cuya nica caracterstica de consistencia es que A=B ya que ambas operaciones son iguales para ambos elementos. Por otro lado sabemos que si las transacciones son ejecutadas aisladamente la consistencia se preservar.
10. Concurrencia
2 transacciones
10. Concurrencia
Serial schedule, T2 precede a T1 Se puede observar que el resultado final no es el mismo, pero esto no es algo central ya que lo que realmente importa es la preservacin de consistencia. En general no se espera que el estado final de una base de datos sea independiente del orden de las transacciones.
10. Concurrencia
Nonserializable schedule
10.3.4 Conflict-Serializability
Tratando de plantear una condicin para asegurar que un schedule es serializable, se debe entender aquellos casos que representan conflictos, es decir, aquellos pares de acciones cuyo orden no puede ser intercambiado. Veamos primero aquellas acciones que no representan conflictos. Cuando Ti y Tj son transacciones diferentes: 1.
10. Concurrencia
3. wi(X); rj(Y) : no es un conflicto, por lo mismo que la anterior 4. wi(X); wj(Y) : no es un conflicto, por lo mismo que la anterior Por otro lado existen 3 situaciones en donde NO se pueden intercambiar el orden de las acciones. 1.
2. 2 escrituras del mismo elemento por diferentes transacciones, wi(X); wj(X). 3. Una lectura y escritura del mismo elemento por diferentes transacciones, ri(X); wj(X) y wi(X); rj(X). De manera que 2 acciones de diferentes transacciones pueden ser intercambiadas, a menos que:
10. Concurrencia
Una transaccin solo puede leer y escribir un elemento si se solicit un bloqueo y ste no se ha liberado.
Si una transaccin bloquea un elemento, debe liberarlo posteriormente. Si un schedule cumple con las condiciones anteriores se dice que el schedule es "legal"
10. Concurrencia
Locking retrasando una peticin para evitar un illegal schedule 10.3.5.2 Two-phase locking (2PL) "En toda transaccin, todos los locks preceden a todos los unlocks". Para los ltimos 2 diagramas de la seccin 10.3.5.1, el primero no cumple con 2PL y el segundo cumple 2PL. El problema de 2PL, caer en un deadlock
Deadlock 10.3.5.3 Shared y Exclusive Locks Un shared lock permite a otras transacciones leer la misma informacin pero no escribir Un exclusive lock evita que otros puedan leer y mucho menos escribir
10. Concurrencia
10. Concurrencia
Write cancelled
10. Concurrencia
dirty read: Una transaccin lee datos escritos por una transaccin concurrente que no ha hecho "commit" nonrepeatable read: Una transaccin re-lee datos que ley previamente y encuentra que han sido modificados por otra transaccin (que hizo commit en el inter). phantom read: Una transaccin re-ejecuta un query regresando un conjunto de tuplas que satisfacen una condicin de bsqueda y encuentra que el resultado ha cambiado debido a otra transaccin que hizo "commit" recientemente.
SQL Transaction Isolation Levels Isolation Level Read uncommitted Read committed Repeatable read Serializable Dirty Read Possible Not possible Not possible Not possible Nonrepeatable Read Possible Possible Not possible Not possible Phantom Read Possible Possible Possible Not possible
*PostgreSQL ofrece Read Committed (default) y Serializable *La mayora de los dbms ofrecen los 4 niveles
10. Concurrencia
READ UNCOMMITTED Tamben llamado "dirty read": los selects que norequieren bloqueos son realizados de manera que no es posible ver una versin ms reciente del registro, entonces no hay lecturas consistentes bajo este nivel. El opuesto es el READ COMMITTED. READ COMMITTED Parecido al nivel de aislamiento en Oracle. Todos los queries del tipo SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE nicamente bloquean los ndices de los registros, no los huecos (gaps) anteriores a ellos y as permite insertar libremente nuevos registros despus de los registros bloqueados. UPDATE yDELETE que usan un ndice nico con una nica condicin de bsqueda, solo bloquean el ndice del registro encontrado, no los huecos entre ellos. Pero en selecciones de "rangos" para UPDATE y DELETE en Innodb se debe aplicar un bloqueo de "next-key o gap" y bloquear las inserciones de otros usuarios en los huecos cubiertos en ese rango. Esto es necesario porque tuplas fantasmas (phantom rows) tienen que ser bloqueadas para replicacin y recuperacin. Lecturas consistentes llegan a ser como en Oracle: cada lectura consistente, aun dentro de la misma transaccin, lee y actualiza su propia copia. REPEATABLE READ El nivel por default en InnoDB. SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE, and DELETE, los cuales usan ndices nicos con una nica condicin de bsqueda, slo bloquean el ndice del registro encontrado, no los huecos anteriores a l. De otra manera estas operaciones emplean bloqueos "next-key", bloqueando el rango de ndices obtenido a partir del bloqueo "next-key" o "gap" y bloquea nuevas inserciones por parte de otros usuarios. En lecturas consistentes hay una diferencia muy importante con el nivel "read commited": en este nivel todas las lecturas consistentes dentro de la misma transaccin leen la misma copia de informacin establecida por la primer lectura. Esta convencin significa que si se realizan varios selects simples dentro de la misma transaccin estos selects son consistentes entre si. SERIALIZABLE Este nivel es como el anterior, pero todos los selects simples son implcitamente convertidos a SELECT ... LOCK IN SHARE MODE.
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
10. Concurrencia
mysql> set autocommit=0; mysql> set autocommit=0; mysql> SELECT * FROM t; Empty set (0.40 sec) mysql> INSERT INTO t VALUES (1, 2); mysql> SELECT * FROM t; Empty set (0.40 sec) mysql> SELECT * FROM t; Empty set (0.40 sec) mysql> COMMIT; mysql> COMMIT; mysql> SELECT * FROM t; +------+------+ | a | b | +------+------+ | 1 | 2 | +------+------+ 1 row in set (0.11 sec)
10.4.4 SELECT ... FOR UPDATE y SELECT ... LOCK IN SHARE MODE
Una lectura consistente no es conveniente en algunas circunstancias. Supongamos que se desea agregar una nueva tupla en una tabla "child", y asegurarse que el hijo tiene un padre en la tabla "parent". Parent id_parent Child id_child name id_parent name
Supongamos que se usa una lectura consistente para leer la tabla "parent" y en efecto se verifica que existe el padre del hijo en la tabla. Se podra agregar de manera segura la tupla en la tabla "child" ?. No, porque puede suceder que mientras algn otro usuario borre la tupla del padre en la tabla "parent" y no se recibira una notificacin al respecto. La solucin es realizar un "select" en modo de bloqueo, LOCK IN SHARE MODE. SELECT * FROM PARENT WHERE NAME = 'Jones' LOCK IN SHARE MODE; Realizando la lectura en modo compartido significa que se lee la ltima versin de los datos y se aplica un bloqueo en modo compartido en las tuplas ledas. Si la ltima versin pertenece a una transaccin "uncommitted" de otro usuario, se debe esperar a que la transaccin termine. Un bloqueo en modo compartido nos previene de que otros puedan actualizar o borrar la tupla que hemos ledo. Este ejemplo muestra cmo implementar integridad referencial en una aplicacin. Veamos otro ejemplo: Tenemos una columna que se emplea como contador en una tabla "child_codes" la cual se emplea para asignar
http://ict.udlap.mx/people/carlos/is341/bases10.html (13 of 23) [28/10/2009 12:27:32 a.m.]
10. Concurrencia
ids a cada hijo que se agrega a la tabla "child". Child_codes code Obviamente usar una lectura consistente para leer el valor actual no es una buena idea, ya que otros usuarios pueden leer la misma informacin y en consecuencia generarn un error de llave duplicada cuando se pretendan agregar a dichos hijos. Usar lock in share mode para la lectura no es una buena solucin tampoco porque si 2 usuarios leen el contador al mismo tiempo, entonces al menos uno de ellos terminar con un "deadlock" cuando trate de actualizar el contador. En este caso existen 2 maneras de implementar la lectura e incrementar el contador:
Actualizar el contador primero, incrementndolo en 1 y slo entonces leerlo. Leer el contador primero con un bloqueo "for update" e incrementarlo despes. SELECT COUNTER_FIELD FROM CHILD_CODES FOR UPDATE; UPDATE CHILD_CODES SET COUNTER_FIELD = COUNTER_FIELD + 1;
Un SELECT ... FOR UPDATE leer la ltima versin del dato, aplicando un bloqueo exclusivo para cada tupla que se lee. Se aplica el mismo bloqueo que se realiza para un update, recordemos que un update lleva tambin una condicin de bsqueda.
10. Concurrencia
una tabla.
10. Concurrencia
10.4.8 Locking and Indexes Generalmente los bloqueos se hacen en los ndices, de manera que hay ciertas variantes entre los distintos tipos.
B-tree indexes
Bloqueos exclusivos o compartidos para R/W a nivel de pgina Los bloqueos son liberados inmediatamente despus de que cada tupla es recuperada o insertada Proveen la ms alta concurrencia sin condiciones de "deadlock"
Bloqueos exclusivos o compartidos para R/W a nivel de ndice Los bloqueos son liberados inmediatamente despus de que cada comando es procesado
Hash indexes
Bloqueos exclusivos o compartidos para R/W a nivel de pgina Los bloqueos son liberados inmediatamente despus de que cada pgina es procesada El bloqueo por pgina es mejor (hablando de concurrencia) que aquellos por nivel de ndice pero son ms factibles para "deadlocks"
B-Tree ndices ofrecen el mejor rendimiento para aplicaciones concurrentes, adems de tener ms ventajas que los ndices hash. Son recomendados para aplicaciones que requieren indexar datos "escalares" en otro caso hay que esta concientes de las limitantes que presentan los otros esquemas. *Un datos escalar es cualquier nmero o cadena de caracteres.
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.09 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | +------+-------+ 2 rows in set (0.00 sec) mysql> insert into bank values(66,3453); Query OK, 1 row affected (0.09 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | +------+-------+ 2 rows in set (0.15 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | | 66 | 3453 | +------+-------+ 3 rows in set (0.00 sec) mysql> delete from bank where id=64; Query OK, 1 row affected (0.12 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 64 | 7865 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.01 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec)
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.01 sec) mysql> select * from bank where id=32 for update; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | +------+-------+ 1 row in set (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> update bank set debit=666 where id=32; . . . . . . .Wait unlock . . . . mysql> update bank set debit=666 where id=32; Query OK, 1 row affected (13.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.07 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 999 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.07 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.01 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.01 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.01 sec)
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 lock in share mode; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | +------+-------+ 1 row in set (0.00 sec)
mysql> update bank set debit=777 where id=32; . . . .>Wait unlock . . . . mysql> update bank set debit=888 where id=32; . ERROR 1213 (40001): Deadlock found when trying to get lock; . Try restarting transaction . . . . >T1 tiene bloqueado a 32, T2 espera la liberacion . >al hacer el update en T1 ahora T1 espera a T2 . >por lo tanto se produce un deadlock . >el dbms hace rollback en T1 (ultimo en espera) y procesa T2 . mysql> update bank set debit=777 where id=32; Query OK, 1 row affected (23.10 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 777 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 777 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 666 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 777 | | 66 | 3453 | +------+-------+ 2 rows in set (0.01 sec)
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 for update; +------+-------+ | id | debit | +------+-------+ | 32 | 777 | +------+-------+ 1 row in set (0.00 sec) mysql> update bank set debit=111 where id=32; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 for update; . . . . . . .> Wait unlock . . . . . mysql> select * from bank where id=32 for update; +------+-------+ | id | debit | +------+-------+ | 32 | 111 | +------+-------+ 1 row in set (32.45 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 111 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> update bank set debit=000 where id=32; Query OK, 1 row affected (6.88 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 111 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
10. Concurrencia
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 for update; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | +------+-------+ 1 row in set (0.00 sec)
mysql> update bank set debit=333 where id=32; . . . . . .> Wait unlock . . . . . mysql> update bank set debit=333 where id=32; Query OK, 1 row affected (3.20 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 0 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 lock in share mode; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | +------+-------+ 1 row in set (0.00 sec)
mysql> select * from bank where id=32 lock in share mode; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | +------+-------+ 1 row in set (0.00 sec) mysql> update bank set debit=444 where id=32; . . . . .> Wait unlock . . . . . . . mysql> update bank set debit=444 where id=32; Query OK, 1 row affected (30.83 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 444 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> commit; mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 444 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 444 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
mysql> update bank set debit=777 where id=32; ERROR 1213 (40001): Deadlock found when trying to get lock; Try restarting transaction
>T2 tiene bloqueado a 32, T1 espera la liberacion >al hacer el update en T2 ahora T2 espera a T1 >por lo tanto se produce un deadlock >el dbms hace rollback en T2 (ultimo en espera) y procesa T1
mysql> select * from bank; +------+-------+ | id | debit | +------+-------+ | 32 | 333 | | 66 | 3453 | +------+-------+ 2 rows in set (0.00 sec)
10. Concurrencia
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from bank where id=32 lock in share mode; +----+-------+ | id | debit | +----+-------+ | 32 | 444 | +----+-------+ 1 row in set (0.00 sec) > > Nadie puede modificar la tupla 32 > mysql> commit; Query OK, 0 rows affected (0.00 sec)
mysql> update bank set debit=555 . . . . .> Wait unlock . . . . . . mysql> update bank set debit=555 Query OK, 1 row Rows matched: 1
where id=32;
mysql> select * from bank; +----+-------+ | id | debit | +----+-------+ | 32 | 444 | | 66 | 3453 | +----+-------+ 2 rows in set (1.02 sec) mysql> commit; Query OK, 0 rows affected (0.14 sec) mysql> select * from bank; +----+-------+ | id | debit | +----+-------+ | 32 | 555 | | 66 | 3453 | +----+-------+ 2 rows in set (0.00 sec)
mysql> select * from bank; +----+-------+ | id | debit | +----+-------+ | 32 | 555 | | 66 | 3453 | +----+-------+ 2 rows in set (0.01 sec)