Bienvenidos sean a este post, hoy veremos que es la consistencia de datos.
Cuando hablamos de consistencia de datos nos referimos a la lectura de los datos en una transaccion y como InnoDB o cualquier motor de almacenamiento nos garantiza la misma, la consistencia en los queries es determinada por el nivel de la transaccion usando a la opcion WITH CONSISTENT SNAPSHOT para START TRANSACTION, a las opciones LOCK IN SHARE MODE o FOR UPDATE para SELECT, con estos argumentos podemos garantizar una buena lectura de los datos y al mismo haciendo que las aplicaciones funcionen correctamente, pero si nos relajamos podemos caer en la concurrencia que ya iremos viendo de que se trata.
Comencemos a hablar sobre los tipos de lecturas, la primera es la lectura no repetible y esta se considera asi cuando al repetir el mismo query dentro de la misma transaccion sin modificar los datos dentro de la transaccion devuelve diferentes resultados, esto ocurre porque la transaccion actual no esta completamente aislada de los cambios solicitados por otras conexiones, esto mejora la performance donde hay concurrencia pero los desarrolladores deben tenerlo en cuenta y en caso de derivar en errores se debe evitar, para hacer una consulta repetible se debe usar los mecanismos de lecturas consistentes y lecturas de bloqueo, los bloqueos de la siguiente clave (next-key lock) tambien nos garantizan una proteccion contra la insercion de valores en un rango informado despues del query, si no se utiliza nada de esto la lectura es no repetible.
Con los bloqueos de la siguiente clave evitamos un inconveniente llamado lineas fantasmas, es cuando efectuamos una consulta (query) y en medio de la misma se efectua una actualizacion en la cual nos puede modificar la misma, vamos a suponer que tenemos la condicion de un query:
WHERE columna BETWEEN 10 AND 20
Por medio de esta condicion el query devolvera los valores 10, 15 y 20, pero si otra conexion agrega un nuevo valor 13, si la primera conexion efectua nuevamente el query aparecera este nuevo valor y esto es lo que llamamos una linea fantasma, aunque si la columna esta indexada InnoDB procede a usar el bloqueo de siguiente clave, la segunda conexion aun podra realizar la insercion de este nuevo valor inmediatamente y no tendra que esperar hasta que la otra transaccion termine pero la nueva linea no sera visible para la primera transaccion lo cual nos garantiza un buen aislamiento entre las distintas transacciones.
Otra tema que tenemos son las lecturas consistentes, estas ocurren cuando leemos desde una tabla que es consistente en la transaccion actual, esta no utiliza bloqueos en cambio cuando la accedemos por primera vez por la transaccion actual esta crea una instantanea (snapshot), dicha instantanea representa los datos de la tabla en un punto exacto de tiempo, cambios solicitados por otras conexiones no afectan a la instantanea incluso luego de un COMMIT, aunque esta instantanea solo se ve afectada cuando se ejecute un comando DML, como por ejemplo un INSERT, desde la transaccion actual y a pesar de esto otras transacciones no seran notificadas de este cambio. Sin embargo, cuando ejecutamos un COMMIT se realizan los cambios en la instantanea, son copiados en la tabla real y se vuelven visible para todas las conexiones, y los cambios realizados por otras conexiones tambien se vuelven visitble para la conexion actual.
Para entender este concepto vamos a probar con un ejemplo, en una base cualquiera vamos a crear la siguiente tabla:
create table p (a int unique) engine = InnoDB;
Una vez creada la tabla, el siguiente paso sera iniciar la accion de utilizar transacciones con lectura consistente mediante el siguiente comando:
start transaction with consistent snapshot;
Y por ultimo agreguemos un valor a esta tabla:
insert into p values (1);
Ahora abran otra terminal y desde esta usen otra conexion al servidor de mariadb, con la nueva conexion agreguen un nuevo valor a la creada anteriormente:
insert into p values (2);
Con nuestro nuevo valor agregado pasemos a hacer una consulta sobre la misma:
MariaDB [curso]> select * from p;
+------+
| a |
+------+
| 2 |
+------+
1 row in set (0.003 sec)
MariaDB [curso]>
En este caso podemos ver como solamente se ve el valor que agregamos en la nueva terminal, volvamos a la conexion anterior y ejecutemos lo siguiente:
MariaDB [curso]> commit;
Query OK, 0 rows affected (0.011 sec)
Aqui le ordenamos que ejecute lo anterior y lo actualice en nuestra tabla, si volvemos a hacer el query en cualquiera de las dos terminales veremos lo siguiente:
MariaDB [curso]> select * from p;
+------+
| a |
+------+
| 1 |
| 2 |
+------+
2 rows in set (0.001 sec)
MariaDB [curso]>
Con esto podemos observar que al trabajar con las instantaneas, nos permite manipular la tabla sin afectar a las demas hasta que subamos los cambios realizados por medio de COMMIT, sin la opcion de la instantanea o snapshot cada cambio que realicemos en las distintas conexiones se vera automaticamente reflejado.
Por ultimo tenemos los bloqueos de lecturas, estos son otra forma de garantizar la consistencia de datos y es mas fuerte que la anterior porque los datos y otras conexiones no podran tener acceso a las mismas o solamente podran leerlas hasta que la transaccion termine, para entender este concepto vamos a hacer un ejemplo aprovechando la tabla anterior, tambien utilizaremos dos conexiones, para ello primero agregaremos un nuevo valor desde cualquiera de las dos ventanas:
insert into p values (3);
En la misma conexion vamos a ejecutar el siguiente comando:
start transaction;
La cual nos dara la posibilidad de poder generar el bloqueo, para ello usaremos el siguiente comando:
select * from p where a = 1 lock in share mode;
Esto nos devolvera el valor en p donde se cumple la condicion pero nos bloquea la lectura de ese registro, para comprobarlo vamos a ir a la otra conexion, en caso de haberla cerrado abran otra terminal y ejecuten otra conexion al servidor, y desde esta ejecutemos el siguiente comando:
MariaDB [curso]> select * from p;
+------+
| a |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.000 sec)
MariaDB [curso]>
Aqui hacemos una consulta donde vemos que nos devolvio todos los valores de la tabla, probemos de ejecutar este comando:
MariaDB [curso]> update p set a = 300 where a = 3;
Query OK, 1 row affected (0.046 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [curso]>
En el ejemplo actualizamos el valor de a donde vale 3 a 300, vemos como se realizo exitosamente pero intentemos modificar el registro con el valor de 1:
MariaDB [curso]> update p set a = 100 where a = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
MariaDB [curso]>
Como pueden ver no nos permitio modificar ese registro dado que es el unico que bloqueamos, para desbloquearlo reemplacen la seccion LOCK IN SHARE MODE por FOR UPDATE, lo ejecuten y luego vuelvan a ejecutar START TRANSACTION para que tome efecto el desbloqueo.
Nota: Por lo menos a mi me funciono asi...
En resumen, hoy hemos hablado sobre la consistencia, como es, para que sirve, como nos ayuda, los distintos tipos de bloqueos que podemos usar, algunas consecuencias de no usarlo y unos ejemplos para verlos en accion, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
