example - Las operaciones de documentos de MongoDB son atómicas y aisladas, pero ¿son coherentes?
mongodb example (2)
Estoy en el proceso de trasladar mi aplicación de un almacén de datos de App Engine a un servidor de MongoDB y tengo una pregunta sobre la consistencia de las "actualizaciones de documentos". Entiendo que las actualizaciones en un documento son todas atómicas y aisladas, pero ¿hay alguna forma de garantizar que sean "coherentes" en los diferentes conjuntos de réplicas?
En nuestra aplicación, muchos usuarios pueden (y estarán) intentando actualizar un documento al mismo tiempo insertando algunos documentos incrustados (objetos) en una sola actualización. Debemos asegurarnos de que estas actualizaciones se realicen de manera lógica y coherente en todas las réplicas, es decir, cuando un usuario "coloca" algunos documentos incrustados en el documento principal, ningún otro usuario puede colocar sus documentos incrustados en el documento principal hasta que nos aseguremos de que hayan Leí y recibí las actualizaciones del primer usuario.
Entonces, lo que quiero decir con coherencia es que necesitamos una manera de garantizar que si dos usuarios intentan realizar una actualización en un documento exactamente al mismo tiempo, MongoDB solo permite que una de esas actualizaciones se realice, y descarta el otro (o al menos evita que ambos ocurran). No podemos usar una solución estándar de "fragmentación" aquí, porque una sola actualización consiste en algo más que un incremento o decremento.
¿Cuál es la mejor manera de garantizar la consistencia de un documento en particular?
MongoDB no ofrece replicación maestro-maestro o concurrencia de múltiples versiones. En otras palabras, las escrituras siempre van al mismo servidor en un conjunto de réplicas. De forma predeterminada, incluso las lecturas de los secundarios están deshabilitadas, por lo que el comportamiento predeterminado es que se comunica solo con un servidor a la vez. Por lo tanto, no necesita preocuparse por resultados inconsistentes en el modo seguro si usa modificadores atómicos (como $inc, $push
, etc.).
Si no quieres restringirte a estos modificadores atómicos, compara e intercambia según lo recomendado por dcrosta (y los documentos de Mongo) parece una buena idea. Sin embargo, todo esto no está relacionado con conjuntos de réplicas o fragmentación, sería lo mismo en un escenario de servidor único .
Si necesita garantizar la coherencia de lectura también en caso de una falla en la base de datos / nodo, debe asegurarse de que está escribiendo a la mayoría de los servidores en modo seguro.
Los dos enfoques se comportan de forma diferente si se permiten lecturas no seguras : las operaciones de actualización atómica seguirían funcionando (pero podrían dar resultados inesperados), mientras que el enfoque de comparación e intercambio fallaría.
Puede haber otras formas de lograr esto, pero un enfoque es la versión de sus documentos, y emitir actualizaciones solo contra la versión que el usuario había leído anteriormente (es decir, asegurarse de que nadie más haya actualizado el documento desde la última vez que se leyó). Aquí hay un breve ejemplo de esta técnica usando pymongo:
>>> db.foo.save({''_id'': ''a'', ''version'': 1, ''things'': []}, safe=True)
''a''
>>> db.foo.update({''_id'': ''a'', ''version'': 1}, {''$push'': {''things'': ''thing1''}, ''$inc'': {''version'': 1}}, safe=True)
{''updatedExisting'': True, ''connectionId'': 112, ''ok'': 1.0, ''err'': None, ''n'': 1}
nota en lo anterior, la tecla "n" es 1, lo que indica que el documento se actualizó
>>> db.foo.update({''_id'': ''a'', ''version'': 1}, {''$push'': {''things'': ''thing2''}, ''$inc'': {''version'': 1}}, safe=True)
{''updatedExisting'': False, ''connectionId'': 112, ''ok'': 1.0, ''err'': None, ''n'': 0}
aquí donde intentamos actualizar contra la versión incorrecta, la tecla "n" es 0
>>> db.foo.update({''_id'': ''a'', ''version'': 2}, {''$push'': {''things'': ''thing2''}, ''$inc'': {''version'': 1}}, safe=True)
{''updatedExisting'': True, ''connectionId'': 112, ''ok'': 1.0, ''err'': None, ''n'': 1}
>>> db.foo.find_one()
{''things'': [''thing1'', ''thing2''], ''_id'': ''a'', ''version'': 3}
Tenga en cuenta que esta técnica se basa en el uso de escrituras seguras, de lo contrario no obtenemos un acuse de recibo que indique el número de documentos actualizados. Una variación de esto usaría el comando findAndModify
, que devolverá el documento, o None
(en Python) si no se encuentra ningún documento que coincida con la consulta. findAndModify
permite devolver la versión nueva (es decir, después de aplicar las actualizaciones) o la versión anterior del documento.