Grimpi IT Blog

febrero 24, 2011

Breve introducción de Read Uncommitted a Snapshot Isolation

Filed under: MVCC, SQL Server — grimpi @ 6:24 pm

Otras de las dudas muy habituales para los iniciados (y no tan iniciados) en SQL Server (y también en cualquier desarrollador de cualquier motor de base de datos) es que tipo de niveles de bloqueos existen (isolation level), para que sirven, en que caso usarlos y que desventajas tiene su uso.
Por lo tanto, decidí hacer un muy breve resumen de los distintos niveles existentes. Si bien, este post especifico esta enfocado en SQL Server, conceptualmente, puede servir para cualquier motor. Los niveles de aislamiento están especificados en el estandar ANSI.

Read Uncommitted: A veces llamado “lectura sucia” o “diry read”. Una transacción ejecutada en este nivel de aislamiento, puede leer datos que están siendo modificados por otra transacción concurrente. Ejemplo: Transacción T2 hace un update sobre el registro R1, sin embargo, la transacción T1 puede leer el registro R1 modificado, a pesar de que T2 potencialmente puede hacer un roll back sobre el mismo.

  • Ventajas: No se necesita hacer un lockeo para leer datos, aunque si para poder modificarlos.
  • Contras: No se puede garantizar la consistencia de los datos leídos. Puede darse el caso de que sea lea un dato que luego no exista más.
  • Uso habitual: En situaciones de alta concurrencia donde la potencial inconsistencia de datos sea tolerada.

Read Committed: Una transacción ejecutada en este nivel de aislamiento puede leer solamente datos commiteados. Por ejemplo, si una transacción concurrente T2 hace un update sobre el registro R1, la transacción T1 no podrá acceder al mismo y va a quedar bloqueada hasta que T2 haga un commit o un rollback sobre el registro.

  • Ventajas: Buen balance entre concurrencia y consistencia.
  • Contras: Mayor nivel de bloqueo.
  • Uso habitual: Es el isolation level mas común, de hecho, el que se usa por defecto en SQL Server.

Repeatable Read: Una transacción ejecutada en este nivel de aislamiento solo puede leer información comiteada con la garantía de que mientras dure la transacción, nadie va a poder leer esos datos. Esto significa que si vuelvo a leer esos datos dentro de la transacción, tengo total seguridad de que el resultado va a ser exactamente el mismo, ya que están bloqueados para la escritura en otra transacción.
Ejemplo: Asumamos que tenemos la tabla Cuentas que guarda el balance monetario de los clientes. Empezamos la transacción T1 con filtrando todos los balances cuyo monto sea mayor a 1000. Supongamos que nos devuelve 10 registros. Ahora empieza otra transacción T2, que inserta un nuevo registro cuyo balance es de 1020 y comitea. Si consultamos nuevamente en la transacción T1 todos los balances mayores a 1000, ahora nos va a devolver 11 registros. Esto se debe a que la transacción T1 bloqueó el conjunto de registros seleccionados en la consulta, pero no el predicado del filtro.
Ahora, si dentro de la transacción T2 intento eliminar todos los registros cuyo balance sea mayor a 1000, va a ocurrir un bloqueo, ya que están lockeados por la transacción T1.

  • Ventajas: Alta consistencia en los datos.
  • Contras: Mayor nivel de bloqueos y disminución del grado de concurrencia.
  • Uso habitual: En operaciones bancarias o financieras que requieran un grado de exactitud muy alto.

Serializable: Una transacción T1 ejecutada en este nivel ofrece el mayor grado de consistencia posible, eliminado los registros fantasmas. El motor puede llegar a bloquear incluso la tabla entera, para garantizar la máxima consistencia de datos.

  • Ventajas: Consistencia de datos absoluta.
  • Contras: Altísimo grado de bloqueo y muy bajo nivel de concurrencia, ya que las tablas pueden ser bloqueadas en su totalidad mientras se realiza una transacción.
  • Uso habitual: No es muy habitual. Casos muy raros cuando se requiera una gran consistencia de datos.

Adicionalmente, existen nuevos niveles de aislamiento a partir de SQL Server 2005.

Read-Committed-Snapshot : Este no es un nuevo nivel de aislamiento, sino una reimplementación del read comitted pero sin bloqueos.
SQL Server implementa versionado de registros mediante copias (snapshots) para evita el bloqueo.

  • Ventajas: Acceso no bloqueante a datos consistentes.
  • Contras: Mayor overhead para mantener el versionado de datos.
  • Uso habitual: Minimar los bloqueos lectura/escritura y el uso de el hint NOLOCK.

Snapshot Isolation: Ya habíamos hablado acá sobre este nivel de lockeo. Una transacción bajo este nivel de aislamiento, trabaja sobre una copia (o “snapshot”) de la base de datos. Cuando finaliza la transacción, hace un merge entre la copia y la base de datos.

  • Ventajas: Mayor nivel de consistencia incluso que un “repetable red”, con mayor nivel de concurrencia, ya que no realiza lockeos.
  • Contras: Mayor overhead para mantener las distintas versiones de un conjunto de datos, lo que impacta en la performance.
  • Uso Habitual: Cuando se necesita obtener información muy consistente y en entornos de alta concurrencia.

Recordemos que para activar especificar el nivel de aislamiento, se debe ejecutar SET TRANSACTION ISOLATION LEVEL <ISOLATION>

Anuncios

febrero 8, 2011

Control de concurrencia multiversión MVCC

Filed under: Engine, MVCC, PostgreSQL, SQL Server — grimpi @ 8:08 pm

Antes de explicar que es MVCC, hay que aclarar dos conceptos muy importantes: bloqueo pesimista y bloqueo optimista. El bloqueo optimista supone que no se va a hacer nada en el código de la aplicación que imponga explícitamente bloqueos de los recursos cuando se esté trabajando con ellos. Mientras que por otro lado, el bloqueo pesimista supone una intervención por parte de la aplicación como gestor del bloqueo.
Para ser más simples: El enfoque optimista delega en la base de datos el bloqueo y manejo de datos, mientras que el bloqueo pesimista, la aplicación es la encargada de gestionar la concurrencia.
MVCC (Multi version concurrency control) es una técnica de concurrencia optimista en donde ninguna tarea o hilo es bloqueado mientras se realiza una operación en la tabla, porque el otro hilo usa su propia copia (versión) del objeto dentro de una transacción.
Si bien obviamente la implementación interna de este algoritmo es distinta en cada motor de bases de datos, a grandes rasgos se podría decir que el MVCC internamente lo que hace es identificar cada transacción con un numero univoco y a cada registro de la tabla, con un numero de versión. Entonces, cada transacción trabaja con su copia y si se modifica un registro de una tabla, el contador de versión del registro se incrementa. Cuando se comitea, la copia del objeto reemplaza a la que existía en la base de datos. Si 2 transacciones modificaron la misma tabla, se hace un mergue de ambas tablas combinando las últimas versiones de cada registro. Si las 2 transacciones, modificaron exactamente el mismo registro, entonces en ese caso, cuando se commitee, el registro que finalmente queda, corresponde a la ultima transacción en realizar una modificación sobre ese registro.
De esta manera se logra que una lectura no bloquee una transacción de escritura y que una transacción de escritura tampoco bloquee una transacción de lectura. Para ser más claros, cuando hago un SELECT, puedo simultáneamente hacer un UPDATE y cuando hago un UPDATE puedo hacer simultáneamente un SELECT, algo que en el isolation habitual de las bases de datos, no es posible.
Al no existir ningún lockeo ni espera por parte del proceso, en determinadas situaciones concurrentes de fuerte escritura donde la información es modificada frecuentemente y de manera concurrente por muchos usuarios, se logran un gran mejoramiento de la performance del sistema. Es muy habitual usar este tipo modelo de concurrencia para evitar los famosos “deadlocks” que suelen ocurrir en bases de datos con mucha demanda.

Sin embargo, MVCC no debe ser usado en cualquier ocasión, ya que tiene un overhead muy importante. Todo el manejo de versionado tiene un alto costo, ya que las versiones de registros se copian y almacenan en tablas o estructuras físicas temporales que luego se descartan (por ejemplo, para guardar las copias, SQL Server usa la tempdb, mientras que Oracle usa el rollback segment). En SQL Server este overhead es un poco alto, por lo tanto solo en escenarios específicos conviene usar este modelo de concurrencia. A medida que las operaciones de escritura sobre la base de datos cobran relevancia sobre las operaciones de lectura, y por otro lado, nuestras lecturas son largas, los beneficios de usar MVCC aumentan, ya que reducimos los bloqueos.
Es fundamental la existencia de un alto grado de paralelismo en nuestra aplicación. MVCC es por definición, un modelo muy escalable y donde mejor se ven sus ventajas, es en escenarios de alta demanda.

Escenarios típicos:
• Aplicaciones que procesan transacciones en línea en gran escala.
• Aplicaciones mixtas que usan reportes contra datos sobre tablas en línea.
• Aplicaciones donde se ejecutan consultas largas mientras que simultaneamente se realizan operaciones sobre ella.

Escenarios donde no generalmente no debería usarse MVCC:
• Aplicaciones de datawarehousing que extraen información sobre tablas históricas donde no hay escritura.
• Aplicaciones que procesan datos en línea con poca concurrencia.
• Aplicaciones realtime, donde la velocidad es mas importante aun que la consistencia de información.

Todas las bases de datos modernas implementan actualmente este algoritmo de concurrencia optimista. En SQL Server se llama SNAPSHOT ISOLATION, en MySql se llama InnoDB Multi Version, en Firebird MGA y en Oracle y PostgreSQL, simplemente MVCC.

Links:
http://msdn.microsoft.com/en-us/library/ms345124(v=sql.90).aspx
http://www.codinghorror.com/blog/2008/08/deadlocked.html
http://www.rtcmagazine.com/articles/view/101612

Crea un blog o un sitio web gratuitos con WordPress.com.