MongoDB permite que varios clientes lean y escriban los mismos datos. Para garantizar la coherencia, MongoDB utiliza bloqueos y... Control de concurrencia para evitar que los clientes modifiquen los mismos datos simultáneamente. Las escrituras en un mismo documento se realizan completas o no se realizan, y los clientes siempre ven datos consistentes.
¿Qué tipo de bloqueo utiliza MongoDB?
MongoDB utiliza un bloqueo de granularidad múltiple [1] que permite a las operaciones bloquearse a nivel global, de base de datos o de colección, y permite que los motores de almacenamiento individuales implementen su propio control de concurrencia por debajo del nivel de colección (por ejemplo, a nivel de documento en WiredTiger).
MongoDB utiliza bloqueos de lectura-escritura que permiten a los lectores concurrentes un acceso compartido a un recurso, como una base de datos o una colección.
Además de un modo de bloqueo compartido (S) para las lecturas y un modo de bloqueo exclusiva (X) para las operaciones de guardado, los modos de intención compartida (IS) y de intención exclusiva (IX) indican una intención de lectura o guardado de un recurso utilizando un bloqueo de granularidad más fina. Al bloquear a una cierta granularidad, todos los niveles superiores se bloquean utilizando un bloqueo de intención.
Por ejemplo, al bloquear una colección para escritura (usando el modo X), tanto el bloqueo correspondiente de la base de datos como el bloqueo global deben estar bloqueados en modo de intención exclusiva (IX). Una única base de datos puede estar bloqueada simultáneamente en moda IS e IX, pero un bloqueo exclusivo (X) no puede coexistir con ningún otro modo, y un bloqueo compartido (S) solo puede coexistir con bloqueos de intención compartida (IS).
Los bloqueos son justos, con las solicitudes de bloqueo para lecturas y guardados en cola en orden. Sin embargo, para optimizar el rendimiento, cuando se concede una solicitud de bloqueo, todas las demás solicitudes de bloqueo compatibles se conceden simultáneamente, lo que puede liberar los bloqueos antes de que se ejecute una solicitud de bloqueo conflictiva. Por ejemplo, considera una situación en la que se acaba de liberar un bloqueo X y la cola de conflictos contiene estos bloqueos:
IS → IS → X → X → S → IS
En un estricto orden de primero en entrar, primero en salir (FIFO), solo se concederían las dos primeras modas IS. En lugar de eso, MongoDB realmente concederá todas las modas IS y S, y una vez que se agoten todas, concederá X, incluso si, mientras tanto, se han puesto en cola nuevas solicitudes IS o S. Como una concesión siempre adelanta a todas las demás solicitudes en la cola, no es posible que ninguna solicitud quede sin atender.
En db.serverStatus()En la salida y, los modos de bloqueo se representan de la siguiente db.currentOp() manera:
Modo de bloqueo | Descripción |
|---|---|
| Representa un bloqueo compartido (S). |
| Representa un bloqueo exclusivo (X). |
| Representa un bloqueo de intención compartida (IS). |
| Representa un bloqueo de intención exclusiva (IX). |
| [1] | Ver la página de Wikipedia sobre Bloqueo de granularidad múltiple para obtener más información. |
¿Qué tan granulares son los bloqueos en MongoDB?
Para la mayoría de las operaciones de lectura y escritura, WiredTiger usa un control de concurrencia optimista. WiredTiger usa únicamente bloqueos de intención a nivel global, de base de datos y de colección. Cuando el motor de almacenamiento detecta conflictos entre dos operaciones, una de ellas incurrirá en un conflicto de escritura, lo que hará que MongoDB reintente esa operación de manera transparente.
Algunas operaciones globales, típicamente operaciones de corta duración que involucran múltiples bases de datos, todavía requieren un bloqueo global "a nivel de instancia". Algunas otras operaciones, como renameCollection, todavía requieren un bloqueo exclusivo de la base de datos en ciertas circunstancias.
¿Cómo puedo ver el estado de los bloqueos en mis instancias de mongod?
Para obtener información sobre la utilización de bloqueos, usa cualquiera de estos métodos:
mongostat, y/oMongoDB Cloud Manager u Ops Manager, una solución on-premises disponible en MongoDB Enterprise Advanced
Específicamente, el documento locks en la salida de serverStatus, o el campo locks en el current operation reporting proporciona información sobre el tipo de bloqueos y la cantidad de contención de bloqueos en la instancia de mongod.
En la salida db.serverStatus() y db.currentOp(), los modos de bloqueo se representan de la siguiente manera:
Modo de bloqueo | Descripción |
|---|---|
| Representa un bloqueo compartido (S). |
| Representa un bloqueo exclusivo (X). |
| Representa un bloqueo de intención compartida (IS). |
| Representa un bloqueo de intención exclusiva (IX). |
Para finalizar una operación, utiliza db.killOp().
¿Una operación de lectura o escritura produce alguna vez el bloqueo?
En algunas situaciones, las operaciones de lectura y guardado pueden ceder sus bloqueos.
Las operaciones de lectura y guardado de larga duración, como los queries, actualizaciones y borrados, generan bloqueos en muchas condiciones. Las operaciones de MongoDB también pueden generar bloqueos entre las modificaciones de documentos individuales en operaciones de guardado que afectan a varios documentos.
Para los motores de almacenamiento que admiten el control de concurrencia a nivel de documento, como WiredTiger, no es necesario ceder al acceder al almacenamiento, ya que los bloqueos de intención, mantenidos a nivel global, de base de datos y de colección, no bloquean a otros lectores y escritores. Sin embargo, las operaciones cederán periódicamente, como:
para evitar transacciones de almacenamiento de larga duración, ya que estas pueden requerir mantener una gran cantidad de datos en la memoria;
para que sirvan como puntos de interrupción de modo que puedas finalizar las operaciones de larga duración;
para permitir operaciones que requieren acceso exclusivo a una colección, como descartar y crear índices y colecciones.
¿Qué bloqueos son tomados por algunas operaciones comunes del cliente?
La siguiente tabla enumera algunas operaciones y los tipos de bloqueos que utilizan los motores de almacenamiento con bloqueo a nivel de documento:
Operación | Database | Colección |
|---|---|---|
Emitir un query |
|
|
Insert data |
|
|
Remover datos |
|
|
Update data |
|
|
Realizar agregación |
|
|
Crear un índice |
| |
Listar colecciones |
| |
Map-Reduce |
|
|
Nota
Crear un índice requiere un bloqueo exclusivo (W) en una colección. Sin embargo, el bloqueo no se retiene durante toda la duración del proceso de creación de índices.
Para obtener más información, consulta Creación de índices en colecciones pobladas.
¿Qué comandos administrativos bloquean una base de datos?
Algunos comandos administrativos pueden establecer un bloqueo exclusivo en una base de datos durante largos períodos de tiempo. Para clústeres grandes, considera llevar la instancia de mongod fuera de línea para que los clientes no se vean afectados. Por ejemplo, si un mongod es parte de un set de réplicas, lleva el mongod fuera de línea y permite que otros nodos del set de réplicas procesen solicitudes mientras se realiza el mantenimiento.
Comandos administrativos que requieren bloqueos extendidos
Estas operaciones administrativas requieren un bloqueo exclusivo a nivel de base de datos durante períodos prolongados:
cloneCollectionAsCappedComandoconvertToCappedComando
Además, el comando renameCollection y el método shell correspondiente db.collection.renameCollection() toman los siguientes bloqueos:
Comando | Comportamiento de bloqueo |
|---|---|
| Si renombras una colección dentro de la misma base de datos, el comando Si el namespace de destino está en una base de datos diferente a la de la colección de origen, el comando |
| El método |
¿Qué comandos administrativos bloquean una colección?
Estas operaciones administrativas requieren un bloqueo exclusivo a nivel de la colección:
El comando
createy los métodos de shell correspondientesdb.createCollection()ydb.createView().El comando
createIndexesy los métodos de shell correspondientesdb.collection.createIndex()ydb.collection.createIndexes(). El proceso de compilación solo mantiene un bloqueo exclusivo en la colección al inicio y al final de la creación de índices.El comando
dropy los métodos de shell correspondientesdb.collection.drop().El comando
dropIndexesy los métodos de shell correspondientesdb.collection.dropIndex()ydb.collection.dropIndexes().El comando
renameCollectiony el método de shell correspondientedb.collection.renameCollection()toman los siguientes bloqueos, dependiendo de la versión:Para
renameCollectionydb.collection.renameCollection(): Si se renombra una colección dentro de la misma base de datos, la operación adquiere un bloqueo exclusivo (W) en las colecciones de origen y destino.Para
renameCollectionsolamente: Si el namespace de destino está en una base de datos diferente a la de la colección de origen, la operación toma un bloqueo exclusivo (W) en la base de datos de destino al renombrar una colección entre bases de datos y bloquea otras operaciones en esa base de datos hasta que finaliza.
El comando
reIndexy el método de shell correspondientedb.collection.reIndex()obtienen un bloqueo exclusivo (W) en la colección y bloquean otras operaciones en la colección hasta que se completan.El comando
replSetResizeOplogadquiere un bloqueo exclusivo (W) sobre la colecciónoplogy bloquea otras operaciones en la colección hasta que se complete.
¿En algún momento una operación de MongoDB bloquea más de una base de datos?
Estas operaciones de MongoDB pueden obtener y mantener un bloqueo en más de una base de datos:
Operación | Comportamiento |
|---|---|
Estas operaciones solo obtienen un bloqueo exclusivo (W) de colección en lugar de un bloqueo exclusivo global. | |
Esta operación obtiene un bloqueo exclusivo (W) en la base de datos de destino, un bloqueo de intención compartida (r) en la base de datos de origen y un bloqueo compartido (S) en la colección de origen al renombrar una colección en distintas bases de datos. Al renombrar una colección en la misma base de datos, la operación solo requiere bloqueos exclusivos (W) en las colecciones de origen y destino. | |
La operación solo obtiene un bloqueo exclusivo (W) en la colección |
¿Cómo afecta la fragmentación a la concurrencia?
El particionado mejora la concurrencia al distribuir colecciones en múltiples instancias de mongod, permitiendo que los servidores de particionado (específicamente, los procesos de mongos) se ejecuten simultáneamente con las instancias de mongod posteriores.
En un clúster particionado, los bloqueos se aplican a cada partición individual, no a todo el clúster; es decir, cada instancia mongod es independiente de las demás en el clúster particionado y utiliza sus propios bloqueos. Las operaciones en una instancia de mongod no bloquean las operaciones en ninguna de las otras.
¿Cómo afecta la concurrencia a un set de réplicas primario?
Con los sets de réplicas, cuando MongoDB escribe en una colección, en la primaria, MongoDB también escribe en el oplog de la primaria, que es una colección especial en la base de datos local. Por lo tanto, MongoDB debe realizar un bloqueo tanto en la base de datos de la colección como en la base de datos local. El mongod debe realizar el bloqueo de ambas bases de datos al mismo tiempo para mantener la base de datos coherente y asegurar que las operaciones de guardado, incluso con replicación, sean operaciones de todo o nada.
Al escribir en un set de réplicas, el alcance del bloqueo se aplica al primario.
¿Cómo afecta la concurrencia a los índices secundarios?
En replicación, MongoDB no aplica escrituras de forma secuencial a los secundarios. Los secundarios recopilan entradas de oplog en agrupaciones y luego aplican esas agrupaciones en paralelo. Las operaciones de guardado se aplican en el orden en que aparecen en el oplog.
Las lecturas que se dirigen a los secundarios se realizan a partir de un snapshot de WiredTiger de los datos si el secundario está en proceso de replicación. Esto permite que la lectura se produzca simultáneamente con la replicación, mientras sigue ofreciendo las garantías de una vista coherente de los datos.
¿MongoDB admite transacciones?
Debido a que un único documento puede contener datos relacionados que, de otro modo, se modelarían en tablas principal-secundario separadas en un esquema relacional, las operaciones atómicas de un solo documento de MongoDB ya proporcionan una semántica de transacción que satisface las necesidades de integridad de datos de la mayoría de las aplicaciones. Uno o más campos pueden guardarse en una sola operación, incluidas las actualizaciones de múltiples subdocumentos y elementos de un arreglo. Las garantías que ofrece MongoDB aseguran un aislamiento completo al actualizarse un documento; cualquier error provoca que la operación se revierta para que los clientes reciban una vista coherente del documento.
Para situaciones que requieren atomicidad de las lecturas y escrituras en varios documentos (en una sola colección o en varias), MongoDB admite transacciones distribuidas, incluidas las transacciones en sets de réplica y clústeres fragmentados.
Para obtener más información, consulta transacciones.
Importante
En la mayoría de los casos, una transacción distribuida incurre en un costo de rendimiento mayor que las escrituras de documentos individuales, y la disponibilidad de transacciones distribuidas no debería ser un sustituto para un diseño de esquema efectivo. Para muchos casos, el modelo de datos desnormalizado (documento incrustado y matrices) seguirá siendo óptimo para tus datos y casos de uso. Es decir, en muchos casos, modelar tus datos de forma adecuada minimizará la necesidad de transacciones distribuidas.
Para consideraciones adicionales sobre el uso de transacciones (como el límite de tiempo de ejecución y el límite de tamaño del oplog), consulta también las consideraciones de producción.
¿Qué garantías de aislamiento proporciona MongoDB?
Dependiendo del nivel de consistencia de lectura, los clientes pueden ver los resultados de los guardados antes de que se vuelvan durables. Para controlar si la lectura de los datos puede revertirse o no, los clientes pueden utilizar la opción readConcern.
¿Cuáles son las operaciones de lectura sin bloqueo?
Nuevo en la versión 5.0.
Una operación de lectura sin bloqueo se ejecuta inmediatamente: no se bloquea cuando otra operación tiene un bloqueo de escritura exclusivo (X) en la colección.
A partir de MongoDB 5.0, las siguientes operaciones de lectura no se bloquean cuando otra operación mantiene un bloqueo de escritura exclusivo (X) en la colección:
Al guardar en una colección, mapReduce y aggregate mantienen un bloqueo de intento exclusivo (IX). Por lo tanto, si ya se tiene un bloqueo exclusivo X sobre una colección, mapReduce y aggregate las operaciones de guardado están bloqueadas.
Para obtener información, consulta: