Prueba del sharding en MongoDB

4
5676
En este tutorial, explicaremos los fundamentos del particionado de datos en MongoDB y configuraremos, a través de un sencillo script, una pequeña prueba de Sharding en nuestra propia máquina.

Índice de contenidos

1. Introducción y Objetivo

Junto con la réplica de datos, de la que ya hablamos en Réplica de datos en MongoDB, el particionado de datos, también conocido como Sharding, es otra de las funcionalidades que nos brinda MongoDB para gestionar la escalabilidad del almacenamiento de nuestros datos.

Con el Sharding conseguiremos distribuir la carga de datos a almacenar entre varios nodos de MongoDB, de forma que no todos los datos se almacenan en todos los nodos. Sin embargo, es importante conocer cómo funciona en detalle el sistema de particionado de datos y cómo las consultas se gestionan entre los distintos nodos, porque ello puede afectar en gran medida al rendimiento que obtengamos de nuestro sistema.

En este artículo explicaremos cómo configurar la réplica de datos (a través de ShardingTest) en MongoDB.

A lo largo del tutorial iremos viendo los pasos para montar un servicio de MongoDB configurado con el sharding activo usando tres instancias, en una misma máquina, entre las que particionaremos los datos.

Para ello se utilizará el propio API de MongoDB y la facilidad que nos proporciona para la configuración por defecto de la funcionalidad de sharding a través del objeto ShardingTest.

Los pasos que se presentan en el presente artículo NO constituyen la forma recomendada de configurar el particionado de datos, sino que pretenden mostrar cómo probar la funcionalidad de particionado de MongoDB de una forma sencilla. Por este motivo, este documento no debe utilizarse como guía para la configuración de entornos de producción.

Para configurar correctamente el particionado de datos en MongoDB, se debe configurar los mismos elementos que se levantan en este ejemplo de forma automática a través de ShardingTest, pero de forma específica y manual.

2. Requisitos

Para poder seguir las instrucciones de esta guía y probar el funcionamiento del Sharding en MongoDB, vamos a utilizar un servidor de MongoDB, que levantará varios procesos mongod en una misma máquina y sobre el que probaremos a insertar datos y comprobar cómo estos se van repartiendo a través de los distintos nodos que forman parte del grupo de Sharding.

Para realizar el proceso, utilizaremos la consola de Mongo y el API JavaScript que ofrece esta consola.

Por tanto, el único requisito es tener una versión de MongoDB instalada. Se requiere que sea al menos la versión 2.2, dado que es la primera versión donde el API incluye el objeto ShardingTest, con la estructura de la que nos valdremos para configurar las pruebas de particionado de datos.

3. Arranque y acceso a la consola con Mongo Shell

Para iniciar la prueba de Sharding necesitamos arrancar una primera consola de Mongo, desde la que iremos levantando los nodos que configuraremos para el grupo de particionado.

Por este motivo, necesitamos arrancar la consola de mongo sin conectar contra ningún servidor en concreto. Esto lo conseguimos con el parámetro --nodb

Es preferible que no tengamos una instancia de MongoDB previamente arrancada, las distintas instancias que formarán parte del grupo de particionado de prueba se irán levantando durante el proceso del script.

4. Creación del grupo de particionado para pruebas

Para probar el mecanismo de réplica de MongoDB, necesitaremos crear varias instancias de mongod que actúen como servidores y relacionarlas de forma que sepan repartirse los datos entre ellas.

El Sharding permite dividir la carga de almacenamiento de datos entre varios nodos, de forma que no todos los datos se almacenan en todos los nodos. Es decir, cada nodo, guarda un subconjunto total de los datos, de forma que el escalado horizontal de la BD es más sencillo de realizar.

MongoDB, con el Sharding, tratará de que, en la medida de lo posible, los datos se repartan de la forma más homogénea posible entre todos los nodos. El objetivo es conseguir que todos los nodos tengan más o menos el mismo volumen de datos, sin que se descompense ninguno de ellos. Aunque para conseguirlo, es necesario que tengamos en cuenta cómo funciona este particionado y lo configuremos en base a atributos que permitan hacer más homogénea dicha división de datos.

Para el grupo de Sharding, MongoDB necesita tener almacenada, en otra BD, la configuración del grupo de particionado, para saber cómo se han repartido los datos (lo que permitirá optimizar las consultas), qué nodos forman parte del grupo de particionado (lo que permitirá optimizar las inserciones) y cuál es la política de particionado, entre otra información.

Esta información sobre la “geometría de cómo se particionan los datos”, se almacena habitualmente en una instancia específica de MongoDB, que sirve para guardar esta configuración.

Esta BD de configuración del particionado de datos es crítica para el funcionamiento y rendimiento de todo el sistema de sharding, dado que si esta base de datos se para, se pierde o se corrompe, el grupo de particionado no podrá saber dónde tiene que insertar, cómo tiene que dividir o dónde tiene que redirigir las operaciones de consulta.

Por este motivo, habitualmente la base de datos de configuración del sharding, se configura en modo de réplica (como ya describimos en el tutorial sobre Prueba de RéplicaSet en MongoDB).

En este proceso de levantar el Sharding, el objeto ShardingTest creará y levantará tantas instancias de mongod como nodos indiquemos que queremos utilizar para configurar nuestras pruebas del particionado de datos.
Además, el objeto ShardingTest también creará las instancias de mongod necesarias para guardar la configuración en un ReplicaSet.

Para levantar el clúster de réplica, desde la consola mongo que tenemos abierta, escribiremos el siguiente comando:

Con este comando indicamos que se cree un objeto ShardingTest que contiene el conjunto de procesos mongod que formarán parte del particionado de datos, así como las instancias mongod para la base de datos de configuración en modo réplica.

Como parámetro a construir el ShardingTest podemos personalizar la configuración de las instancias que formarán parte del particionado de datos. Las opciones de configuración completas se pueden consultar en la documentación de ShardingTest.

A diferencia de lo que ocurre con la creación de un grupo de réplica de prueba, a través de ReplSetTest, en el caso de un ShardingTest, al crearlo, directamente se instancian ya todos las instancias del demonio de mongo necesarias.

Vemos esta información en algunos de los fragmentos que obtendremos en las trazas (en la consola salen muchas más trazas, pero mostramos sólo las partes más descriptivas):

Lo primero es que vemos que se levantan la base de datos de configuración y configurada en modo réplica.

Al finalizar el arranque de esta base de datos de configuración, podemos ver la estructura de instancias de mongod para sharding que se almacena en la propia base de datos de configuración:

En ella podemos ver los elementos que hemos descrito antes:

  • Base de datos config: en modo replicaSet con nombre test-configRS y con tres nodos en el grupo de réplica para el almacenamiento de dicha configuración:
    • mongod en el puerto 20003,
    • mongod en el puesto 20004
    • mongod en el puerto 20005
  • Tres instancias, denominadas “shards”, para los nodos entre los que se realizará el particionado de datos:
    • mongod en el puerto 20000
    • mongod en el puerto 20001
    • mongod en el puerto 20002

También podemos ver en otro fragmento de las trazas, cómo se arrancan los procesos mongod que forman los shards.

Por último, se levantará un proceso mongos, que actúa como balanceador y que es el encargado de recibir las peticiones de consulta y las operaciones de escritura/modificación y, tras consultar en la BD de configuración los shards y la división de datos que se ha realizado, es capaz de redirigir la consulta al proceso (o al conjunto de procesos) mongod adecuados:

Al final de este proceso, podemos ver la comprobación final, que realiza el balanceador (el proceso mongos) de carga entre los nodos y que se irá repitiendo cada cierto tiempo para comprobar:

  • El número de shards que están definidos en la base de datos de configuración,
  • El tamaño del bloque (chunksize) donde se agrupan los datos particionados en cada shard
  • La necesidad o no de hacer un balanceo de datos (en el caso de que un nodo del shard tenga muchos más datos que otro).
  • Por último, comprueba que el grupo de réplica para la base de datos de configuración está vivo.

Este chequeo se irá repitiendo cada cierto tiempo, y se volcará en la consola de mongo que hemos abierto para arrancar el objeto ShardingTest.

Podemos comprobar las instancias de mongod que se han levantado.

Vemos en esta salida los procesos mongod arrancados para representar los nodos que forman parte del Sharding (los tres primeros), los nodos que actuarán en modo de réplica para almacenar la base de datos de configuración (del 4 al 6) y el proceso mongos (el último) que actúa como balanceador.

5. Prueba del particionado de datos y distribución entre los nodos del Shard

Una vez levantado el Sharding en Mongo DB, realizaremos las pruebas de inserción de datos para comprobar cómo se realiza el particionado.

5.1. Inserción de datos sobre el balanceador

Para actuar sobre el conjunto de Sharding, arrancaremos una nueva consola cliente de mongo contra el balanceador:

Conectamos al balanceador, que actuará a todos los efectos como nuestro servidor MongoDB que representa el conjunto de Shards completo.

Ejecutamos una inserción de documentos, en este caso vamos a simular una inserción de entradas en un blog (por ejemplo 100.000 entradas parece un buen número para probar)

Esta operación tardará un tiempo; pero al cabo de unos segundos obtendremos la confirmación de que se ha realizado la inserción

Podemos comprobar el número de datos insertados, de forma global entre todos los nodos del Shard, consultando directamente a través de la conexión al balanceador:

Como salida del comando obtenemos el número total de documentos almacenados en la colección blog_posts entre todos los nodos del Shard.

5.2. Comprobación (fallida) de la distribución de datos entre los nodos

Desde el balanceador, siempre tenemos la visión del conjunto de datos completo, para ver cómo se ha producido de forma efectiva la distribución de datos entre los distintos nodos, podemos conectarnos a los distintos procesos que forman parte del Shard.

Para ello, abriremos una nueva consola de Mongo desde la que nos conectaremos a cada uno de los nodos con objetos distintos.:

Desde esta consola, comenzamos por conectarnos al primero de los nodos del shard, el que se ha levantado escuchando en el puerto 20.000, obtenemos el acceso a la BD de test que se mantiene en este primer nodo, y consultamos el número de registros guardados en ella.

Repetimos la misma secuencia para comprobar los registros que se han almacenado en la base de datos del segundo nodo del Shard:

Por último, comprobamos sobre el tercer nodo del Shard.

Según esta comprobación, todos los documentos que insertamos se han almacenado en el primer nodo del shard, y los nodos 2 y 3 no contienen ningún documento en la colección blog_posts.

Según estas pruebas, podríamos concluir que el particionado de datos no funciona porque, de hecho, no se ha producido ningún tipo de reparto de datos entre los distintos nodos y el desequilibrio es obvio.

Antes de concluir que el Sharding en MongoDB no funciona, debemos saber que, por defecto, al crear el objeto para las pruebas de Sharding, no se activa el particionado y éste es el motivo de que no se reparta la carga de datos entre todos los nodos.

En el siguiente apartado veremos cómo activar el particionado en el Shard, comprobaremos cómo el balanceador detecta el desequilibrio y cómo entra en acción para corregir dicho desequilibrio moviendo datos entre los nodos y repartiendo la carga.

5.3. Activación del Sharding

Para activar la funcionalidad de particionado de datos, debemos actuar sobre el balanceador. Por tanto, volveremos a la consola de mongo que hemos arrancado contra la instancia de mongos (que corría en el puerto 20006).

En dicha consola, podremos comprobar, a través de la función status(), el estado del grupo de Sharding:

En este caso vemos, efectivamente, que el balanceador (el proceso que reparte la carga de datos entre los distintos nodos) no está activo (propiedad balancer: Currently enabled : no y balancer: Currently running: no) y, de hecho el particionado de datos para el shard0000 (el nombre que se le ha dado automáticamente a nuestro shard al crear el ShardingTest) no está particionado (partitioned : false)

Para activar el sharding utilizaremos la función enableSharding() sobre la base de datos que queremos que reparta sus datos entre todos los nodos:

Pero no es suficiente sólo con activar el particionado de datos. El proceso de reparto, se basa en un atributo (o conjunto de atributos) en función del cual MongoDB creará grupos de datos (chunks) que irá moviendo entre los distintos nodos.

Este concepto de chunks o grupos de datos en el shard se verá mucho mejor cuando comprobemos cómo se ha realizado la creación de dichos chunks y su distribución entre los nodos.

Para indicar este atributo, debemos crear un índice sobre la colección que vamos a particionar. Esto lo realizamos con la función ensureIndex() indicando como argumento el atributo de los objetos de la colección por el que queremos realizar los grupos de datos antes de repartirlos.

Con la creación de este índice lo que hacemos es marcar la restricción de que todos los documentos de la colección contienen ese atributo.

Una vez creado el índice, y como último paso para que MongoDB comience a distribuir los documentos de la colección entre los nodos de Shard es, precisamente, realizar el particionado de la colección mencionada. Esto lo conseguimos con la función shardCollection(), donde especificaremos los siguientes atributos:

    • base de datos y colección a particionar
    • atributo (especificado como objeto JSON, el mismo que utilizamos para definir el índice) por el que se agruparán los objetos en chunks y se distribuirán esos chunks entre los nodos.

Si volvemos a comprobar el estado del grupo de particionado, otra vez con la función status(), esta vez comprobaremos que:

En este caso el shard0000 está en modo particionado (partitioned : true), sin embargo el balancer (que es quien de forma efectiva moverá los datos) no se ha llegado a ejecutar.

De hecho, podemos comprobar su estado de ejecución con getBalancerState()

Ahora sólo nos queda conseguir que el balancer comience a ejecutarse, para ello, utilizamos la función setBalancerState(boolean).

En este momento, si nos movemos a la consola de mongo donde creamos el objeto ShardingTest, veremos que las trazas nos indican que el balanceador ha detectado el desequilibrio y está compensándolo:

Estas trazas las veremos repetirse, según va moviendo más bloques entre los nodos.

Podemos ir comprobando cuando acaba de ejecutar su tarea el balancer, invocando al método isBalancerRunning() hasta que nos diga que ha acabado.

5.4. Comprobación de la réplica de datos (correcto)

Una vez que ha acabado el balancer de equilibrar la carga de datos entre todos los nodos, podemos volver a repetir la consulta del apartado 5.2 y comprobaremos que esta vez el número de datos está más equilibrado.

Para ello, volvemos a la tercera consola de MongoDB, donde teníamos las tres conexiones a los tres nodos de mongod:

Ahora sí, efectivamente, podemos ver que los datos están distribuidos entre los tres nodos teniendo cada uno de ellos, aproximadamente un tercio del total y que, entre los tres, suman el grupo completo de los mismos (32.518 + 36.336 + 31.146 = 100.000).

5.5. Consulta de la distribución de chunks entre nodos

Utilizando la consola abierta contra el balanceador, podemos comprobar la agrupación de datos en chunks (en base al atributo que se utiliza como clave en el particionado) la ubicación de cada grupo en los nodos.

Para obtener esta información, ejecutaremos la función status():

En esta traza de consola podemos ver que esta vez el sharding activo y los números de grupos que contiene cada servidor (bajo la propiedad databases:chunks).

Si especificamos el nivel de detalle (pasando true como argumento a la función status()), obtendremos además la ubicación de cada chunk de datos(la lista de datos final bajo databases:chunks).

Por ejemplo, todos los documentos cuyo atributo author va desde el primero por orden natural (representado por "author" : { "$minKey" : 1 }) hasta el documento con valor del atributo author author14669, están ubicados en el nodo shard0001.

Todos los documentos con clave del author desde el author14669 al author19340 están ubicados en el nodo shard0002. Y así sucesivamente hasta los documentos con valor para el atributo author entre el author98764 y el valor máximo (representado por maxKey), que estarán ubicados en el nodo shard0000 del Shard.

6. Comprobación del mecanismo de distribución de consultas.

Toda la información sobre la agrupación de datos en chunks y la ubicación de estos sobre los distintos nodos del shard se almacenan en la base de datos de configuración (test-ConfigRS) que está, además, configurada en modo réplica dada su importancia.

Si se pierde o se corrompe esta información, mongoDB no podría, a priori, saber el estado del shard ni dónde tiene ubicados los distintos datos.

6.1. Consulta de un documento ubicado en un nodo concreto.

Internamente, MongoDB utiliza esta información de configuración para optimizar, en la medida de lo posible, las consultas sobre los distintos nodos del shard.

Así, una consulta o modificación sobre un documento buscandolo por el propio atributo que se ha definido para la división de datos (en nuestro caso por el atributo author) el balanceador puede saber a qué nodos dirigirla exclusivamente.

Podemos ver el plan de ejecución de la consulta de MongoDB, utilizando la función explain() sobre la propia consulta a realizar:

En este caso, vemos que el proceso mongos, tras consultar la distribución de chunks en test-ConfigRS, sabe que la consulta afecta sólo a información contenida en el nodo shard0000.

7. Parada del cluster de particionado de datos

Por último, para parar nuestro ShardingTest, volveremos a la consola inicial, donde creamos el objeto que levantó todos los nodos y ejecutaremos la función stop() sobre el cluster que tenemos:

Veremos como se comienza a enviar la secuencia de finalización de todos los procesos mongod y mongos y se van parando los distintos servicios.

Anexo A. Resolución de problemas

A.1. Todos los datos siguen en el mismo nodo, pese a haber activado el sharding sobre la colección y activar el balancer.

El balanceador sólo activa la división de un chunk de datos en al menos otros dos más pequeños susceptibles de ser repartidos entre los nodos del shard, cuando se ha alcanzado un tamaño máximo en dicho chunk.

Este tamaño está expresado en el atributo chunksize que se pasa como argumento al construir el ShardingTest. El valor se expresa en Mb.

Para las pruebas, conviene asegurarse de que se ha arrancado el ShardingTest con un valor de 1Mb como tamaño máximo. De otro modo, tendríamos que insertar muchísimos más valores para alcanzar el tamaño máximo del chunk y provocar su división y movimiento a otro nodo.


4 Comentarios

  1. no me sirve man, entiendo todo pero lamentablemente me saca error tan solo ejecutar el comando para crear el cluster cluster = new ShardingTest({shards : 3, chunksize : 1})
    me manda el siguiente error

    Resetting db path ‘/data/db/test0’
    2017-11-05T20:29:10.482-0500 E QUERY [thread1] Error: Caught std::exception of type class boost::filesystem::filesystem_error: boost::filesystem::create_directory: El sistema no puede encontrar la ruta especificada: «/data/db/test0» :
    MongoRunner.runMongod@src/mongo/shell/servers.js:722:17
    ShardingTest@src/mongo/shell/shardingtest.js:1141:24
    @(shell):1:11

Dejar respuesta

Please enter your comment!
Please enter your name here