Esta web utiliza cookies, puedes ver nuestra política de cookies, aquí Si continuas navegando estás aceptándola

MySQL avanzado Vol1


Mysql es una base de datos muy compleja y altamente configurable, en este primer artículo vamos a explicar solo los conceptos básicos sobre el funcionamiento de esta y algunas ideas comunes entre las diferentes bases de datos SQL.

MySQL se compone de 5 partes bien diferenciadas:

  • Gestor de conexiones: Se encarga de la gestión de las conexiones así como de la autenticación
  • Cache: Si una query es exactamente igual a una anteriormente cacheada esta es servida directamente.
  • Parser: Genera una estructura de la query
  • Optimizador: Reescribe la query en caso de ser necesario para obtener un mayor rendimiento
  • Engine: Motor encargado del funcionamiento interno de la base de datos

 

El mayor problema existente es el bloqueo de datos entre querys, es decir la concurrencia. Esto se solventa mediante el bloqueo de los datos que pueden ser a diferentes niveles:

  • Bloqueo de la tabla: Menor concurrencia y proceso muy liviano.
  • Bloqueo de la fila: Mayor concurrencia a cambio de una mayor capacidad de procesamiento.

 

No se puede explicar el funcionamiento de una base de datos sin llegar a las transacciones, estas no son mas que un conjunto de operaciones que forman una sola unidad, es decir o se ejecutan todas y cada una de ellas o no se aplica ninguna, un clásico es el ejemple del banco, imaginemos el siguiente escenario, un cliente tiene dos cuentas bancarias y decide transferir 100$ de una de ellas a la otra, los pasos a seguir serían:

  • Comprobar en cuenta1 que saldo >= 100$
  • Retirar 100$ de la cuenta1
  • Ingresar en cuenta2 100$

 

Si alguno de estos pasos sale mal por alguna razón todos los cambios deben deshacerse de forma segura dejando ambas cuentas en el estado inicial antes de comenzar la operación, a esto se le llama rollback y el conjunto de las operaciones anteriores transacción.

Para MySQL las operaciones serían del siguiente modo:

  • START TRANSACTION
  • SELECT saldo FROM cuenta1 WHERE cliente=001;
  • UPDATE cuenta1 SET saldo = saldo - 100 WHERE cliente=001;
  • UPDATE cuenta2 SET saldo = saldo + 100 WHERE cliente=001;
  • COMMIT;

Commit indica fin de la transacción, si no llega al commit se realizaría un rollback revirtiendo todos los cambios realizados desde que se inició la transacción, si la base datos no soportase transacciones podrían ocurrir autenticos desastres ya que se podría perder el dinero del cliente, imaginemos que ocurre algún problema en el servidor justo cuando se retira el dinero de la cuenta1, no se llegaría a ingresar en cuenta2 y el dinero se habría perdido, mediante transacciones se revertirían los cambios hasta el inicio de la transacción dejando las dos cuentas en el estado en el que se encontraban inicialmente.

NOTA: No es recomendable gestionar como deben realizarse las transacciones ni los bloqueos a no ser que se sepa exactamente lo que se está haciendo y que implicaciones conlleva, es preferible delegar en el engine para esas tareas.

 

La mayoría de bases de datos cumplen lo que se llama ACID:

  • Atomicity: Las transacciones son consideradas una única unidad, o se ejecutan todas las operaciones o ninguna.
  • Consistency: La base de datos no puede acabar en un estado inconsistente, esto habría ocurrido en el caso anterior si no soportase transacciones la DB y se produjese algún problema.
  • Isolation: Las transacciones no pueden ver de forma general los cambios realizados por otras hasta que se ejecute el COMMIT, en nuestro ejemplo si alguna transacción consultase el saldo de cuenta1 entre el primer update y el segundo no vería la reducción del saldo ya que no se ha ejecutado el COMMIT.
  • Durability: Una vez commiteados los cambios deben ser permanentes.

 

Existen diferentes niveles de aislamiento:

  • READ UNCOMMITED: Las transacciones pueden leer datos que no se han commiteado, suelen llamarse lecturas sucias, este modo de operar es realmente peligroso y el aumento de rendimiento en cuanto a otros niveles es inapreciable.
  • READ COMMITED: Solo se leen datos ya commiteados, pero en una misma transacción se pueden leer dos valores distintos de las mismas filas ya que los datos pueden haber sido alterados por otras transacciones que se iniciaron antes de la transacción actual y se comitearon mientras la actual está consultando datos.
  • REPETEABLE READ(default MySQL): Con este método cualquier número de lecturas permanecerán igual en posteriores lecturas dentor de la misma transacción. En este método pueden aparecer lecturas fantasma, esto quiere decir que se puede elegir un rango de filas dentro de una transacción, desde fuera de la transacción se insertan registros en este rango y se lee el mismo rango de nuevo desde dentro de la transacción, los valores cambiarán debido a la inserción.
  • SERIALIZABLE: Cada vez que se lee/escribe se bloquea, la forma mas segura de operar pero también la mas lenta.

 

Se puede cambiar de forma global en el my.cnf o en la sesión actual mediante:

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

 

Uno de los mayores problemas en las bases de datos son los temidos deadlocks, estos consisten en bloqueos que permanecen mientras esperan que otro recurso sea liberado, puede darse el caso en el que una transacción está esperando a que un recurso A esté libre y mientras espera mantiene B bloqueado, en cambio la transacción que mantiene A bloqueado lo está haciendo porque espera que B sea liberado para avanzar, como podemos ver es un bucle infinito en el que nunca se liberan los recursos, InnoDB resuelve este tipo de conflictos ejecutando un rollback de la transacción que menos bloqueos mantiene.

MySQL por defecto opera en modo autocommit: cada operación es considerada como una transacción.

mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT';
mysql> SET AUTOCOMMIT = 1;

 

Una opción interesante para hagilizar las transacciones es Transaction Logging, de este modo cuando se genera un cambio no se escribe en el fichero correspondiente de la base de datos si no que se escribe en un fichero de logs, las escrituras en este serán secuenciales ya que se escriben tanto los cambios de la base de datos A como la B .... N. Se obtiene un mayor I/O escribiendo en un fichero continuo en el disco que ir saltando de fichero en fichero, si ocurriese cualquier problema como la perdida de suministro electrico se leería del log y se aplicaría a los ficheros de la base de datos. El fichero de logging es de un tamaño determinado, como mas grande con menos frecuencia se tendrá que buscar y escribir en los ficheros de DB pero hay que tener en cuenta que como mas grande mas tiempo llevará el rearranque de MySQL ya que hasta que no haya escrito los cambios en los ficheros finales no arrancará.

 

NOTA: Mezclar motores transaccionales con no transaccionales (InnoDB-MyISAM) puede resultar muy peligroso ya que si ocurre algún error y hay que hacer un roll-back solo se podrá deshacer de las bases de datos InnoDB.

Los unicos engines propios de MySQL con soporte para transacciones son:

  • InnoDB
  • NDB Cluster

 

MVCC-multiversion concurrency control:

MySQL además de utilizar bloqueos a nivel de tabla y filas emplea MVCC para obtener unos niveles de concurrencia mayores, cada engine implementa MVCC de un modo determinado, vamos a explicar como lo hace InnoDB.

Hay un contador que se incrementa en cada transacción, además en cada fila hay dos valores ocultos, el de creación de la fila y el de borrado, según la operación a realizar se ejecutarán unas comprobaciones u otras:

  • SELECT: La versión de las filas debe ser <= que el contador de transacción actual(la fila se creó antes de ejecutar la transacción). El número de versión de borrado debe ser nulo o >= que el contador actual(la fila todavía existe o se ha borrado después del inicio de la transacción actual).
  • INSERT: Se inserta el número de versión en la fila nueva.
  • DELETE: Se actualiza la versión del campo delete con el número de transacción actual.
  • UPDATE: Copia la fila original pero insertando el contador de transacción actual como versión de la nueva fila y actualiza la fila vieja con el valor de delete al número actual de transacción.

 

El resultado final es que la mayoría de select no precisan de bloqueo de ninguna fila, por contra se deben hacer mas comprobaciones y almacenar mas información.

MVCC solo es compatible con los siguientes niveles de aislamiento:

  • REPETEABLE READ
  • READ COMMITTED

 

Con esto espero que hayan quedado claros los conceptos básicos sobre bases de datos, mas adelante nos adentraremos en temas mas complejos como engine, realizar profiling de la db...


Autor: Kr0m -- 08/12/2014 23:12:00