Servidor Couchbase

Propiedades ACID de Couchbase: Parte 1

Nota importante: Transacciones Multi-Documento ACID están ahora disponibles en Couchbase. Ver: Transacciones ACID para aplicaciones NoSQL para más información

Las propiedades ACID son un tema sobre el que me preguntan mucho. Generalmente, la gente pregunta en el contexto de las transacciones: "¿Hay transacciones NoSQL?", "¿Puedo usar transacciones ACID en Couchbase?", y así sucesivamente. Pero las aplicaciones distribuidas de hoy en día no siempre esperan o necesitan todas las propiedades ACID de su base de datos. Profundizaré en cómo las propiedades NoSQL y ACID se unen en Couchbase.

Antes, definamos de qué estamos hablando. ACID significa: Atomicidad, Consistencia, Aislamiento, Durabilidad. Se acuñó en los años 80, pero existe desde los 70 en las bases de datos relacionales tradicionales no distribuidas. Describe una clase de base de datos capaz de proporcionar operaciones con garantías ACID.

Couchbase y las transacciones

La escalabilidad, el rendimiento y la flexibilidad han sido el objetivo principal de la plataforma de datos Couchbase, y las transacciones distribuidas y multidocumento suelen estar reñidas con estas características.

La primera parte de esta entrada de blog de dos partes va a cubrir los componentes básicos de ACID que son disponibles en Couchbase. Puedes usar estas "primitivas" sin sacrificar el escalado general, el rendimiento y la flexibilidad de Couchbase. Dependiendo de tu caso de uso, estas primitivas pueden ser adecuadas por sí solas, o en conjunto con otras.

Actualmente, Couchbase soporta muchas propiedades ACID en documentos individuales, pero no proporciona soporte para transacciones multi-documento. En la parte 2 de esta serie, mostraré un enfoque que utiliza estos bloques de construcción para hacer algo como una transacción multi-documento con Couchbase. Pero primero, es importante tener una comprensión completa de los bloques de construcción.

¿Cuáles son las propiedades ACID?

Las aplicaciones han evolucionado desde aplicaciones monolíticas hasta aplicaciones distribuidas basadas en microservicios. Los microservicios aún esperan ciertos aspectos de la transaccionalidad, como un commit atómico o un rollback, pero no necesariamente un comportamiento ACID completo. El comportamiento ACID completo puede seguir siendo importante, pero en el software web y móvil moderno, a menudo es menos prioritario que el rendimiento y la escalabilidad. Sin embargo, no es una situación de "o lo uno o lo otro". Couchbase proporciona algunas herramientas y capacidades para ayudarte a equilibrar las propiedades ACID y el rendimiento.

A de Atomicidad

"Atomicidad" significa que un grupo de operaciones o todas tienen éxito o todas fallan. Couchbase proporciona atomicidad para documentos individuales. Una operación para obtener o establecer un documento tiene éxito o falla. Comparado con una garantía RDBMS de varios operaciones que triunfan o fracasan juntas, esto puede no parecer gran cosa.

Pero considera que modelado de datos es muy diferente entre las bases de datos documentales y las tecnologías de bases de datos relacionales.

En una base de datos relacional, normalmente normalizar los datos. Por ejemplo, para almacenar un carrito de la compra con 3 artículos, necesitarías:

  • 1 fila en una tabla ShoppingCart
  • 3 filas en una tabla ShoppingCartItems

Shopping Cart example for ACID properties

Si desea crear un carro de la compra con 3 artículos, necesitará cuatro INSERTAR sentencias. Por lo tanto, puede que necesite que su base de datos relacional trate esas 4 sentencias atómicamente.

Consideremos ahora una base de datos de documentos. Se crearía un único documento JSON del carrito de la compra, que contendría 3 artículos.

Obtener o establecer este documento es una única operación atómica en Couchbase. Un modelo completamente normalizado no es necesario a menudo. Si ampliara este modelo para incluir la dirección, y mi dirección cambia un mes después de enviar un pedido, ¿cambia realmente el pedido al que se envió la dirección? La normalización pretende en parte reducir la duplicación de datos. Esto es lo correcto y eficiente en algunas situaciones, pero no en todas.

Si puede modelar sus datos de este modo, reducirá inmediatamente la necesidad de una transacción multioperación (si le preocupa que el tamaño del documento se dispare, no se preocupe, puede utilizar un archivo subdocumento o N1QL para operar sobre una parte del documento).

C de coherencia

"Coherencia" significa en general que una operación "no violará las restricciones de integridad del sistema declaradas". Cualquier restricción en la base de datos sobre los datos permanece consistente antes y después de una operación ACID. Por ejemplo, si falla un conjunto atómico de operaciones, los datos siguen siendo coherentes con lo que eran antes de la operación (porque se puede volver atrás). Hay otros tipos de restricciones de consistencia que una base de datos puede imponer: en una base de datos relacional, por ejemplo, no se pueden insertar 6 columnas de datos en una tabla de 5 columnas.

En lugar de un esquema, Couchbase utiliza JSON. Cada documento contiene implícitamente su propio esquema. Si realizas una operación de inserción con JSON, debes suministrar JSON válido (los SDKs de Couchbase normalmente se encargan de esto por ti). Couchbase sólo impone esto a nivel de documento. Couchbase no puede evitar que uses un esquema JSON diferente de documento a documento. Por ejemplo, considera estos dos documentos:

Un cubo Couchbase permite ambos documentos. Tenga en cuenta que los nombres de los campos no son coherentes entre sí. Esto significa que hay más responsabilidad a nivel de aplicación para asegurar que los documentos tienen nombres consistentes (pero normalmente, esto se derivará de su modelo de datos, y por lo tanto se maneja automáticamente).

Otra restricción que impone Couchbase es la clave. Cada documento debe tener una clave única. Si intentas insertar otro documento con una clave de "user::mgroves", por ejemplo, se producirá un error. Por tanto, si necesita imponer una restricción única, una forma de conseguirlo es utilizar la clave del documento. (También es ventajoso utilizar una clave natural cuando sea posible para que un cluster de Couchbase pueda realizar una búsqueda directa).

Coherencia de las consultas

Por último, quiero mencionar índice de coherencia. Wcon el lenguaje de consulta N1QL, Couchbase es capaz de indexar campos o combinaciones de campos en documentos JSON (igual que las bases de datos relacionales pueden indexar columnas o combinaciones de columnas). Sin embargo, Couchbase actualiza los índices de forma asíncrona para proporcionar un mejor rendimiento que otros tipos de bases de datos. Cuando se ejecuta una consulta N1QL, el comportamiento por defecto es "Not Bounded". Esto significa que el motor de consulta devolverá resultados basados en el estado actual del sistema. Así que, hipotéticamente, si usted crea un nuevo documento e inmediatamente ejecuta una consulta, ese documento podría no estar en los resultados.

Afortunadamente, existen otras dos opciones para ajustar la coherencia de las consultas: RequestPlus y AtPlus.

SolicitarPlus se encuentra en el extremo opuesto del espectro de la coherencia. Esperará a que todos los documentos conocidos por el clúster formen parte de los recálculos del índice antes de procesar la consulta. La contrapartida es, por supuesto, la latencia. Una consulta RequestPlus puede tardar más en ejecutarse.

AtPlus está en el medio. En lugar de esperar a que se complete un índice completo, AtPlus sólo esperará a la indexación de los documentos conocidos por una instancia concreta de su aplicación. Esto proporciona una latencia más baja con una ventana estrecha de consistencia, pero requiere más trabajo para el SDK. La solicitud de consulta también debe ser gestionada por la misma instancia.

Nota: La "consistencia" en ACID es diferente que la "Coherencia" en el Teorema CAP. Cuando piense en Couchbase y CAP, recuerde que se trata de un muy coherente base de datos distribuida. Esto significa que cada documento tiene un único documento correcto por clúster, y no puede haber un documento "conflictivo" o "hermano" con la misma clave en otra parte del clúster. Una base de datos fuertemente coherente no implican nada sobre transacciones o propiedades ACID.

I de Aislamiento

"Aislamiento" es la capacidad de que una operación se produzca sólo después de que se haya completado otra operación sobre los mismos datos. De este modo, cada operación independiente de otras operaciones. Esto es muy importante cuando una base de datos gestiona el acceso concurrente a los datos. Debe aparece que la base de datos sólo gestione una operación a la vez. Para ello, los datos que se actualizan deben ser bloqueado de ser modificado (y/o visualizado) hasta que la operación haya finalizado.

Couchbase proporciona aislamiento de lectura comprometida por defecto en (de nuevo) el nivel de documento único.

Para conseguir un aislamiento más estricto, hay dos tipos de bloqueos que puedes hacer en Couchbase: bloqueo pesimista y bloqueo optimista.

Bloqueo optimista

El bloqueo "optimista" depende de un valor en Couchbase llamado CAS (compare-and-swap). Cada documento tiene un valor CAS, que es un valor opaco. Cada vez que el documento cambia, obtiene un nuevo valor CAS. Cuando intentas actualizar un documento, pasas un valor CAS como parte de la operación. Si los valores CAS coinciden, Couchbase permite la operación. Si no, la operación no está permitida, y Couchbase devuelve un error.

Como ejemplo, supongamos que hay dos procesos: A y B. A y B hacen una petición "get" a Couchbase para obtener un documento. Couchbase devuelve el documento, junto con un valor CAS. Entonces, A y B envían una petición "set" a Couchbase, pasando el valor CAS que recibieron previamente. El procesamiento correrá, ocurriendo en uno de ellos primero. Digamos que es A. Cuando es el turno de B, el valor CAS del documento ya ha cambiado, por lo que la operación falla.

He aquí un ejemplo en .NET. Supongamos que estamos trabajando en un juego para móviles, y queremos llevar un registro del Nivel de Espada de un jugador. Cuanto mayor sea el nivel, más daño hace. En este ejemplo, A está intentando subir al nivel 2 y B está intentando subir al nivel 3.

Al final de este proceso, B falla, y la espada del jugador se queda en el nivel 2. Si quieres que B tenga éxito, una solución es volver a buscar el documento, obtener el último valor CAS e intentarlo de nuevo.

Por supuesto, esa solución podría volver a fallar. Y otra vez. Pero por eso se llama "optimista". Asume que el documento no va a estar sometido a una fuerte contención, y finalmente tendrá éxito. No se requiere un bloqueo real por parte de los servidores: sólo una comprobación de valores.

Cierre pesimista

Puede utilizar el bloqueo pesimista para establecer realmente un bloqueo. Esto puede ser útil si desea bloquear un gráfico de documento para mutar varios documentos.

Hay una operación atómica disponible en Couchbase llamada "GetAndLock". Esta operación devuelve el documento y un valor CAS. En este punto, el documento se considera "bloqueado". No se pueden realizar más bloqueos sobre él por otros procesos, y sólo el valor CAS puede desbloquear el documento.

He aquí un ejemplo C# de un cierre pesimista en acción:

Además, cuando se utiliza GetAndLock, se debe establecer un período de tiempo de espera. Después del tiempo de espera, Couchbase libera el bloqueo automáticamente. Aquí hay un ejemplo del tiempo de espera en acción.

Al ejecutar este ejemplo, se produciría la siguiente salida:

Pessimistic lock time out

Utilizando estos bloqueos, puede obtener el aislamiento de un documento individual, para asegurarse de que los cambios se producen en el orden que usted espera.

D de Durabilidad

"Durabilidad" significa tradicionalmente que cuando una operación se completa con éxito, que el disco almacena los cambios realizados por la operación. En una base de datos distribuida, la durabilidad puede significar que la disco y/o la memoria en otros nodos almacenar los cambios. La replicación a otros nodos es el mecanismo preferido para la durabilidad en Couchbase, ya que la red es mucho más rápida que el disco. En última instancia, para un desarrollador, significa que aunque se produzca un fallo en el sistema, el cambio sigue teniendo lugar.

Couchbase tiene una arquitectura "memory-first". Esto significa que los resultados de las operaciones de escritura se reconocen cuando se reciben en memoria, y luego se ponen en una cola para ser asíncronamente escritos en disco o replicados a otro nodo poco después. Por lo tanto, si una operación se escribe en memoria y el sistema se apaga inmediatamente, esa operación no es duradera. Este es el compromiso por defecto que hace Couchbase: velocidad sobre durabilidad.

Sin embargo, Couchbase permite anular esa configuración predeterminada y especificar un nivel de durabilidad más estricto con Requisitos de durabilidad. Esto alejará el péndulo del rendimiento y lo acercará a las propiedades ACID. La ventaja de este diseño es que el desarrollador de la aplicación sabe y decide cuándo es importante pagar el coste adicional.

El comportamiento por defecto es que escribir el documento en memoria se considera un éxito. Couchbase aún persistirá y replicará de acuerdo a la configuración de tu cluster de Couchbase, pero la llamada al método procederá después de que la operación sea reconocida.

Puedo especificar el número de nodos a los que replicar que considero una operación duradera con éxito utilizando ReplicateTo:

Y también puedo especificar una combinación de persistencia a otros nodos y replicación a otros nodos que considere "suficientemente" duradera utilizando PersistTo también:

Cada una de estas llamadas a métodos se bloqueará hasta que el Requisitos de durabilidad y permitirá que su aplicación realice una gestión de errores adicional.

Tenga en cuenta que si los requisitos de durabilidad fallaEntonces Couchbase todavía puede guardar el documento y eventualmente distribuirlo a través del cluster. Todo lo que sabemos es que no tuvo éxito hasta donde el SDK sabe. Puedes elegir actuar sobre esta información para introducir más propiedades ACID en tu aplicación.

Notas finales

Esta entrada del blog hablaba de las varias primitivas disponibles en Couchbase para construir garantías tipo ACID en tu aplicación. Aunque estas primitivas no satisfacen la definición completa de ACID, son suficientes para lo que la gran mayoría de aplicaciones modernas basadas en microservicios necesitan. Para el pequeño porcentaje de casos de uso que necesitan garantías transaccionales adicionales, Couchbase continuará innovando aún más.

En el próximo post, veremos técnicas y código de ejemplo que podrías aprovechar para construir una transacción multi-documento en Couchbase.

Si tiene alguna pregunta, no deje de consultar el Foros de Couchbase. Puede encontrar el mismo código utilizado en esta entrada del blog en Github.

Un agradecimiento especial a todos los coautores de esta entrada de blog: Shivani Gupta, Ravid Mayuram, John Liang, Chin Hong, Matt Ingenthron, Michael Nitschinger (y seguro que me faltan algunos más).

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Autor

Publicado por Matthew Groves

A Matthew D. Groves le encanta programar. No importa si se trata de C#, jQuery o PHP: enviará pull requests para cualquier cosa. Lleva codificando profesionalmente desde que escribió una aplicación de punto de venta en QuickBASIC para la pizzería de sus padres, allá por los años noventa. Actualmente trabaja como Director de Marketing de Producto para Couchbase. Su tiempo libre lo pasa con su familia, viendo a los Reds y participando en la comunidad de desarrolladores. Es autor de AOP in .NET, Pro Microservices in .NET, autor de Pluralsight y MVP de Microsoft.

3 Comentarios

  1. Perry Krug, Arquitecto principal, Couchbase mayo 21, 2018 a 7:58 am

    Matt, ¡gran post! Creo que también sería útil discutir la "consistencia" dentro de Couchbase en términos de referencias a documentos y JOINs. Es decir, que puedes evitar los típicos problemas de desnormalización haciendo referencia a un documento desde dentro de otro. De esa manera, cuando haces una escritura en cualquiera de los documentos, sigue siendo ACID.

  2. Gracias, Perry. He hablado exactamente de eso, excepto en "atomicidad", ya que creo que es más aplicable.

    1. Gracias Matt, veo que la discusión inicial allí, pero creo que vale la pena destacar cómo varios documentos todavía se puede actualizar "coherente", mostrando el vínculo entre dos documentos y el uso de un N1QL JOIN cuando la lectura de nuevo. es decir, documento1 contiene la mayor parte de mi perfil de usuario y un puntero / referencia a document2 que contiene mi lista de pedidos. Cuando actualizo el documento2, sigue siendo coherente con todo el perfil de usuario cuando lo vuelvo a leer (mediante JOIN) aunque no haya tenido que actualizar atómicamente tanto el documento1 como el documento2. Es esta combinación de normalización y desnormalización lo que hace a Couchbase tan potente y fácil de conseguir atomicidad y consistencia. El documento1 está desnormalizado para contener múltiples tablas, pero el documento1+documento2 está normalizado para evitar la sobrecarga de datos y mejorar la concurrencia... pero la combinación sigue siendo atómica y consistente al actualizar cualquiera de los dos documentos (o cualquiera de la cadena). ¿Tiene esto más sentido?

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.