Buscar y matar transacciones inactivas. Evitando problemas mayores

InnoDB se convirtió en el motor de almacenamiento por defecto en MySQL 5.5. Era un paso lógico. Es un motor transaccional, escalable y con un rendimiento superior a MyISAM. Hay que recordar esa frase tan mítica... MyISAM es el lugar donde los datos van para morir. Pero ese cambio ha traído algunas consecuencias. Malas prácticas que en MyISAM no tenían ningún efecto visible en InnoDB pueden causar graves problemas.

Uno de ellos es dejar transacciones abiertas y olvidadas. Y no hablo de minutos o horas, si no días e incluso he llegado a ver semanas. Cuando te contactan y te dicen que hay un problema con alguno de estos síntomas:

  • ibdata1 no para de crecer, nos vamos a quedar sin espacio en disco
  • la base de datos funciona muy lenta y tenemos constantes errores de tiempo de espera agotado esperando bloqueos de filas

Ya casi tenemos claro donde está el problema. Un SHOW ENGINE INNODB STATUS\G nos mostrará las transacciones activas y la última de todas será posiblemente la causante. Estará en estado ACTIVE y el número de segundos será posiblemente exageradamente grande.
---TRANSACTION 7941BDFEF, ACTIVE 1256288 sec

Esto causa diferentes problemas en nuestra base de datos. En primer lugar InnoDB necesita mantener un snapshot consistente para esa transacción, esto es, la transacción debe ver la base de datos tal y como era cuando comenzó. En el ejemplo que vemos antes, la transacción se inició hace 14 días. Por lo tanto, InnoDB debe mantener una copia original de todas las filas que se han modificado desde entonces para que la vista transaccional sea consistente. Esto se hace mediante las páginas undo que se guardan en el tablespace compartido ibdata1. Por lo tanto, ibdata1 crecerá de tamaño de forma descontrolada. Y como bien sabemos, los tablespaces no se pueden reducir de tamaño ni aún borrando datos.

Otro de los problemas asociados son los bloqueos. Si la transacción que lleva 14 días abierta tiene bloqueos en filas, causadas por un UPDATE/DELETE, ninguna otra transacción podrá acceder a dichas filas durante 14 días. Y eso se visualizará como un problema de rendimiento, ya que el timeout de espera por defecto cuando se quiere acceder a una fila es de 50 segundos. Imagínate añadir una latencia de 50 segundos a cada petición a base de datos de tu web, suena a catástrofe.

La solución a este problema es sencilla, tienes que matar el thread de dicha transacción con un KILL. Esto lanzará un rollback y todos los recursos quedarán libres. Como solucionarlo es fácil, vamos a hablar de como evitarlo. En este caso tenemos tres formas.

1- "Kill Idle Transaction" de Percona Server

Percona Server incluye una característica que te permite matar de forma automática todas las transacciones que estén en idle por un número de segundos mayor que el indicado.

innodb_kill_idle_transaction = n

http://www.percona.com/doc/percona-server/5.5/management/innodb_kill_idle_trx.html

2- pt-kill de Percona Toolkit

pt-kill es una herramienta muy sencilla. Le indicas que tipo de consultas deseas matar y pt-kill analizará la salida de "SHOW PROCESSLIST" y matará aquellos procesos que cumplan las características que indiques. Funciona en modo daemon. Es bastante flexible, no solo te permite matar aquellos procesos idle si no también los que están en otros estados como Copying to tmp table on disk, Sending data, etc. Para que no se escape ni una.

http://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html

3- Alertas de nagios con Percona Monitoring Plugins

Percona Monitoring Plugins son una serie de herramientas y scripts para Nagios y Cacti. Dentro de los scripts de nagios incluye uno que analiza las transacciones activas y te puede avisar en caso de que una supere un tiempo máximo de inactividad que tu indiques.

http://www.percona.com/software/percona-monitoring-plugins

Bueno, ahora si el desarrollador mete la pata ya no tienes excusa. ¡Estabas avisado! Las transacciones que sean lo más cortas y sencillas posibles :)

Rotado de logs en MySQL y los problemas de rendimiento

MySQL provee muchos tipos de logs. Tenemos el log binario, el log general y el log de queries lentas. Son muy útiles y necesarios tanto para analizar un problema o montar una replicación pero hay que tener cuidado con el tamaño que pueden llegar a alcanzar. Para los logs binarios existe una serie de parámetros que nos permite limitar su tamaño e ir rotándolos eliminando los antiguos, cosa que no hay para los otros dos tipos de log.

Un cliente me contactó porque al desactivar el log general MySQL se quedaba parado durante varios minutos no aceptando más consultas y causando por lo tanto caída del servicio. Lo primero que te viene a la cabeza es... "¿Cómo cojones desactivar un log puede tirar un servidor abajo?" :)

Las razones son dos principalmente:

1- Debido a la forma en la que MySQL cierra el fichero.
2- Por el rendimiento del almacenamiento.

Para entender mejor el primer punto debemos analizar que pasa en MySQL 5.6 internamente cuando desactivamos el log general. Con strace lo sabremos:

[pid  3878] read(40, "\3set global general_log=0", 25) = 25
[pid  3878] setsockopt(40, SOL_SOCKET, SO_RCVTIMEO, "\36\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0
[pid  3878] time(NULL)                  = 1386847723
[pid  3878] write(41, "131212 11:28:43\t    3 Query\tset "..., 53) = 53
[pid  3878] fsync(41)                   = 0
[pid  3878] close(41)                   = 0

Vemos como ejecuto el set global para desactivarlo, pero MySQL no simplemente cierra el log, si no que hace una llamada fsync antes de dicho cierre. Fsync es, utilizando una explicación básica, una llamada al sistema operativo para que escriba físicamente los datos pendientes al disco duro. Como os podréis imaginar, esto es una operación costosa y en algunos casos bloqueante, ya que un disco duro sólo puede atender una llamada de fsync al mismo tiempo. Y aquí radica el segundo problema. Generalmente la gente escribe los logs en sistemas de almacenamiento "baratos" ya que lo que importa es la capacidad, no el rendimiento. Por lo tanto, generalmente no hay una controladora RAID detrás con caché para estos fsync lo que hace que el simple proceso de parar un log pueda tirar abajo tu base de datos.

Volviendo al caso del cliente. El log terminaba teniendo un tamaño de 30GB lo cual no tiene porque ser un problema ya que es escritura secuencial, pero es un problema en cuanto se produce una llamada fsync y todos los datos en la caché deben volcarse al disco duro. Entonces, es cuando llegan los problemas si el rendimiento no es el correcto. Como el almacenamiento no puede responder a ninguna otra petición durante el fsync, el servicio deja de responder.

Como ya he comentado, no existen formas de rotar estos logs desde MySQL, por lo que el "workaround" se basa sencillamente en crear un script de rotado con logrotate que lo rote cada pocos GB haciendo que el fsync sea lo menos costoso posible y evitando así una caída del sistema. Eso, o comprarte un almacenamiento mejor :)

Así que, cuidado con los logs. No siempre todo es tan simple como parece.

Analizar el uso de memoria de MySQL

Una pregunta muy habitual suele ser:

Mi base de datos tiene 40GB de buffer pool, pero está consumiendo 60GB. ¿Por qué?

Una pregunta sencilla cuya respuesta es por lo general difícil de encontrar. MySQL no nos da mucha información sobre donde se está usando esa memoria. Un problema que se intentará arreglar en el próximo Oracle MySQL 5.7 así que hasta que llegue ese momento toca realizar tareas de investigación. Lo primero y lo lógico es mirar el my.cnf y tener en cuenta estos detalles:

  • Hay 4 buffers que se usan por sesión. Eso quiere decir que no es un valor global, si no que se puede multiplicar por el número de threads que tengas abiertos. read_buffer_size, read_rnd_buffer_size, sort_buffer_size y join_buffer_size son los que tenemos que investigar primero. La recomendación habitual es no cambiar los valores por defecto de dichas variables. La mejora que podríamos tener haciendo dichos buffers más grandes termina convirtiendose en una perdida de rendimiento por el coste que suponen las llamadas malloc para la reserva de memoria. Contra mayor sea la reserva de memoria que hay que hacer mayor será el tiempo que necesite para hacerlo. Teniendo en cuenta que son buffers por thread, a mayor buffer y mayor número de threads, peor rendimiento. Aquí hay un ejemplo que demuestra como uno de ellos, sort_buffer_size, a partir de 440KB no nos da ninguna mejora y que incluso puede degradar el rendimiento. Por lo tanto, buffers por sesión suelen dejarse en sus valores por defecto.

http://www.mysqlperformanceblog.com/2010/10/25/impact-of-the-sort-buffer-size-in-mysql/

También se puede llegar a mejorar el rendimiento cambiando la libreria malloc por defecto por otra con mejor rendimiento y escalabilidad:

http://www.mysqlperformanceblog.com/2012/07/05/impact-of-memory-allocators-on-mysql-performance/

  • El tamaño configurado para las tablas temporales. Por defecto si MySQL necesita una tabla temporal para ejecutar una query la creará primero en memoria. A más queries de ese estilo concurrentes, más gasto de memoria. La solución no es hacer tmp_table_size más grande, sino reescribir la query o añadir los índices necesarios para que no use una tabla temporal.

  • Query cache. Ya hablamos de esta caché aquí:

http://miguelangelnieto.net/?action=view&url=porque-deber%C3%ADas-desactivar-la-query-cache

  • Buffer Pool, uno de los valores más importantes para el rendimiento de nuestra base de datos.

Suma todo y comprueba si da como resultado el número de GB que está siendo utilizado por MySQL. Si no, toca seguir investigando.

El siguiente paso será ver con más detalle la memoria usada por InnoDB. Para ello usaremos SHOW ENGINE INNODB STATUS y analizaremos la sección de buffer pool:

----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 2206203904; in additional pool allocated 0
Internal hash tables (constant factor + variable factor)
    Adaptive hash index 38109920    (35402344 + 2707576)
    Page hash           2213656 (buffer pool 0 only)
    Dictionary cache    78496875    (8851984 + 69644891)
    File system         12794272    (82672 + 12711600)
    Lock system         5323944     (5313416 + 10528)
    Recovery system     0   (0 + 0)
Dictionary memory allocated 69644891
Buffer pool size        131071
Buffer pool size, bytes 2147467264
Free buffers            79043
Database pages          51863
Old database pages      19164
Modified db pages       21
Pending reads 2
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 51854, created 7, written 822
154.24 reads/s, 0.04 creates/s, 2.16 writes/s
Buffer pool hit rate 960 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 51863, unzip_LRU len: 0
I/O sum[0]:cur[189], unzip sum[0]:cur[0]

Ese es la salida de un servidor con Percona Server con 2GB te Buffer Pool, pero en realidad según "top" está consumiendo 4GB. Lo que tenemos que mirar ahí es:

Total memory allocated 2206203904

Como vemos, a pesar de indicar 2GB como buffer pool está utilizando 2.2G. Esto es así porque InnoDB generalmente usará un 10% más de la memoria que tu le indiques, así que recuerda sumar ese 10% cuando configures tus bases de datos.

Dictionary memory allocated 69644891

InnoDB guarda las definiciones de las tablas en memoria, por lo tanto, contra más tablas accedas más memoria usará. Muchas veces, este valor suele ser el causante del uso incontrolado de memoria en aquellos servidores con miles de tablas. En este caso, vemos que está usando poco más de 65MB, así que en este caso no es nuestro problema.

En MySQL 5.5 no hay posibilidad de limitar el uso de memoria para el diccionario de InnoDB pero Percona Server si lo permite. Si tienes un servidor con miles y miles de tablas y eso causa un consumo excesivo de memoria quizás deberías probar:

http://www.percona.com/doc/percona-server/5.5/management/innodb_dict_size_limit.html

Como vemos, seguimos sin llegar a los 4GB. Ya no podemos sacar más información de MySQL, eso es todo lo que nos da. Quedan por ahí otros 1,8GB que no sabemos donde están siendo usados. ¿Cómo averiguarlo? Valgrind nos va a ayudar.

Valgrind es una herramienta de debugging para analizar el uso de memoria y rendimiento de nuestras aplicaciones. Para ello, tendremos que ejecutar mysqld desde valgrind. Los paquetes debuginfo de MySQL deberán estar instalados. Es importante recalcar que valgrind y el uso de paquetes debug puede reducir el rendimiento de MySQL unas diez veces por debajo del valor normal, así que no lo uses en producción.

Ejecutamos valgrind:

valgrind --tool=massif --massif-out-file=/tmp/massif.out --pages-as-heap=yes /home/mysql/5.5/bin/mysqld --defaults-file=/etc/my.cnf --user=mysql &

y simplemente usamos MySQL y esperamos a que el uso de memoria llegue al punto que deseamos. Una vez hecho, paramos normalmente MySQL (no lo matamos) y analizamos el fichero de salida:

ms_print /tmp/massif.out > /tmp/output.txt

y podremos ver algo así en output.txt:

96.51% (68,180,839B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
 ->50.57% (35,728,995B) 0x7A3CB0: my_malloc (in /usr/local/percona/mysql-5.5.28/usr/sbin/mysqld)
 | ->10.10% (7,135,744B) 0x7255BB: Log_event::read_log_event(char const*, unsigned int, char const**, Format_description_log_event const*) (in /usr/local/percona/mysql-5.5.28/usr/sbin/mysqld)
 | | ->10.10% (7,135,744B) 0x728DAA: Log_event::read_log_event(st_io_cache*, st_mysql_mutex*, Format_description_log_event const*) (in /usr/local/percona/mysql-5.5.28/usr/sbin/mysqld)
 | |   ->10.10% (7,135,744B) 0x5300A8: ??? (in /usr/local/percona/mysql-5.5.28/usr/sbin/mysqld)
 | |   | ->10.10% (7,135,744B) 0x5316EC: handle_slave_sql (in /usr/local/percona/mysql-5.5.28/usr/sbin/mysqld)
 | |   |   ->10.10% (7,135,744B) 0x3ECF60677B: start_thread (in /lib64/libpthread-2.5.so)
 | |   |     ->10.10% (7,135,744B) 0x3ECEAD325B: clone (in /lib64/libc-2.5.so)

Como vemos, el 10% de la memoria se está usando en "Log_event::read_log_event", que está relacionado con la replicación de MySQL. El reporte se paró antes se paró antes de tiempo pero aún así un 10% para una función que solo lee eventos del relay log, es mucho. Esperando más tiempo hubiésemos visto como llega al GB de memoria. Acabamos de descubrir un memory leak:

https://bugs.launchpad.net/percona-server/+bug/1042946

Generalmente las funciones de MySQL se explican por si solas con el nombre, si no tendrás que investigar el código. Muchas veces el código está mejor documentado que la propia documentación.

InnoDB Log, que es y como configurarlo

Prácticamente todas las bases de datos tienen algo que generalmente se conoce como REDO LOG. MySQL también lo tiene (ib_logfile*) y hasta donde yo se toda base de datos con propiedades ACID lo tiene. La idea de este post es explicar cual es la utilidad de dicho log y que tamaño deberemos configurar para mejorar el rendimiento de nuestra base de datos.

¿Qué es este log?

Son dos ficheros (o más si tu lo configuras así) que funcionan de forma circular y secuencial. Imaginemos que tenemos A y B. InnoDB empieza escribiendo en A desde el inicio del fichero hasta el final. Una vez terminado con A comienza con B... y una vez terminado B vuelva a A reescribiendo los datos que en el hay. Por lo tanto, independientemente del número de ficheros que tengas configurado en innodb_log_files_in_group este actuará como uno solo. Por esa razón no se suele cambiar innodb_log_files_in_group pero si el tamaño total del log con innodb_log_file_size.

Las filas de nuestras tablas InnoDB se almacenan en páginas de 16KB. Por lo tanto, una página puede tener 1 o más filas y una tabla está dividida en múltiples páginas. Cuando modificamos un grupo de filas, lo que hace InnoDB es:

  1. Guardar la página nueva en el buffer pool (dirty page) dejando la antigua en el tablespace en disco (.ibd).
  2. Escribir el diferencial en el log. A cada escritura se le asigna un número identificativo único conocido como LSN.
  3. Mediante checkpointing terminará volcando la página nueva al disco duro en el tablespace correspondiente. La página de buffer pool pasa a denominarse clean page.

¿Por qué necesitamos el log?

Algunas personas pueden ver raro que se guarde un diferencial de los cambios cuando simplemente puedes escribir la nueva página en el tablespace y ahorrarte el paso intermedio. Estas son las dos principales razones por la que un log es necesario en InnoDB:

1- Mejorar el rendimiento en el almacenamiento de datos:

Históricamente el mayor cuello de botella ha sido el acceso al disco duro, en concreto las escrituras aleatorias. Gran parte de las optimizaciones que incluyen las bases de datos actuales se basan en algoritmos de reordenación de datos para agrupar un grupo de escrituras aleatorias y convertirlas en una única escritura secuencial. En los discos duros mecánicos una escritura aleatoria es mucho más lenta y costosa que una secuencial, por lo que reducir el número de aleatorias es una optimización fundamental en cualquier base de datos.

Las páginas de 16KB están distribuidas por todo el tablespace en el disco duro. Por lo tanto, una simple INSERT/UPDATE/DELETE puede afectar a múltiples páginas y modificarlas puede requerir muchas escrituras aleatorias degradando el rendimiento. Usando el log convertimos dichas escrituras aleatorias en una única secuencial. Además, es una escritura mucho más pequeña ya que solo guardamos un diferencial, haciendo que el rendimiento sea aún mejor.

2- Crash recovery

Como vimos antes los datos que modificamos en la base de datos se aplican en un principio solo en memoria, en el buffer pool, mientras que en el disco duro siguen los datos antiguos. Entonces, ¿qué pasa si el servidor se cae? La lógica dice que todo lo que hay en memoria se pierde, nuestros cambios desaparecerán y MySQL leerá datos antiguos. Aquí es donde entra el log. InnoDB detecta al arrancar que hemos tenido una caida y comenzará el proceso de crash recovery. Este se resume en algo muy sencillo:

Comprueba cual es el último LSN que se ha escrito en los tablespaces en disco, ya que eso marca hasta que punto en concreto los datos están a salvo. Tomando ese LSN como punto de partida comienza a leer todos los diferenciales que tenemos en los logs y aplica los cambios al tablespace, dejando la base de datos en un estado correcto y evitando así pérdidas de información.

¿Cómo podemos optimizarlo para mejorar el rendimiento?

Recordemos que los logs se escriben de forma circular, A->B->A. Cuando InnoDB empieza a reescribir A primero tiene que comprobar si las páginas que va a reescribir se han volcado ya al disco duro. Si reescribieramos esos diferenciales sin haber volcado primero las páginas no podriamos recuperarlas jamás después de una caída. Entonces, aquí queda claro que contra más grande sean estos logs, más tiempo podrá mantener las páginas modificadas en memoria sin necesidad de hacer un volcado, reduciendo de forma considerable las escrituras aleatorias y alargando el tiempo entre checkpoints.

La regla general suele ser que estos logs deben ser capaces de guardar al menos una hora de transacciones. La forma de calcularlo es sencilla:

mysql> pager grep sequence
PAGER set to 'grep sequence'
mysql> SHOW ENGINE INNODB STATUS\G SELECT SLEEP(60); SHOW ENGINE INNODB STATUS\G
Log sequence number 69184283751
Log sequence number 69195423321

Ese es el LSN del que hable anteriormente, el número único que identifica cada cambio. Lo que hacemos es comprobar el LSN dos veces con un tiempo de espera de 1 minuto. Haciendo un cálculo rápido vemos que:

69195423321 - 69184283751 = 11139570 * 60 = 668374200 = 637MB

Por lo tanto, para tener una hora de transacciones tendremos que tener al menos 637 megabytes de logs. Si tenemos el número de log files por defecto a 2, innodb_log_file_size tendría que ser 335544320 bytes (320MB).

Es importante recalcar que configurar un log de tamaño adecuado ayudará muchísimo al rendimiento de la base de datos en cuanto a almacenamiento se refiere, pero también hay que tener en cuenta su segunda utilidad. Contra más grande sea el log, más largo será el proceso de crash recovery.

Por último, el tamaño máximo del log en MySQL está limitado a 4GB mientras que en Percona Server este límite en máquinas de 64bits se aumenta hasta los 8 millones de TB.

Mi opinión personal sobre MariaDB y el FUD

Disclaimer típico: esto es una opinión mía, completamente personal y no la opinión de la empresa para la que trabajo.

Últimamente mucha gente habla de MariaDB, generalmente por noticias estilo:

RedHat will switch from Oracle MySQL to MariaDB. Por cierto, desmentida por Red Hat, ya que ellos aún no han tomado ninguna decisión.

Google swaps out MySQL, moves to MariaDB. ¿Razones técnicas? Nadie lo sabe, por lo que posiblemente sea un "vamos a tocar los huevos a Oracle como ellos a nosotros con las patentes de Java":

Ahora, expliquemos un poco la historia de forma resumida.

  • Monty crea MySQL en 1996.
  • A partir de 3.23 InnoDB de Innobase se añade como motor de almacenamiento transaccional a InnoDB. Hasta este momento MySQL no tenía transacciones ni propiedades ACID.
  • En 2005 Oracle compra Innobase, por lo que InnoDB pasa a formar parte de Oracle y como consecuencia, gran parte de MySQL YA ES de Oracle. Se empiezan a escribir los típicos artículos explicando como esto traerá el apocalipsis. Se empiezan a crear nuevos motores transaccionales tanto libres como propietarios para salvarnos a todos del apocalipsis. Eventualmente casi todos terminan desapareciendo porque Oracle no detiene el desarrollo de InnoDB como la gente pensaba.
  • Monty vende MySQL en 2008 a Sun.
  • El desarrollo de MySQL por parte de Sun es pésimo y se producen la release de 5.1, desastrosa a nivel de QA.
  • Oracle compra Sun. De nuevo el apocalipsis está a las puertas de MySQL según los "expertos analistas".
  • Monty hace un fork y comienza con MariaDB.
  • Oracle publica la versión de MySQL 5.5.

Y aquí empieza toda esta locura de FUD (Fear, uncertainty and doubt) por parte de Monty y MariaDB. Decenas de artículos y entrevistas por parte de Monty diciendo frases al estilo:

They clearly don’t want to support MySQL in any way.

They have closed all development and people are starting to realize that.

No me sorprende leer supuestas mentiras en una entrevista pero si que sean tan descaradas y fáciles de desmontar. Aún así nadie se cuestiona si lo que dice es verdad. Para muestra, un botón:

MariaDB, el software libre y el lucro cesante

Se está poniendo en evidencia que el desarrollo de MySQL con Oracle es tan rápido que MariaDB no es capaz de seguir su nivel de desarrollo. Cuando aún no se sabe si habrá un MariaDB 5.6, Oracle ya tiene una beta de MySQL 5.7 con funcionalidades como multi-source replication o intra-database multi thread replication. Eso sin contar GTID, que MariaDB aún no lo tiene y según parece lo están reescribiendo.

No es la primera vez que MariaDB (o Monty) pierden una carrera contra Oracle.

Monty comenzó programando un engine de MySQL que se supone iba a sustituir al InnoDB de Oracle. Por aquel entonces se llamaba Maria (luego Aria):

MySQL creator releases Maria, the new engine that will replace InnoDB

Ya sabemos quien ganó esta batalla. Una vez abandonada esa idea, pasaron a trabajar en el optimizador de MySQL (el que decide como se va a ejecutar una query). Aplicaron mejoras, pero una vez más Oracle les ganó esta batalla en 5.5 y 5.6.

Y ahora MariaDB, como último recurso, se ha sacado de la manga un MariaDB 10.0 en Alpha, que posiblemente dejará de ser compatible con MySQL y posiblemente no incluirá ninguna de las nuevas características de MySQL 5.7. Por lo que ya antes de ser liberado se puede decir que está obsoleto y es incompatible. Imaginad quien ganará también esta batalla.

Entonces, cuando a nivel técnico no puedes competir y Oracle no está matando MySQL como a ellos les gustaría... ¿cuál es tu estrategia de marketing? FUD, FUD y más FUD. Crear ruido en la red, hacer creer a la gente que Oracle está matando MySQL y que ellos son los salvadores que nos van a dar un software libre y puro. Así, cada mínimo movimiento de Oracle es usado para atacarlos, muchas veces sin argumentos:

Oracle comienza a desarrollar extensiones de MySQL privativas. Ejemplo, Thread Pool y Pam Authentication.

Link

Horror, Oracle mata MySQL.

La red se llena de artículos diciendo que MySQL deja de ser libre y que pasa a ser de pago. Cuando la realidad se resume en que Oracle crea extensiones para dar funcionalidades muy específicas que un limitado número de usuarios necesitan. ¿Cuantos proyectos open source grandes hacen algo similar? Cada vez que veáis un artículo de Monty criticando estas extensiones, preguntadle:

¿A quién se le ocurrió la idea de crear MySQL Enterprise, completamente cerrado? Si, a el.

Oracle no publica los test cases de los bugs:

Link

Horror, Oracle mata MySQL. Si, otra vez... se ve que la vez anterior no fueron muy eficientes.

Los test cases son unos guiones que se usan para replicar bugs y se usan en el proceso de QA para comprobar si hay alguna regresión. Oracle decide no seguir publicándolos, posiblemente como política de seguridad, porque un test case de un bug de seguridad es simple y llanamente un exploit. Podemos estar más o menos de acuerdo con esa política, que solo te afecta si eres desarrollador, pero ya está, ahí se acaba del drama.

Oracle cambia la licencia de las páginas man y dejan de ser GPL.

Link

Horror, Oracle mata MySQL. En este punto empiezo a pensar que son rematadamente torpes, tercer intento y ahí sigue. Y mira que lo tienen fácil, solo tienen que enviar una nota de prensa diciendo que pasan de MySQL y que dejan de desarrollarlo. Pero siempre buscan estrategias mucho más complicadas. Son muy retorcidos.

Aquí MariaDB publicó un post en su blog explicando el cambio de licencia y propagando FUD. ¿Le preguntaron a Oracle? ¿Crearon un bug report? No, simplemente difundieron FUD. Más tarde un usuario creó el bug report, Oracle admitió el problema y lo solucionó. La respuesta de Oracle en su blog fue algo del estilo:

"La próxima vez, si se vuelve a dar un problema así, la mejor forma de solucionarlo es abriendo un bug report. Es la mejor forma de contactar con nosotros."

O dicho de otra forma:

"La próxima vez nos preguntas antes de difundir mierda, gracias."

Y realmente, ODIO con toda mi alma esta campaña de marketing. No me entendáis mal, MariaDB me gusta, tiene cosas buenas. Sus mejoras en la gestión de subqueries es muy buena, han desarrollado las versiones libres de los plugin de Thread Pool y PAM de los que hablé antes. Pero por favor! Si quieres vender tu producto, habla de lo bueno que es, de lo que ofrece y no te dediques a lanzar mierda a los demás. A mi, trabajando de ingeniero de soporte de MySQL, me da absolutamente igual quien gane esta batalla. Ya sea MySQL, MariaDB o Drizzle, me importa bien poco, porque mi trabajo seguirá siendo el mismo, solo cambia el nombre del producto. Lo único que pido es que la batalla sea limpia y que sea a nivel de código. De esa forma todos saldremos ganando.

Lanzo una pregunta al lector. ¿Alguna vez has leído un artículo que explique las mejoras de MariaDB? ¿Sabes que ofrece MariaDB que no ofrezca MySQL? La respuesta será posiblemente que no tienes ni idea, que lo único que sabes es que Oracle está matando MySQL. Pues si, tienen características y mejoras con respecto a MySQL, pero parece que hablar de eso no interesa, pero si hablar de Oracle. Y de mientras Oracle tiene GTID, replicación multi-thread por schema e intra-database, una mejora de rendimiento y escalabilidad en 5.7 que deja por los suelos todas las versiones anteriores y a cualquier fork, DDL (ALTER TABLE) en caliente, interfaz memcached, etc. etc.

Y a la pregunta, ¿por qué MariaDB tiene FUD como estrategia de Marketing? De nuevo, mi respuesta a modo personal es:

Porque no pueden competir a nivel técnico con Oracle.

UPDATE:

Es importante recalcar que yo no soy adivino. No se si Oracle un día decidirá que se la suda MySQL, o quizás MariaDB se quede sin dinero para financiar el desarrollo y pagar a sus programadores, o quizás una catástrofe nuclear hagan pasar MySQL y las bases de datos a un segundo plano ;) El tema es como se están haciendo las cosas, y de momento, uno de ellos lo está haciendo rematadamente mal. Oracle por su parte, responde con código.

Ojalá MariaDB siga creciendo como proyecto Open Source, que sigan liberando plugins, mejorando MySQL y creando nuevas funcionalidades. Ojalá Oracle haga lo mismo durante muchos años también y que salgan todos los fork que hagan falta si eso mejora el producto final. Pero que la discusión sea técnica, dejémonos de peleas de patio de vecinos.

Por qué deberías desactivar la Query Cache

La web está plagada de howtos para saber como configurar o medir el rendimiento de la Query Cache en MySQL. Incluso en este blog hay uno de esos documentos. En este post trataré de explicar, sin entrar mucho en los detalles técnicos, la razón por la cual el uso de la Query Cache está desaconsejado en la gran mayoría de los casos.

En primer lugar, ¿Qué es la Query Cache? El funcionamiento es muy simple. Cada vez que ejecutas una SELECT MySQL comprueba la lista de queries en su caché. Si está ahí, te muestra directamente el resultado sin ejecutarla. En caso de que no esté, ejecuta la query y guarda el resultado para usarlo más adelante en caso de que sea necesario. Visto así parece genial y puede parece que nos ayuda a reducir la carga de MySQL. Pero el problema es que la Query Cache se diseño en una época donde no había procesadores multi-core y lo habitual eran servidores con una CPU (o dos la gente con dinero). Por lo tanto, el acceso concurrente a la base de datos por más de un thread era algo que no se veía mucho ni funcionaba correctamente.

Ese diseño no ha sido modificado desde entonces, por lo que es una característica que no está realmente pensada para los entornos de trabajo actuales. ¿Cuál es el problema? Simplemente, la Query Cache serializa todos los accesos a la base de datos impidiendo la concurrencia.

Con la Query Cache habilitada todos los threads deben pasar por la Query Cache. El problema es que esta se bloquea, de forma que el primero que llega tiene acceso exclusivo al fin de evitar inconsistencias. De esta forma, una SELECT que debe chequear la Query Cache debe comprobar si tiene acceso y no está bloqueada, en caso de que lo esté le tocará esperar. Lo mismo pasa con UPDATE, INSERT, DELETE... todas tus queries que antes se ejecutaban de forma más o menos paralela, ahora se ponen todos en fila india esperando para tener acceso a la Query Cache. Has convertido tu maravilloso octo-core en un single-core con solo habilitar una variable.

Para entenderlo, nada mejor que una explicación gráfica. Imagínate que hay diez puertas para entrar a un estadio, por lo tanto 10 filas de personas. Entonces viene un DBA y dice que mejor que pase todo el mundo por la puerta 5, que como es un poco más grande cada persona pasará algo más rápidamente. El problema es que si, puede que la persona que llegue a la puerta pase más rápidamente por esta, pero a costa de esperar una fila 10 veces más larga. No compensa.

En el caso de Query Caches grandes el problema es peor. Si hablamos de varios cientos de megas o gigas asignados a esta caché una simple DML (INSERT, DELETE, UPDATE) puede tirar tu servidor abajo. El funcionamiento es sencillo. Tienes, digamos 4 GB de caché e insertas una fila. Ese thread bloqueará la Query Cache y tendrá que invalidar todas las entradas que afectan a esa tabla para eliminarlas de la caché. Esto puede tardar segundos, pero en un servidor con muy alta carga segundos equivale a un problemón. Más y más consultas apiladas, lo que quiere decir más y más contención. En definitiva, estás ante el problema de la bola de nieve causado por algo que se supone debe mejorar el rendimiento. En los escasísimos casos en los que una persona tiene carga generalmente single-thread y se pueda beneficiar de la caché no se deberían poner valores superiores a 64 o 128 megas.

En la gran mayoría de los casos, la mejor forma de tunear la Query Caché es esta:

query_cache_type=0

Así que ya podéis dejar de hacer cálculos o perder el tiempo con herramientas estilo mysqltuner (ya hablaré de esta desgracia otro día).

Posts publicados en MySQL High Performance

Como ya comenté anteriormente ahora mis esfuerzos de blogging se centran más en generar contenido para MySQL High Performance :) Para los que quieran un rápido listado de mis últimos posts, aquí lo tenéis:

Y si, prometo volver a escribir en mi blog en un futuro cercano. Solo necesito reordenar mi vida :)

Último certificado del año, MySQL 5.0 Developer

Justo cuando llegaba el final del año conseguí mi objetivo, el certificado MySQL 5.0 Developer. Ya tengo el pack completo :) La verdad es que este me ha parecido ligeramente más complicado que los anteriores, más que nada por mi poca experiencia con el lenguaje SQL. Pero para eso es están las certificaciones, para aprender.

Ahora toca centrarse en aplicar lo aprendido y seguir mejorando poco a poco. Tengo muchos libros para leer en mi Kindle pero poco tiempo. He empezado por "MySQL High Availability" que a pesar de ser un poco pesado de leer en algunas partes y que son muy pesados con su librería de Python, es un buen libro para ampliar los conocimientos en replicación y alta disponibilidad.

Fuera de la lectura técnica, ¿tenéis alguna recomendación para leer en este 2012? A mi me ha picado la curiosidad Metro 2033.

Nuevos cambios, nuevas oportunidades

Hace bastante tiempo que no escribo aquí y creo que es necesario dar una explicación a las personas que me siguen, aunque no sean muchas ;)

Mi vida laboral ha vuelto a dar un cambio importante, el segundo en lo que voy de año. Desde Octubre he entrado a trabajar en Percona como Support Engineer lo cual, bajo mi punto de vista, es un salto profesional grandísimo y le estoy muy agradecido a Ewen Fortune por esta oportunidad. Ahora mismo tengo de todo en mi vida, pero no tiempo libre ;) Los que me conocen bien saben que en cuanto tengo un reto por delante no puedo dejar de trabajar y esforzarme dando lo mejor de mi, por lo que estos meses están siendo muy intensos. Mucha lectura, estudio y práctica para poder alcanzar el nivel de profesionalidad y conocimiento que tanto hacen destacar a Percona.

Seguiré publicando posts sobre MySQL en mi blog, pero en un principio con menos frecuencia que antes, al menos hasta que me asiente completamente en mi nuevo trabajo. De vez en cuando podéis encontrar algún post mio en MySQL Performance Blog y si saco tiempo iré publicando aquí las versiones en Español.

Para empezar, aquí tenéis mi primera contribución:

Avoiding auto-increment holes on InnoDB with INSERT IGNORE

Muchas gracias a todos y un saludo especial a mis antiguos compañeros de Arsys.

Percona Live London

La próxima semana comienza la Percona Live y esta vez podremos disfrutarla sin salir de Europa. Durará dos días, 24 y 25 de Octubre.

Durante el primer dia se impartirán tutoriales de diversos tema, como por ejemplo NDB o Sphinx.

http://www.percona.com/live/london-2011/schedule-tutorial/

Y el segundo día se reserva para las conferencias:

http://www.percona.com/live/london-2011/schedule-conference/

Las conferencias serán impartidas no solo por compañeros de Percona, si no también por trabajadores de empresas como Paypal, Facebook, Nokia, Couchbase o Monty Program.

Como se puede comprobar, posibilidades de aprender hay miles. Aún estás a tiempo de apuntarte :) Yo estaré por allí toda la semana, así que si algún lector de este blog va a asistir a la Percona Live... ¡allí nos veremos!