MongoDB Sharding: Guía práctica paso a paso

Sharding es un proceso de dividir la gran escala de conjuntos de datos en una porción de conjuntos de datos más pequeños en varias instancias de MongoDB en un entorno distribuido.

¿Qué es fragmentación?

La fragmentación de MongoDB nos brinda una solución escalable para almacenar una gran cantidad de datos entre la cantidad de servidores en lugar de almacenarlos en un solo servidor.

En términos prácticos, no es factible almacenar datos que crecen exponencialmente en una sola máquina. La consulta de una gran cantidad de datos almacenados en un solo servidor podría conducir a una alta utilización de recursos y es posible que no proporcione un rendimiento de lectura y escritura satisfactorio.

Básicamente, existen dos tipos de métodos de escalado para llevar a cabo el crecimiento de datos con el sistema:

Vertical Scaling funciona para mejorar el rendimiento de un solo servidor al agregar procesadores más potentes, actualizar la memoria RAM o agregar más espacio en disco al sistema. Pero existen las posibles implicaciones de aplicar el escalado vertical en casos de uso práctico con la tecnología y las configuraciones de hardware existentes.

El escalado horizontal funciona al agregar más servidores y distribuir la carga en varios servidores. Dado que cada máquina manejará el subconjunto del conjunto de datos completo, proporciona una mejor eficiencia y una solución rentable en lugar de implementar el hardware de alta gama. Pero requiere un mantenimiento adicional de una infraestructura compleja con una gran cantidad de servidores.

La fragmentación de Mongo DB funciona con la técnica de escalado horizontal.

Fragmentación de componentes

Para lograr la fragmentación en MongoDB, se requieren los siguientes componentes:

Shard es una instancia de Mongo para manejar un subconjunto de datos originales. Es necesario implementar fragmentos en el conjunto de réplicas.

Mongos es una instancia de Mongo y actúa como una interfaz entre una aplicación cliente y un clúster fragmentado. Funciona como un enrutador de consultas a fragmentos.

Config Server es una instancia de Mongo que almacena información de metadatos y detalles de configuración del clúster. MongoDB requiere que el servidor de configuración se implemente como un conjunto de réplicas.

Arquitectura fragmentada

El clúster de MongoDB consta de varios conjuntos de réplicas.

Cada conjunto de réplicas consta de un mínimo de 3 o más instancias de mongo. Un clúster fragmentado puede constar de varias instancias de fragmentos de mongo, y cada instancia de fragmento funciona dentro de un conjunto de réplicas de fragmentos. La aplicación interactúa con Mongos, que a su vez se comunica con fragmentos. Por lo tanto, en Sharding, las aplicaciones nunca interactúan directamente con los nodos de fragmentos. El enrutador de consultas distribuye los subconjuntos de datos entre los nodos de fragmentos en función de la clave de fragmento.

Implementación de fragmentación

Siga los pasos a continuación para fragmentar

Paso 1

  • Inicie el servidor de configuración en el conjunto de réplicas y habilite la replicación entre ellos.

mongod –configsvr –puerto 27019 –replSet rs0 –dbpath C:datadata1 –bind_ip localhost

mongod –configsvr –puerto 27018 –replSet rs0 –dbpath C:datadata2 –bind_ip localhost

mongod –configsvr –puerto 27017 –replSet rs0 –dbpath C:datadata3 –bind_ip localhost

Paso 2

  • Inicialice el conjunto de réplicas en uno de los servidores de configuración.

rs.initiate( { _id : “rs0”, configsvr: verdadero, miembros: [   { _id: 0, host: “IP:27017” },   { _id: 1, host: “IP:27018” },   { _id: 2, host: “IP:27019” }    ] })

rs.initiate( { _id : "rs0",  configsvr: true,  members: [   { _id: 0, host: "IP:27017" },   { _id: 1, host: "IP:27018" },   { _id: 2, host: "IP:27019" }    ] })
{
        "ok" : 1,
        "$gleStats" : {
                "lastOpTime" : Timestamp(1593569257, 1),
                "electionId" : ObjectId("000000000000000000000000")
        },
        "lastCommittedOpTime" : Timestamp(0, 0),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593569257, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1593569257, 1)
}

Paso 3

  • Inicie la fragmentación de servidores en el conjunto de réplicas y habilite la replicación entre ellos.

mongod –shardsvr –puerto 27020 –replSet rs1 –dbpath C:datadata4 –bind_ip localhost

mongod –shardsvr –puerto 27021 –replSet rs1 –dbpath C:datadata5 –bind_ip localhost

mongod –shardsvr –puerto 27022 –replSet rs1 –dbpath C:datadata6 –bind_ip localhost

MongoDB inicializa el primer servidor de fragmentación como Primario, para mover el uso del servidor de fragmentación principal movePrimary método.

Paso 4

  • Inicialice el conjunto de réplicas en uno de los servidores fragmentados.

rs.initiate( { _id : “rs0”, miembros: [   { _id: 0, host: “IP:27020” },   { _id: 1, host: “IP:27021” },   { _id: 2, host: “IP:27022” }    ] })

rs.initiate( { _id : "rs0",  members: [   { _id: 0, host: "IP:27020" },   { _id: 1, host: "IP:27021" },   { _id: 2, host: "IP:27022" }    ] })
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593569748, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1593569748, 1)
}

Paso 5

  • Comience los mangos para el racimo fragmentado

mongos –puerto 40000 –configdb rs0/localhost:27019, localhost:27018, localhost:27017

Paso 6

  • Conectar el servidor de ruta mongo

mongo-puerto 40000

  • Ahora, agregue servidores de fragmentación.

sh.addShard («rs1/host local: 27020, host local: 27021, host local: 27022»)

sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
{
        "shardAdded" : "rs1",
        "ok" : 1,
        "operationTime" : Timestamp(1593570212, 2),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593570212, 2),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Paso 7

  • En Mongo Shell, habilite la fragmentación en DB y colecciones.
  • Habilitar fragmentación en base de datos

sh.enableSharding(“geekFlareDB”)

sh.enableSharding("geekFlareDB")
{
        "ok" : 1,
        "operationTime" : Timestamp(1591630612, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1591630612, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Paso 8

  • Se requiere fragmentar la clave de fragmento de colección (que se describe más adelante en este artículo).

Sintaxis: sh.shardCollection(“dbName.collectionName”, { “key” : 1 } )

sh.shardCollection("geekFlareDB.geekFlareCollection", { "key" : 1 } )
{
        "collectionsharded" : "geekFlareDB.geekFlareCollection",
        "collectionUUID" : UUID("0d024925-e46c-472a-bf1a-13a8967e97c1"),
        "ok" : 1,
        "operationTime" : Timestamp(1593570389, 3),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593570389, 3),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Tenga en cuenta que si la colección no existe, créela de la siguiente manera.

db.createCollection("geekFlareCollection")
{
        "ok" : 1,
        "operationTime" : Timestamp(1593570344, 4),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593570344, 5),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Paso 9

Insertar datos en la colección. Los registros de Mongo comenzarán a crecer e indicarán que un equilibrador está en acción e intentará equilibrar los datos entre fragmentos.

Paso 10

El último paso es comprobar el estado de la fragmentación. El estado se puede verificar ejecutando el siguiente comando en el nodo de ruta de Mongos.

Estado de fragmentación

Verifique el estado de fragmentación ejecutando el siguiente comando en el nodo de ruta mongo.

sh.estado()

mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "minCompatibleVersion" : 5,
        "currentVersion" : 6,
        "clusterId" : ObjectId("5ede66c22c3262378c706d21")
  }
  shards:
        {  "_id" : "rs1",  "host" : "rs1/localhost:27020,localhost:27021,localhost:27022",  "state" : 1 }
  active mongoses:
        "4.2.7" : 1
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  5
        Last reported error:  Could not find host matching read preference { mode: "primary" } for set rs1
        Time of Reported error:  Tue Jun 09 2020 15:25:03 GMT+0530 (India Standard Time)
        Migration Results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                rs1     1024
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "geekFlareDB",  "primary" : "rs1",  "partitioned" : true,  "version" : {  "uuid" : UUID("a770da01-1900-401e-9f34-35ce595a5d54"),  "lastMod" : 1 } }
                geekFlareDB.geekFlareCol
                        shard key: { "key" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                rs1     1
                        { "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
                geekFlareDB.geekFlareCollection
                        shard key: { "product" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                rs1     1
                        { "product" : { "$minKey" : 1 } } -->> { "product" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
        {  "_id" : "test",  "primary" : "rs1",  "partitioned" : false,  "version" : {  "uuid" : UUID("fbc00f03-b5b5-4d13-9d09-259d7fdb7289"),  "lastMod" : 1 } }

mongos>

Distribución de datos

El enrutador Mongos distribuye la carga entre los fragmentos en función de la clave del fragmento y para distribuir los datos de manera uniforme; El equilibrador entra en acción.

El componente clave para distribuir datos entre fragmentos son

  • Un equilibrador desempeña un papel en el equilibrio del subconjunto de datos entre los nodos fragmentados. Balancer se ejecuta cuando el servidor Mongos comienza a distribuir cargas entre fragmentos. Una vez iniciado, Balancer distribuyó los datos de manera más uniforme. Para verificar el estado del balanceador, ejecute sh.status() o sh.getBalancerState() o sh.isBalancerRunning().
mongos> sh.isBalancerRunning()
true
mongos>

O

mongos> sh.getBalancerState()
true
mongos>

Después de insertar los datos, podemos notar cierta actividad en el daemon de Mongos que indica que está moviendo algunos fragmentos para los fragmentos específicos y así sucesivamente, es decir, el balanceador estará en acción tratando de equilibrar los datos entre los fragmentos. Ejecutar el equilibrador podría provocar problemas de rendimiento; por lo tanto, se sugiere ejecutar el equilibrador dentro de un cierto ventana del equilibrador.

mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "minCompatibleVersion" : 5,
        "currentVersion" : 6,
        "clusterId" : ObjectId("5efbeff98a8bbb2d27231674")
  }
  shards:
        {  "_id" : "rs1",  "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022",  "state" : 1 }
        {  "_id" : "rs2",  "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",  "state" : 1 }
  active mongoses:
        "4.2.7" : 1
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  yes
        Failed balancer rounds in last 5 attempts:  5
        Last reported error:  Could not find host matching read preference { mode: "primary" } for set rs2
        Time of Reported error:  Wed Jul 01 2020 14:39:59 GMT+0530 (India Standard Time)
        Migration Results for the last 24 hours:
                1024 : Success
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                rs2     1024
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "geekFlareDB",  "primary" : "rs2",  "partitioned" : true,  "version" : {  "uuid" : UUID("a8b8dc5c-85b0-4481-bda1-00e53f6f35cd"),  "lastMod" : 1 } }
                geekFlareDB.geekFlareCollection
                        shard key: { "key" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                rs2     1
                        { "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs2 Timestamp(1, 0)
        {  "_id" : "test",  "primary" : "rs2",  "partitioned" : false,  "version" : {  "uuid" : UUID("a28d7504-1596-460e-9e09-0bdc6450028f"),  "lastMod" : 1 } }

mongos>
  • Shard Key determina la lógica para distribuir documentos de la colección fragmentada entre los fragmentos. La clave fragmentada puede ser un campo indexado o un campo compuesto indexado que debe estar presente en todos los documentos de la colección que se insertará. Los datos se dividirán en fragmentos, y cada fragmento se asociará con la clave de fragmento basada en el rango. Sobre la base de la consulta de rango, el enrutador decidirá qué fragmento almacenará el fragmento.

Shard Key se puede seleccionar considerando cinco propiedades:

  • Cardinalidad
  • Distribución de escritura
  • Leer distribución
  • Orientación de lectura
  • Leer localidad

Una clave de fragmento ideal hace que MongoDB distribuya uniformemente la carga entre todos los fragmentos. Elegir una buena clave de fragmento es extremadamente importante.

Imagen: MongoDB

Quitar el nodo del fragmento

Antes de eliminar fragmentos del clúster, el usuario debe garantizar la migración segura de los datos a los fragmentos restantes. MongoDB se encarga de drenar datos de manera segura a otros nodos de fragmentos antes de eliminar el nodo de fragmento requerido.

Ejecute el siguiente comando para eliminar el fragmento requerido.

Paso 1

Primero, debemos determinar el nombre de host del fragmento que se eliminará. El siguiente comando enumerará todos los fragmentos presentes en el clúster junto con el estado del fragmento.

db.adminCommand( { listFragmentos: 1 } )

mongos> db.adminCommand( { listShards: 1 } )
{
        "shards" : [
                {
                        "_id" : "rs1",
                        "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022",
                        "state" : 1
                },
                {
                        "_id" : "rs2",
                        "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
                        "state" : 1
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1593572866, 15),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593572866, 15),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Paso 2

Emita el siguiente comando para eliminar el fragmento necesario del clúster. Una vez emitido, el balanceador se encarga de eliminar los fragmentos del nodo de fragmentos de drenaje y luego equilibra la distribución de los fragmentos restantes entre los demás nodos de fragmentos.

db.adminCommand( { removeShard: “ShardedReplicaNodes” } )

mongos> db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
{
        "msg" : "draining started successfully",
        "state" : "started",
        "shard" : "rs1",
        "note" : "you need to drop or movePrimary these databases",
        "dbsToMove" : [ ],
        "ok" : 1,
        "operationTime" : Timestamp(1593572385, 2),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593572385, 2),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Paso 3

Para verificar el estado del fragmento de drenaje, emita el mismo comando nuevamente.

db.adminCommand( { removeShard: “rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022” } )

Tenemos que esperar hasta que se complete el drenaje de los datos. Los campos msg y state mostrarán si el drenaje de datos se ha completado o no, de la siguiente manera

"msg" : "draining ongoing",
"state" : "ongoing",

También podemos comprobar el estado con el comando sh.status(). Una vez eliminado, el nodo fragmentado no se reflejará en la salida. Pero si el drenaje está en curso, el nodo fragmentado vendrá con el estado de drenaje como verdadero.

Paso 4

Continúe verificando el estado del drenaje con el mismo comando anterior, hasta que el fragmento requerido se elimine por completo.
Una vez completado, la salida del comando reflejará el mensaje y el estado como completado.

"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "rs1",
"ok" : 1,

Paso 5

Finalmente, debemos verificar los fragmentos restantes en el clúster. Para comprobar el estado, introduzca sh.status() o db.adminCommand( { listShards: 1 } )

mongos> db.adminCommand( { listShards: 1 } )
{
        "shards" : [
                {
                        "_id" : "rs2",
                        "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
                        "state" : 1
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1593575215, 3),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1593575215, 3),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

Aquí, podemos ver que el fragmento eliminado ya no está presente en la lista de fragmentos.

Beneficios de la fragmentación sobre la replicación

  • En la replicación, el nodo principal maneja todas las operaciones de escritura, mientras que los servidores secundarios son necesarios para mantener copias de respaldo o atender operaciones de solo lectura. Pero al fragmentar junto con conjuntos de réplicas, la carga se distribuye entre varios servidores.
  • Un único conjunto de réplicas está limitado a 12 nodos, pero no hay restricciones en cuanto a la cantidad de fragmentos.
  • La replicación requiere hardware de alta gama o escalado vertical para manejar grandes conjuntos de datos, lo cual es demasiado costoso en comparación con agregar servidores adicionales en fragmentación.
  • En la replicación, el rendimiento de lectura se puede mejorar agregando más servidores esclavos/secundarios, mientras que, en la fragmentación, tanto el rendimiento de lectura como el de escritura se mejorarán al agregar más nodos de fragmentos.

Limitación de fragmentación

  • El clúster fragmentado no admite la indexación única en los fragmentos hasta que el índice único tenga como prefijo la clave de fragmento completa.
  • Todas las operaciones de actualización para la recopilación fragmentada en uno o varios documentos deben contener la clave fragmentada o el campo _id en la consulta.
  • Las colecciones se pueden fragmentar si su tamaño no supera el umbral especificado. Este umbral se puede estimar sobre la base del tamaño promedio de todas las claves de fragmentos y el tamaño configurado de los fragmentos.
  • La fragmentación se compone de límites operativos sobre el tamaño máximo de la colección o el número de divisiones.
  • Elegir las claves de fragmento incorrectas para dar lugar a implicaciones de rendimiento.

Conclusión

MongoDB ofrece fragmentación integrada para implementar una gran base de datos sin comprometer el rendimiento. Espero que lo anterior le ayude a configurar la fragmentación de MongoDB. A continuación, es posible que desee familiarizarse con algunos de los comandos de MongoDB más utilizados.