Arrancando nuestro primer cluster
Una vez que conocemos la teoría, vamos a poner en marcha nuestro primer Cluster. Estará compuesto únicamente por 3 ordenadores.
Nodo 1 (192.168.1.106):
- ndb_mgmd
- mysqld
Nodo 2 (192.168.1.104):
- ndbd
Nodo 3 (192.168.1.105):
- ndbd
Esto es, el nodo 1 será un Management Node + API Node y los dos restantes Data Nodes.
Lo primero de todo es descargarnos MySQL Cluster de http://dev.mysql.com/downloads/cluster/
La instalación es tan sencilla como descomprimir el fichero y copiar a nuestro PATH los ejecutables que necesitemos. Por lo tanto, llevaremos a /usr/bin/ los ejecutables ndbd, ndb_mgmd, ndb_mgm, mysqld, mysqld_safe.
Para tener un poco ordenadas las cosas, creamos la carpeta /etc/mysql-cluster/ donde alojaremos el fichero de configuración del cluster config.ini.
# cat /etc/mysql-cluster/config.ini [ndbd default] NoOfReplicas=2 DataDir=/var/lib/mysql-cluster DataMemory=512M IndexMemory=128M TransactionDeadlockDetectionTimeout=5000 MaxNoOfConcurrentOperations=100000 MaxNoOfLocalOperations=110000 [ndb_mgmd] Id=1 HostName=192.168.1.106 [ndbd] Id=5 HostName=192.168.1.104 [ndbd] Id=6 HostName=192.168.1.105 [mysqld] Id=7
Como vemos, en primer lugar indicamos los valores por defecto para todos los Data Nodes (ndbd). Por ejemplo se indican el número de replicas, donde se almacenarán los datos, la cantidad de memoria usada para almacenar datos e índices, número de operaciones concurrentes, etc. Estos valores no están ni calculados ni ajustados a la realidad, solamente son funcionales para este ejemplo y para trabajar con la base de datos employees. Cada caso y cada base de datos necesitará distintos valores que se deberán ajustar en función de la carga y cantidad de datos a almacenar.
El Management Node será el número 1 con la IP indicada, mientras que los Data Nodes serán los ID 5 y 6 con sus respectivas IPs. El API node (o en este caso más concreto Mysql Node) no tiene una ip asignada, por lo que se podría conectar desde cualquier equipo de la red.
Una vez que tenemos el fichero procedemos a arrancar el Management Node:
# ndb_mgmd -f /etc/mysql-cluster/config.ini 2010-07-25 19:30:06 [MgmtSrvr] INFO -- NDB Cluster Management Server. mysql-5.1.44 ndb-7.1.4b 2010-07-25 19:30:06 [MgmtSrvr] INFO -- Reading cluster configuration from '/etc/mysql-cluster/config.ini'
Arrancamos la consola y vemos que nodos tenemos arrancados:
# ndb_mgm -- NDB Cluster -- Management Client -- ndb_mgm> show Connected to Management Server at: localhost:1186 Cluster Configuration --------------------- [ndbd(NDB)] 2 node(s) id=5 (not connected, accepting connect from 192.168.1.104) id=6 (not connected, accepting connect from 192.168.1.105) [ndb_mgmd(MGM)] 1 node(s) id=1 @192.168.1.106 (mysql-5.1.44 ndb-7.1.4) [mysqld(API)] 1 node(s) id=7 (not connected, accepting connect from any host)
Solo tenemos el Management Node en marcha. Vamos a proceder a arrancar los Data Nodes. Nos conectamos a cada nodo y ejecutamos:
# ndbd --connect-string=192.168.1.106 --initial -n 2010-07-25 19:31:38 [ndbd] INFO -- Configuration fetched from '192.168.1.106:1186', generation: 1
La opción --initial se usa cuando iniciamos por primera vez el Cluster para arrancar con el sistema de ficheros limpio. -n indica que el nodo no se autoarranque y --connect-string indica la IP del Management Node al que nos conectaremos.
Una vez que Data Node se conecta al Management Node, este último les entrega el fichero de configuración con los parametros necesarios para que se configuren. Ahora, desde la consola de administración podremos arrancar los dos Data Nodes:
ndb_mgm> 5 start Database node 5 is being started. ndb_mgm> Node 5: Start initiated (version 7.1.4) ndb_mgm> 6 start Database node 6 is being started. ndb_mgm> Node 6: Start initiated (version 7.1.4) ndb_mgm> show Cluster Configuration --------------------- [ndbd(NDB)] 2 node(s) id=5 @192.168.1.104 (mysql-5.1.44 ndb-7.1.4, Nodegroup: 0, Master) id=6 @192.168.1.105 (mysql-5.1.44 ndb-7.1.4, Nodegroup: 0) [ndb_mgmd(MGM)] 1 node(s) id=1 @192.168.1.106 (mysql-5.1.44 ndb-7.1.4) [mysqld(API)] 1 node(s) id=7 @192.168.1.106 (mysql-5.1.44 ndb-7.1.4)
Ya tenemos los Data Nodes en marcha.
Para arrancar el Api Node únicamente hay que ejecutar el demonio mysqld:
# mysqld_safe --ndbcluster &
No es necesario indicarle la IP del Management Node ya que se encuentran en la misma máquina.
Listo! Si ahora nos conectamos con el cliente mysql a localhost y creamos una base de datos usando tablas ndbcluster, estas se almacenarán en nuestro recién creado Cluster :)
Introducción a MySQL Cluster
MySQL Cluster es una base de datos que como su nombre indica funciona en un Cluster de servidores. Mucha gente confunde terminos y define un conjunto de servidores con replicación como un MySQL Cluster, pero hay que tener en cuenta que son dos conceptos totalmente distintos. MySQL Cluster nos ofrece:
- Alta disponibilidad
- Escalabilidad
- Failover automático
- Redundancia
- Alto throughput
La versión actual es la 7.1 y puede descargarse de http://www.mysql.com/products/database/cluster/
Componentes
Un Cluster MySQL está compuesto por los siguientes componentes:
Manager (ndb_mgmd): es un servicio encargado de poner en marcha el cluster, conectar nuevos servidores y ejecutar distintos comandos de administración mediante el CLI ndb_mgm. Una vez que hemos levantado el cluster no es necesario ni un requisito indispensable que esté levantado.
Data Nodes (ndbd): son nodos encargados del almacenamiento de los datos. Se recomiendan al menos dos para disponer de redundancia y alta disponibilidad. Estas serán las máquinas más potentes del cluster, almacenarán los índices en memoria y los datos en memoria o disco. Todos los Data Nodes deben tener el mismo hardware para evitar crear cuellos de botella.
API nodes (mysqld): aunque el más usado sea mysqld, un API node puede ser cualquier aplicación que haciendo uso de la API acceda al cluster. El típico, también conocido como SQL Node, es el demonio mysqld típico (compilado con soporte nbdcluster). De esta forma podremos escribir o leer datos de nuestra BBDD como hemos hecho hasta ahora, mediante comandos SQL.
Se recomienda que cada componente esté instalado en una máquina física distinta.
Funcionamiento interno
Internamente, el funcionamiento del cluster se basa en dos conceptos básicos. Replicación interna síncrona y auto particionado de datos. La primera nos ofrece la redundancia y el segundo nos da la escalabilidad. Importante diferenciarlo de la replicación típica de MySQL (asíncrona). En este caso, hasta que los datos no han sido replicados en los nodos seleccionados no se devuelve el control al usuario, obteniendo de esta forma la consistencia que no tenemos en la replicación asíncrona.
El particionado (PARTITION BY KEY) es también totalmente automático. El cluster se encarga de dividir las tablas en distintas particiones y dividir los datos entre los distintos Data Nodes. Aunque es posible que definamos nuestro propio particionado, no se recomienda. Añades complejidad y posiblemente el rendimiento no sea el esperado.
Replicas
A la hora de configurar nuestro cluster, una de los valores más importantes a tener en cuenta es decidir el número de replicas que tendremos de nuestros datos. No podemos decidir cualquier número, si no que tendremos que seguir unas sencillas reglas. Pongamos por ejemplo que tenemos 4 Data Nodes. En este caso podremos tener 1, 2 y 4 replicas. Esto es, el número de nodos debe poder ser divisible por el numero de replicas. Aún así, no se debería tener una única replica, ya que eso no nos da ningún tipo de alta disponibilidad ya que al caerse un solo nodo perderíamos el acceso a los datos.
Node Groups
MySQL Cluster agrupa automáticamente los Data Nodes en grupos. Esto no está bajo nuestro control ni podemos decidir que nodo está en que grupo, será trabajo del cluster hacer estas agrupaciones. Siguiendo el ejemplo anterior, si tenemos 4 Data Nodes y 2 réplicas, MySQL Cluster nos generará dos Node Groups (4/2=2). Además hay que tener en cuenta que el número de particiones que se harán de nuestros datos siempre será igual al número de Data Nodes.
Por lo tanto, imaginemos que tenemos el N1 y N2 en el grupo 1 (G1) y N3 y N4 en el grupo 2 (G2). A la hora de particionar y repartir los datos es necesario pensar en la alta disponibilidad, por lo que el particionado se hará de la siguiente forma:
G1 N1 = P1 y P2' N2 = P2 y P1'G2 N3 = P3 y P4' N4 = P4 y P3'
Siendo PX el número de la partición y PX' una copia de la partición de Backup.
Sabiendo esto, podemos imaginar cuando dispondremos de alta disponibilidad. Mientras al menos uno de los nodos de un grupo esté levantado, el cluster estará online. Por ejemplo se podrían caer N1 y N4 o N3 y N2 y todo seguiría funcionando. Pero una caida de N1 y N2 dejaría el cluster completamente caido.
A mayor número de replicas menos posibilidades de fallo, más escalabe y más throughput :)
Próximamente extenderé el tema de MySQL Cluster con más entradas según vaya profundizando en el estudio de la certificación. La intención será termianr teniendo un mini manual para andar por casa que nos permita dar los primeros pasos.
Postfix TLS con múltiples certificados
Cuando hablamos de TLS o SSL siempre nos encontramos con el mismo problema, el certificado. Este debe ser único por puerto e IP, de forma que dependiendo de a donde se conecte el cliente le entregamos un certificado u otro acorde a su dominio. Esto es algo con lo que tenemos que pelear constantemente en Apache, creando un VirtualHost por cada IP y asignándole un certificado.
Postfix no se libra y también necesitamos una IP por certificado, de forma que a cada cliente le muestre el certificado de su dominio y nos evitemos los famosos warning. Para hacerlo en primer lugar es necesario tener una IP por cada dominio. Una vez completado ese simple requisito pasamos a configurar Postfix. Primero ponemos TLS a nivel global en Postfix dentro del main.cf:
#TLS smtpd_use_tls = yes smtpd_tls_key_file = /etc/ssl/dominio1.com.key smtpd_tls_cert_file = /etc/ssl/dominio1.com.crt smtpd_tls_received_header = yes smtpd_tls_session_cache_timeout = 3600s tls_random_source = dev:/dev/urandom
Ahora tenemos que hacer que Postfix arranque un proceso smtpd por cada una de las IPs/Dominios. Esto lo hacemos en el master.cf. Al mismo tiempo que indicamos la IP, también le indicamos el certificado que usará:
#========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) #========================================================================== 10.10.10.1:smtp inet n - - - - smtpd 127.0.0.1:smtp inet n - - - - smtpd 10.10.20.1:smtp inet n - - - - smtpd -o smtpd_tls_cert_file=/etc/ssl/dominio2.com.crt 10.10.30.1:smtp inet n - - - - smtpd -o smtpd_tls_cert_file=/etc/ssl/dominio3.com.crt 10.10.40.1:smtp inet n - - - - smtpd -o smtpd_tls_cert_file=/etc/ssl/dominio4.com.crt
Listo, en cada IP entregará un certificado :)
Replicación maestro-esclavo en MongoDB
Esta es una entrada cortita, gracias en parte a la extrema facilidad de administrador de nuestra base de datos NoSQL favorita. En esta ocasión vamos a ver como crear una replicación dentre dos sistemas MongoDB, en arquitectura Maestro-Esclavo. Como pasa bastante habitualmente, la arquitectura Maestro-Maestro, a pesar de ser posible, no la recomiendan. Esta, al igual que en MySQL, se basa en el truco de hacer que un esclavo sea al mismo tiempo maestro.
Yo voy a mostrar la opción recomendada, Maestro-Esclavo.
Para ello, lo primero es instalar dos MongoDB en Debian Lenny.
El primero se llamará Maestro con IP 192.168.1.105 y el segundo Esclavo con IP 192.168.1.101.
Arrancamos el maestro indicando que actuará con ese Rol:
debian1:/usr/local/mongodb# bin/mongod --master Sat Jun 12 16:41:23 Mongo DB : starting : pid = 2158 port = 27017 dbpath = /data/db/ master = 1 slave = 0 64-bit Sat Jun 12 16:41:23 db version v1.4.3, pdfile version 4.5 Sat Jun 12 16:41:23 git version: 47ffbdfd53f46edeb6ff54bbb734783db7abc8ca Sat Jun 12 16:41:23 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41 Sat Jun 12 16:41:23 waiting for connections on port 27017 Sat Jun 12 16:41:23 ****** Sat Jun 12 16:41:23 creating replication oplog of size: 944MB (use --oplogSize to change) Sat Jun 12 16:41:23 ****** Sat Jun 12 16:41:23 allocating new datafile /data/db/local.ns, filling with zeroes... Sat Jun 12 16:41:23 done allocating datafile /data/db/local.ns, size: 16MB, took 0.026 secs Sat Jun 12 16:41:23 allocating new datafile /data/db/local.0, filling with zeroes... Sat Jun 12 16:41:23 done allocating datafile /data/db/local.0, size: 64MB, took 0.185 secs Sat Jun 12 16:41:23 allocating new datafile /data/db/local.1, filling with zeroes...
Una vez hecho, arrancamos el Esclavo indicándole por parámetro donde está su Maestro:
debian2:/usr/local/mongodb/bin# ./mongod --slave --source 192.168.1.105:27017 Sat Jun 12 16:42:03 Mongo DB : starting : pid = 2172 port = 27017 dbpath = /data/db/ master = 0 slave = 1 64-bit Sat Jun 12 16:42:03 db version v1.4.3, pdfile version 4.5 Sat Jun 12 16:42:03 git version: 47ffbdfd53f46edeb6ff54bbb734783db7abc8ca Sat Jun 12 16:42:03 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41 Sat Jun 12 16:42:03 waiting for connections on port 27017 Sat Jun 12 16:42:03 web admin interface listening on port 28017 Sat Jun 12 16:42:04 allocating new datafile /data/db/local.ns, filling with zeroes... Sat Jun 12 16:42:04 done allocating datafile /data/db/local.ns, size: 16MB, took 0.028 secs Sat Jun 12 16:42:04 allocating new datafile /data/db/local.0, filling with zeroes... Sat Jun 12 16:42:05 done allocating datafile /data/db/local.0, size: 64MB, took 0.185 secs Sat Jun 12 16:42:05 building new index on { id: 1 } for local.sources Sat Jun 12 16:42:05 Buildindex local.sources idxNo:0 { name: "_id", ns: "local.sources", key: { _id: 1 } } Sat Jun 12 16:42:05 done for 0 records 0secs Sat Jun 12 16:42:05 repl: from host:192.168.1.105:27017 Sat Jun 12 16:42:05 repl: applied 1 operations Sat Jun 12 16:42:05 repl: end sync_pullOpLog syncedTo: Sat Jun 12 16:42:00 2010 4c139cb8:1
Ya tenemos las dos bases de datos en marcha:

Creamos una nueva base de datos llamada test y añadimos una colección:
> use test switched to db test > a = { nombre : "Miguel Angel", apellido : "Nieto" } { "nombre" : "Miguel Angel", "apellido" : "Nieto" } > db.gente.save(a);
Comprobamos que se ha añadido en Maestro:

Comprobamos que se ha añadido en Esclavo:

Y lo buscamos en el Esclavo:

Más facil y rápido imposible :)
Finalmente no me certifico en MySQL Cluster
Realmente no es porque yo no quiera, que en realidad tengo muchas ganas, si no que el pésimo servicio de atención al cliente de Oracle me lo impide por su completa inutilidad operativa. Llevo meses esperando que hagan algo que a priori es sencillo, comprobar mis certificados en PearsonVUE para permitirme sacar la nueva certificación en Prometric. Algo tan sencillo como eso, más aún siendo Oracle cliente también de PearsonVUE, se ha convertido en una hazaña imposible.
Desde la última vez que me pidieron algunos datos y documentos desde "suncert_ww@oracle.com" no he vuelto a saber de ellos. Y cuando escribo ya no me llega ni el mensaje de respuesta automática. Por lo tanto, ¿que puedo pensar de esto? O que estoy filtrado de por vida o que son unos completos inútiles. Desde aquí lanzo unas preguntas:
- ¿Si tuviese una cuenta en Metalink u Oracle Support me harían más caso?
- ¿Y Si estuviese interesado en certificarme en Oracle?
Estas pidiendo ayuda al servicio de soporte para poder certificarte y te sientes como si estuvieras mendigando.
Si alguien ha tenido un problema similar y ha logrado solucionarlo, ¡que se comunique conmigo para saber como! Contacto
El guardián entre el centeno
Si no fuera porque su autor es J. D. Salinger podría jurar que lo escribí yo :P
Pensé que encontraría trabajo en una gasolinera poniendo a los coches aceite y gasolina. Pero la verdad es que no me importaba qué clase de trabajo fuera con tal de que nadie me conociera y yo no conociera a nadie. Lo que haría sería hacerme pasar por sordomudo y así no tendría que hablar. Si querían decirme algo, tendrían que escribirlo en un papelito y enseñármelo. Al final se hartarían y ya no tendría que hablar el resto de mi vida. Pensarían que era un pobre hombre y me dejarían en paz. Yo les llenaría los depósitos de gasolina, ellos me pagarían, y con el dinero me construiría una cabaña en algún sitio y pasaría allí el resto de mi vida. La levantaría cerca del bosque, pero no entre los árboles, porque quería ver el sol todo el tiempo. Me haría la comida, y luego, si me daba la gana de casarme, conocería a una chica guapísima que sería también sordomuda y nos casaríamos. Vendría a vivir a la cabaña conmigo y si quería decirme algo tendría que escribirlo como todo el mundo. Si llegábamos a tener hijos, los esconderíamos en alguna parte. Compraríamos un montón de libros y les enseñaríamos a leer y escribir nosotros solos.
¡Grande! Toca leérmelo otra vez, el ritual... una vez al año.
MogileFS, sistema de ficheros distribuido a través de la red
MogileFS es un sistema de ficheros distribuido que funciona en activo-activo o dicho de otra forma, es un RAID a través de la red. Algunas de sus características:
No requiere de ningún parche en kernel ni módulos especiales ya que funciona completamente a nivel de aplicación.
No hay ningún SPOF(single point of failure) ya que todos los elementos que forman MogileFS pueden estar distribuidos en múltiples máquinas.
Se pueden especificar clases de ficheros. Las clases definirán cuantas réplicas pueden llegar a tener. Por ejemplo, un fichero de log poco importante puede ser suficiente con dos réplicas, pero los datos bancarios de la empresa necesita 10 réplicas. Dependiendo de la clase, MogileFS hará automáticamente las réplicas que sea necesario.
No importa el sistema de ficheros que tengamos debajo. Puede ser Ext3, Ext4, XFS…
Está compuesto por varios componentes, cada uno de los cuales puede estár en diferentes máquinas y replicado tantas veces como quieras:
Base de datos: MySQL ;) Se almacenan los metadatos y los namespaces y debería estar en HA o tendremos un punto de fallo.
Tracker (mogilefsd): es el proceso encargado de recibir las peticiones del cliente y gestionarlas.
Nodos de almacenamiento (mogstored): donde los datos se guardan físicamente. Estos nodos son simplemente servidores HTTP que recibe peticiones PUT, GET, DELETE
Vamos con la instalación ;)
1- Instalar dependencias:
# apt-get install debhelper dpkg-dev fakeroot # apt-get install libio-aio-perl libdanga-socket-perl libnet-netmask-perl libstring-crc32-perl # apt-get install gearman-server libgearman-client-perl libgearman-client-async-perl
2- Instalar perlbal:
# cd /usr/src # svn co http://code.sixapart.com/svn/perlbal # cd perlbal/trunk # dpkg-buildpackage -rfakeroot # dpkg -i ../libperlbal-perl_1.70-1_all.deb
3- Instalar mogileFS:
# cd /usr/src # svn co http://code.sixapart.com/svn/mogilefs # cd mogilefs/trunk # bin/build-all-debian.sh # dpkg -i ./api/perl/libmogilefs-perl_1.00-1_all.deb # bin/build-all-debian.sh # dpkg -i packages/mogilefsd_1.00-2_all.deb packages/mogilefs-utils_0.01-1_all.deb packages/mogstored_1.00-2_all.deb
4- Crear base de datos:
$ mysql -u root -p mysql> create database mogilefs; mysql> grant all on mogilefs.* to 'mogile'@'%' identified by 'mogilepw'; mysql> flush privileges; mysql> quit
5- Crear esquema:
$ /usr/bin/mogdbsetup --yes --dbname=mogilefs --dbuser=mogile --dbpassword=mogilepw
Al menos tiene que existir un tracker, se puede configurar en el mismo nodo de storage:
zimbra:/var/mogdata# cat /etc/mogilefs/mogilefsd.conf #daemonize = 1 # Database connection information db_dsn = DBI:mysql:mogilefs:host=127.0.0.1 db_user = mogile db_pass = mogilepw # IP:PORT to listen on for mogilefs client requests listen = 127.0.0.1:7001 # Optional, if you don't define the port above. conf_port = 7001 # Number of query workers to start by default. query_jobs = 10 # Number of delete workers to start by default. delete_jobs = 1 # Number of replicate workers to start by default. replicate_jobs = 5 # Number of reaper workers to start by default. # (you don't usually need to increase this) reaper_jobs = 1 # Number of fsck workers to start by default. # (these can cause a lot of load when fsck'ing) #fsck_jobs = 1 # Minimum amount of space to reserve in megabytes # default: 100 # Consider setting this to be larger than the largest file you # would normally be uploading. #min_free_space = 200 # Number of seconds to wait for a storage node to respond. # default: 2 # Keep this low, so busy storage nodes are quickly ignored. #node_timeout = 2 # Allow replication to use the secondary node get port, # if you have apache or similar configured for GET's #repl_use_get_port = 1
Una vez hecho, se añaden los hosts de almacenamiento:
mogadm --trackers=127.0.0.1:7001 host add hostmolon1 --ip=10.10.0.122 --port 7500 --status=alive mogadm --trackers=127.0.0.1:7001 host add hostmolon2 --ip=10.10.0.122 --port 7500 --status=alive
Se listan para ver si es correcto:
zimbra:/var/mogdata# mogadm --trackers=127.0.0.1:7001 host list hostmolon1 [1]: alive IP: 127.0.0.1:7500hostmolon2 [2]: alive IP: 10.10.0.122:7500
Se añaden los dispositivos de almacenamiento:
mogadm --trackers=127.0.0.1:7001 device add hostmolon1 1 mogadm --trackers=127.0.0.1:7001 device add hostmolon2 2
El último valor es el id, por lo tanto en hostmolon1 habrá que crear la carpeta:
/var/mogdata/dev1/
y en hostmolon2:
/var/mogdata/dev2/
El propietario de la carpeta tiene que ser “mogstored”. Generalmente lo que se suele hacer es montar discos duros sata directamente en dichas carpetas.
Comprobamos que tira:
zimbra:/var/mogdata# mogadm --trackers=127.0.0.1:7001 device list hostmolon1 [1]: alive used(G) free(G) total(G) dev1: alive 1.539 5.567 7.106hostmolon2 [2]: alive used(G) free(G) total(G) dev2: alive 2.502 4.604 7.106
Para poder catalogar los ficheros es necesario definir dos cosas, dominios y clases. El dominio sería como una separación de un grupo de ficheros. El dominio a su vez puede tener definido un número n de clases.
mogadm --trackers=127.0.0.1:7001 domain add irontec mogadm --trackers=127.0.0.1:7001 class add irontec miclase
Y ya podemos añadir ficheros. Estos se deben gestionar mediante API, ya sea con PHP, Python o cualquier otro que nos guste más. Esto es así porque los ficheros no se escriben en la carpeta como si se tratase de un sistema de ficheros normal, si no que hay que hacerlo a través de un tracker para que este se encarge de las réplicas. Nosotros, en lugar de hacer uso de estas APIs, tiraremos de la herramienta de consola mogtool.
mogtool inject /boot/vmlinuz-2.6.26-2-686 vmlinuz --trackers=127.0.0.1:7001 --domain=irontec
Buscarlos:
zimbra:/var/mogdata# mogtool locate vmlinuz --trackers=127.0.0.1:7001 --domain=irontec http://127.0.0.1:7500/dev2/0/000/000/0000000005.fid [^] http://10.10.0.122:7500/dev10/0/000/000/0000000005.fid [^] 0000002 paths found
Y extraerlos:
zimbra:/var/mogdata# mogtool extract vmlinuz /tmp/test --trackers=127.0.0.1:7001 --domain=irontec Fetching piece 1... Trying http://10.10.0.122:7500/dev10/0/000/000/0000000005.fid... [^] Done. zimbra:/var/mogdata# md5sum /tmp/test /boot/vmlinuz-2.6.26-2-686 f6690b230573b7f791e84c33adf0804c /tmp/test f6690b230573b7f791e84c33adf0804c /boot/vmlinuz-2.6.26-2-686
Todo esto tiene una mejora importante sobre los RAID por software o hardware. El RAID por si mismo te salva si se rompe alguno de los discos, ¿pero que pasa si se rompe el servidor? ;) Nada mejor que un RAID distribuido a través de la red para evitar estos problemas.
Introducción al mundo noSQL
NoSQL es la nueva palabra de moda. Una solución pensada para el almacenamiento de gran cantidad de datos para su posterior uso. Esto ya nos suena, una base de datos, pero realmente, ¿cual es la diferencia?
NoSQL es un término usado en informática para agrupar una serie de almacenes de datos no relacionales que no proporcionan garantías ACID. Normalmente no tienen esquemas fijos de tablas ni sentencias "join".
Wikipedia
La diferencia ya es clara, no es una base de datos como las que conocemos actualmente, es bastante más peculiar y tira por tierra bastantes de los conceptos y formas de trabajar a los que estamos acostumbrados. Y aunque el termino en si sea nuevo, la teoría tiene sus años, bastantes más que las bases de datos relacionales, pero ahora vuelve con fuerza para salvarnos la vida en algunos casos concretos, evitando problemas de escalabilidad o rendimiento a costa de algo de fiabilidad.
Hasta ahora el cuello de botella siempre ha sido el acceso a los datos y la forma de evitarlo la escalabilidad vertical, esto es, añadiendo más memoria, procesamiento y discos rápidos. Pero todo escalado vertical tiene un límite, y es el que pone nuestro bolsillo y la propia tecnología. Por otro lado, las bases de datos relacionales no suelen tender a escalar bien horizontalmente.
- Normalización o desnormalización
- Replicación asíncrona y por lo tanto problemática
- ¿Cómo distribuyes la carga de escrituras?
- ¿Transacciones distribuidas? Uff... no!
Es importante tener en cuenta que no es un reemplazo a las bases de datos SQL que llevamos usando desde hace años, si no que un complemento que nos ayudará en casos particulares, por lo tanto tened en mente que noSQL es en realidad no(tOnly)SQL :)
A modo introducción vamos a ver el funcionamiento de MongoDB, una de las que más me han impresionado por su facilidad de uso y rapidez.
MongoDB agrupa los datos documentos (y sub-documentos). Esto es, objetos sin ningún tipo de esquema fijo, como por ejemplo un array de PHP o una tupla de Python. Los datos se guardan en BSON (JSON binario) y permite consultas algo más complejas que algunas otras soluciones NoSQL.
BSON [bee · sahn], short for Binary JSON, is a binary-encoded serialization of JSON-like documents. Like JSON, BSON supports the embedding of documents and arrays with in other documents and arrays. BSON also contains extensions that allow representation of data types that are not part of the JSON spec. For example, BSON has a Date type and a BinData type.
Además soporta el uso de índices compuestos, simples o geo-espaciales de dos dimensiones, auto particionado de datos y replicación master-slave entre otras muchas cosas. Importante para los desarrolladores, ¡existe módulo de PHP! ;) No en vano, la gente lo empieza a llamar el MySQL de NoSQL.
Después de esta pequeña introducción vamos a jugar un poco con MongoDB :) Para ello nos bajamos el binario de http://www.mongodb.org/display/DOCS/Downloads La instalación es tan sencilla como descomprimir y lanzar el binario del servidor (mongod):
bin # ./mongod ./mongod --help for help and startup options Fri May 7 21:16:00 Mongo DB : starting : pid = 307 port = 27017 dbpath = /data/db/ master = 0 slave = 0 64-bit Fri May 7 21:16:00 db version v1.4.1, pdfile version 4.5 Fri May 7 21:16:00 git version: nogitversion Fri May 7 21:16:00 sys info: Darwin iMac-Punisher.local 10.3.0 Darwin Kernel Version 10.3.0: Fri Feb 26 11:58:09 PST 2010; root:xnu-1504.3.12~1/RELEASE_I386 i386 BOOST_LIB_VERSION=1_42 Fri May 7 21:16:00 waiting for connections on port 27017 Fri May 7 21:16:00 web admin interface listening on port 28017
A primera vista ya vemos datos interesantes. Los datos se van a guardar en la ruta /data/db, que debemos crear con antelación o indicar otra mediante parámetro, y que el puerto de escucha será 27017. Si no le indicamos nada, el demonio se lanzará en foreground y la consola se quedará "bloqueada". Podremos parar el demonio con un simple Control+C.
La consola para empezar a usar mongoDB se llama "mongo", que se encuentra en la misma ruta de binarios que el resto.
bin $ ./mongo MongoDB shell version: 1.4.1 url: test connecting to: test type "help" for help >
Vemos las bases de datos existentes y nos creamos la nuestra propia:
> show dbs admin local > use midb switched to db midb
Como veis no es necesario crearla antes de usarla, simplemente dile que la quieres usar y si no existe la creará. Ahora vamos a meter una serie de datos:
>a = { nombre : "Miguel Angel", apellidos : "Nieto Salazar", edad : "27", localidad : "Barakaldo" } { "nombre" : "Miguel Angel", "apellidos" : "Nieto Salazar", "edad" : "27", "localidad" : "Barakaldo" } >b = { nombre : "Estibaliz", apellidos : "Sanchez", Nacimiento : "Logroño", estudio : "Comercio" } { "nombre" : "Estibaliz", "apellidos" : "Sanchez", "Nacimiento" : "Logroño", "estudio" : "Comercio" } > db.coll.save(a); > db.coll.save(b);
Y como ya dije anteriormente, no existen tablas ni esquemas, añades datos según lo vas necesitando, sin necesidad de seguir un patrón o estando obligado a rellenar unos datos en concreto. Todos estos documentos se guardarán en la colección "coll" dentro de la base de datos midb. Una colección no es más que una agrupación de datos BSON que estos a su vez se pueden agrupar en namespaces. Por ejemplo blog.post y blog.comments son dos colecciones que cuelgan del mismo namespace. Esto es solamente a nivel organizativo para el usuario, internamente no afecta en nada.
Una vez que tenemos los datos introducidos, podemos hacer consultas:
> db.coll.find({edad:"27"}); { "_id" : ObjectId("4be029e11967ab80a305e1f0"), "nombre" : "Miguel Angel", "apellidos" : "Nieto Salazar", "edad" : "27", "localidad" : "Barakaldo" } > db.coll.find({Nacimiento:"Logroño"}); { "_id" : ObjectId("4be029e41967ab80a305e1f1"), "nombre" : "Estibaliz", "apellidos" : "Sanchez", "Nacimiento" : "Logroño", "estudio" : "Comercio" }
Facil, sencillo y para toda la familia. Cuando lo ves así piensas, "esto es lo lógico, el almacenamiento de datos tendría que ser así siempre" :)
También podemos almacenar datos binarios, esto es, ficheros. Puedes olvidarte de todo lo que has sufrido con los tipos BLOB en otras bases de datos. MongoDB guarda los ficheros en la base de datos haciendo uso de las especificaciones GridFS. BSON solo permite almacenar datos de hasta 4 megas, para saltarnos esta limitación GirdFS divide de forma transparente los ficheros en grupos de documentos (lo cual también nos permitira buscar por rangos de bytes).
Listamos el contenido, de momento no hay ningún fichero:
$ mongofiles list connected to: 127.0.0.1
Añadimos un fichero, en este caso un documento PDF:
$ mongofiles put /Users/punisher/Documents/High\ Performance\ MySQL\ Second\ Edition.pdf connected to: 127.0.0.1 added file: { _id: ObjId(4be46d25a409d73217ca227b), filename: "/Users/punisher/Documents/High Performance MySQL Second Edition.pdf", length: 5904835, chunkSize: 262144, uploadDate: new Date(1273261349209), md5: "082f8333d45d1e3b7bce6323d0da59f0" } done!
Listamos de nuevo:
$ mongoflist connected to: 127.0.0.1 /Users/punisher/Documents/High Performance MySQL Second Edition.pdf 5904835
Lo sacamos de la base de datos a nuestro sistema de ficheros:
$ mongofiles get "/Users/punisher/Documents/High Performance MySQL Second Edition.pdf" connected to: 127.0.0.1 done write to: /Users/punisher/Documents/High Performance MySQL Second Edition.pdf
No lo voy a negar, estoy enamorado de mongoDB y del concepto noSQL. Olvídate de las relaciones, de optimizar queries, normalizar tablas... dedicate a lo que importa, guardar y sacar datos. Una revolución para los que nos guiamos por la filosofía KISS (Keep it simple, stupid)
Entendiendo y utilizando la Query Cache
La Query Cache nos permite almacenar el resultado de las query SELECT en una cache, de forma que si se piden los mismos datos repetidamente, únicamente tendrá que ejecutarlo una vez, devolviendo el resto de veces el resultado desde la memoria. Esto, como os podeis imaginar, mejora en gran medida el rendimiento de nuestro servidor. Pero hay que tener una serie detalles en cuenta al hacer uso de esta cache :)
- Para mantener la consistencia en los resultados guarda una relación de las tablas a las cuales afectan la query, de forma que si una de esas tablas se ve modificada, la Query Cache se invalida.
- La query se guarda tal y como la hemos escrito, esto es, para la Cache no sería lo mismo "SELECT Nom,Ap from t where id=2" que "SELECT nom,ap from T where id=2".
- No se guardará el resultado de la query a no ser que esta no sera determinista. Por lo tanto, funciones como NOW(), CURRENT_DATE(), CURRENT_USER(), etc. no son candidatas a ser cacheadas.
- La Query Cache añade overhead. Las queries de lectura deben leer o escribir de la cache y las de escritura deben borrar las caches asociadas a las tablas modificadas.
- Durante una transacción, las caches asociadas a las tablas modificadas no se podrán usar hasta que no se haga COMMIT o ROLLBACK.
Podemos controlar como de eficiente es nuestra Cache mirando el estado de algunas variables de MySQL. Cuando una query se responde de la cache, el valor que aumenta es Qcache_hits. Por el contrario, si la query debe ejecutarse por no estar previamente cacheada, aumentaremos en uno el valor de Com_select. Por lo tanto, para conocer el ratio de hacierto podemos hacer uso de esta formula:
Qcache_hits / (Qcache_hits+Com_select)
Otra variable importante a tener en cuenta es Qcache_lowmem_prunes, que es un contador del número de queries sacadas de la caché por falta de memoria. Si el valor de esta variable sube demasiado a lo largo del tiempo, deberás ir pensando en aumentar el tamaño de la caché.
Los valores que debemos "tunear" en nuestro my.cf son los siguientes:
query_cache_type: puede tener el valor ON, OFF y DEMAND. Los dos primeros habilitan o deshabilitan la caché, mientras que el último cacheará una query siempre y cuando lleve el modificador SQL_CACHE
query_cache_size: es el tamaño de la caché. Debe ser un valor múltiplo de 1024 bytes
query_cache_min_res_unit: MySQL guarda las cachés en la memoria en pequeños bloques, como si de un sistema de ficheros se tratase. En un principio no sabe realmente el tamaño que va a tener el resultado de una query, por lo que según va enviando las filas al cliente, va cogiendo bloques. Los bloques tendrán como mínimo el tamaño aquí indicado. Este valor es importante para evitar la fragmentación de la memoria y así aprovecharla lo mejor posible
query_cache_limit: si un resultado supera el tamaño aquí indicado, no se cacheará. Pero recordad lo comentado en el punto anterior, MySQL no sabe a priori cuando ocupará, por lo que igualmente irá reservando bloques hasta llegar al query_cache_limit, momento en el cual los bloques escritos se liberarán de nuevo
Para evitar la fragmentación hay que elegir un valor correcto. Este no debe ser muy pequeño, ya que aunque evitaremos perder memoria, MySQL tendrá que ir cogiendo bloques constantemente. Ni muy grande, ya que si nuestras queries devuelven resultados pequeños tendremos bastante fragmentación. Una de las formas de tener un valor cercano al óptimo es saber la media de tamaño de las queries almacenadas. Para ello podemos aplicar la siguiente formula:
(query_cache_size – Qcache_free_memory) / Qcache_queries_in_cache
Esto es, la memoria usada entre el numero de queries en caché.
Para saber si realmente tienes la caché muy fragmentada, puedes hacerte una idea comprobando el número de bloques libres Qcache_free_blocks. Si el valor de esta variable está cercana a Qcache_total_blocks / 2, entonces tienes un grave problema de fragmentación.
MySQL también tiene su propio desfragmentador :) FLUSH QUERY CACHE moverá todos los bloques utilizados eliminando los espacios en blanco entre ellos.
Trasparencias del curso "Apache Avanzado"
- Instalación desde código fuente
- Instalación desde binarios
- Directivas de configuración
- SSL
- Certificados de cliente
- VirtualHost
- NameVirtualHost
- Balanceo de carga
- Optimización del servicio
- Mod_Cache
- Mod_Rewrite
- Mod_Security
- Mod_Proxy
- Apache Benchmark











