El teorema CAP en base de datos

Éste post me parece muy importante para poder entender como funcionan y hacer grandes sistemas pensados para escalar, sea por tráfico, por multi-sitio, multi-país, y más… todos estos sistemas distribuidos presentan la misma particularidad, y en el año 2000 un señor llamado Eric Brewer, pudo definir tres importantes propiedades y desarrollo un teorema. El se dio cuenta que mientras más aplicaciones basadas en la WEB existan, menos debemos preocuparnos por la consistencia de los datos, si queremos alta disponibilidad de nuestras aplicaciones entonces no podemos garantizar la consistencia de los datos.

El teorema CAP, también llamado formalmente Teorema de Brewer, dice que un sistema de datos distribuido pude asegurar dos de estas tres propiedades: Consistencia, Disponibilidad y Tolerancia al particionado. Bien, que significa cada una:

  • La consistencia (Consistency), Todos los nodos deben ver los mismos datos al mismo tiempo, esto quiere decir que; cualquier cambios en los datos se debe aplicar en todos los nodos, y cuando se recupere el dato tiene que ser el mismo en todos los nodos. Esto se le llama consistencia atómica, y se consigue replicando la información en todos los nodos.
  • La disponibilidad (Availability), Cada petición en un nodo debe recibir y garantizar una confirmación si ha sido resuelta satisfactoriamente. En pocas palabras, se debe leer y escribir en todos los nodos.
  • La tolerancia al particionado (Partition Tolerance), El sistema debe funcionar a pesar de que haya sido dividido por un fallo de comunicación, garantizando la disponibilidad a pesar que un nodo se separe del grupo sin importar la causa.

CAPTheorem

El teorema solo nos puede garantizar las siguientes combinaciones:

  • CP (Consistency & Partition): El sistema aplicara los cambios de forma forma consistente y aunque se pierda la comunicación entre nodos ocacionando el particionado, no se asegura que haya disponibilidad.
  • AP (Availability & Partition): El sistema siempre estará disponible a las peticiones aunque se pierda la comunicación entre los nodos ocacionando el particionado, y en consecuencia por la perdida de comunicación existirá inconsistencia porque no todos los nodos serán iguales.
  • CA (Consistency & Availability): El sistema siempre estará disponible respondiendo las peticiones y los datos procesados serán consistentes. En este caso no se puede permitir el particionado.

La correcta decisión de que combinación necesitamos depende de nuestras necesidades de negocio. Nunca olvide que lo más importante en una base de datos relacional es la Consistencia. Conociendo el teorema CAP, nos puede ayudar aún más para saber que Sistemas de Base de Datos debemos escoger, si un SQL o un NoSQL. Si queremos profundizar más en el tema, recomiendo este post.

Niveles de aislamiento en Base de Datos

El aislamiento es una parte importante de la propiedad ACID que garantiza que las transacciones sean fiables. Esto permite que las transacciones que se ejecutan simultáneamente no interfieran con otras, garantizando la integridad de los datos, al no existir aislamiento en una transacción podría modificar los datos que otra transacción está leyendo, por lo que se crea una inconsistencia cuando se crean datos.

Ahora que entendemos que es el aislamiento en términos generales, vamos a conocer cuales son los niveles de aislamiento, estos determinan como las transacciones se comportan con otras transacciones, es como ser más o menos restrictivo. Escoger cual es el mejor nivel depende de las necesidades de la aplicación, primero debe entender cuales son los beneficios y consecuencias de cada una de ellas.

InnoDB soporta los cuatro niveles de aislamiento estándar, a continuación se describen los estándares usados en muchos maneadores de bases de datos relacionales, no solo se aplica para MySQL, sino para Oracle, SQL Server y PostgreSQL.

  • READ-UNCOMMITTED (LECTURA NO CONFIRMADA): La ejecución de la instrucciones SELECT se llevan a cabo sin bloqueo, puede utilizar una versión antigua de una fila que ya no existe. Por lo tanto, el uso de este nivel no tiene aislamiento y no garantiza la trasacción, tales lecturas no son consistentes. Esto también se le llama una lectura sucia. De lo contrario, este nivel de aislamiento funciona igual que “READ COMMITTED”.
  • READ-COMMITTED (LECTURA CONFIRMADA): Está es la opción favorita de Oracle y la que se recomienda para muchos casos. Con éste nivel de aislamiento se evita el fenómeno de la lectura sucia, porque los cambios no confirmados no son visibles para cualquier otra transacción, hasta que se confirme el cambio. Dentro de este nivel de aislamiento, cada SELECT utiliza su propia instantánea de los datos que se confirmo (commit) antes de la ejecución de la instrucción SELECT. Ahora, ya que cada SELECT tiene su propia instantánea, por lo que el mismo SELECT cuando se ejecuta varias veces durante la misma transacción podría regresar diferentes conjuntos de resultados. Este fenómeno se le llama lectura no repetible.
  • REPEATABLE-READ (LECTURA REPETIBLE): Lee todos los datos de forma coherente dentro de la misma transacción, es como hacer una foto instantánea de los datos desde la primera lectura. Con este nivel de aislamiento se evita el fenómeno de la lectura no repetible. Este nivel de aislamiento devuelve el mismo conjunto de resultados para diferentes SELECT dentro de una misma transacción. Una instantánea de la SELECT se toma la primera vez que se ejecuta durante la transacción y la misma instantánea se utiliza dentro de la transacción cada vez que se ejecuta el mismo SELECT. Una transacción que se ejecuta en este nivel de aislamiento no tiene en cuenta los cambios de los datos realizados por otras transacciones, independientemente de si los cambios se han confirmado (commit) o no. Esto asegura que las lecturas siempre son consistentes (repetible). Este nivel de aislamiento es el predeterminado para InnoDB. Aunque este nivel de aislamiento resuelve el problema de lectura no repetible, pero hay otro fenómeno fantasma.
  • SERIALIZABLE: Con éste nivel de aislamiento se evita el fenómeno de fantasma. Coloca un bloqueo de rango en el conjunto de datos, cuando las transacciones se ejecuta en este nivel de aislamiento se bloquean todos los registros y recursos que se tiene acceso, así bloquea todo cambio, impidiendo que otros usuarios actualizar o insertar filas en el conjunto de datos hasta que la transacción se ha completado. Este nivel de aislamiento es el más fuerte posible.

Cada uno de estos niveles de aislamiento tienen sus beneficios y consecuencias, vamos a explicar cada situación:

Se puede evitar el nivel de aislamiento SERIALIZABLE

Si, Como vemos es el más restrictivo sacrificando consistencia por rendimiento, un nivel de aislamiento parecido es REPEATABLE-READ, tiene una forma de bloqueo especial que ayuda a evitar los fenómenos fantasmas, podemos asegurarnos de hacer un buen bloqueo sin fenómenos fantasmas usando la estructura SELECT con FOR UPDATE o LOCK IN SHARE MODE. El bloqueo compartido permite a otras transacciones leer los registros examinados, pero no actualizar o borrar mientras que otra transacción esté interviniendo.

Realicación y Niveles de aislamiento

El tipo de replicación predeterminada en MySQL es la replicación basada en declaraciones [Statement Based Replication (SBR)], mantiene los cambios de los datos por la re-ejecución de las sentencias SQL ejecutadas en el maestro a los esclavos. Esto requiere que el nivel de aislamiento sea más estricto, de modo que los cambios de datos deben ser consistentes, de tal manera que el mismo SQL debe garantizar los mismos cambios en el esclavo. Cuando usamos este tipo de replicación puedes configurar el nivel de aislamiento en SERIALIZABLE o REPEATABLE-READ. Si usas la versión 5.1 o superior de MySQL debes usar el nivel de aislamiento READ-COMMITTED para garantizar la consistencia de datos.

Los niveles de rendimiento y aislamiento

Como hemos podido ver, mientras más bloqueos menos rendimiento y mayor consistencia, la intención es buscar un equilibrio según nuestras necesidades, ya que los extremos son perjudiciales. Usar el nivel de aislamiento SERIALIZABLE no es para nada favorable a nivel de rendimiento, pero usar READ-UNCOMMITTED mejora mucho el rendimiento pero no garantiza la integridad de los datos, por lo que nos quedan las dos opciones del medio.

Presentaciones del Curso de SQL de PostgreSQL

Hace un tiempo cree una serie de presentaciones en pdf para usarlas en la clase de introducción a SQL en PostgreSQL para la materia de Base de Datos II en la Universidad Nueva Esparta, mucho de mis alumnos todavía aún me lo piden luego de un tiempo que no doy la materia. Éste material fue basado en el curso de Introducción a SQL de Oracle, el cual tuve la oportunidad de presenciar y quede fascinado, quise implementar con Oracle de Venezuela en su momento para los estudiantes, pero la empresa que representa a Oracle nunca mostro interés en apoyar y fomentar la educación de sus productos en la Universidad, por ende me puse a traducir, adaptar dicho curso a PostgreSQL y viva el OpenSource una vez más. Aquí se los entrego a todos los interesados bajo licencia de Creative Commons.

Espero que las disfruten!.

¿Cómo guardar y mostrar una imagen desde PostgreSQL con PHP?

Para poder alojar una imagen o archivo dentro de una Base de Datos, debemos definirle un tipo de dato especial para este formato, en el caso de PostgreSQL usamos el tipo de dato bytea. Para poder leer y almacenar una foto u archivo X, el trato en este campo no tiene ninguna dificultad, por que se trata como cualquier otro valor a la hora de hacer un INSERT o UPDATE. A continuación se muestra la receta ideal mediante un ejemplo con una tabla y los códigos necesarios en PHP.

Estructura de la tabla: El siguiente código SQL, muestra cual es el tipo de dato que debe tener una tabla que aloja imágenes, en este caso existe un campo llamado foto de tipo bytea.

CREATE TABLE tbl_usuarios
(
  id serial NOT NULL,
  nombre character varying(60) NOT NULL,
  apellido character varying(60) NOT NULL,
  foto bytea
)

Subir una imagen: El siguiente archivo llamado save.php, es el encargado de subir el archivo de una imagen a la base de datos.

$data = file_get_contents('1.jpg');
$image = pg_escape_bytea($data);
$conn = pg_connect("user=DB password=admin dbname=DB host=localhost");

pg_query($conn, "UPDATE usuarios SET foto = '{$image}' WHERE id = 1");
pg_close($conn);

Cargar una imagen: El siguiente archivo llamado display.php, es el encargado de buscar y mostrar la imagen alojada en la base de datos, el resultado de este archivo es el de convertirse una imagen, y es llamado desde el código HTML, por eso se modifica el header.

$conn  = pg_connect("user=DB password=admin dbname=DB host=localhost");
$query = pg_query($conn, "SELECT foto FROM usuarios WHERE id = 1");
$row   = pg_fetch_row($query);
$image = pg_unescape_bytea($row[0]);

header("Content-type: image/jpeg");
echo $image;

pg_close($conn);

Nota: Recordar de mantener el mismo formato que se guarda y el header al leer el archivo.

Mostrar una imagen: El siguiente archivo llamado foto.html, contiene los tags necesarios para mostrar la imagen, podemos observar que se llama al archivo anterior y esta definido en la propiedad que indica la ruta de la imagen.

<h1>Foto:</h1>
<img src="display.php"