{"id":14249,"date":"2023-04-04T13:27:26","date_gmt":"2023-04-04T20:27:26","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=14249"},"modified":"2025-06-13T23:00:08","modified_gmt":"2025-06-14T06:00:08","slug":"updating-sensor-data-exploring-couchbases-multi-model-options","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/updating-sensor-data-exploring-couchbases-multi-model-options\/","title":{"rendered":"Actualizaci\u00f3n de datos de sensores: Explorando las opciones multimodelo de Couchbase"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Couchbase se ha convertido en una opci\u00f3n popular para <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/solutions\/iot-data-management\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Casos de uso de IoT<\/span><\/a><span style=\"font-weight: 400;\">gracias a su flexibilidad <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/how-multimodel-databases-can-reduce-data-sprawl\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">multimodelo<\/span><\/a><span style=\"font-weight: 400;\"> capacidades de gesti\u00f3n de datos.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Recientemente, estuve trabajando con un cliente de la industria de cruceros que ten\u00eda un reto \u00fanico - necesitaban Couchbase para recibir y almacenar actualizaciones frecuentes de muchos sensores que registran lecturas en su flota de barcos. Estas lecturas podr\u00edan llegar a Couchbase fuera de orden cronol\u00f3gico. \u00bfC\u00f3mo pod\u00edan asegurarse de que una nueva lectura de un sensor s\u00f3lo se almacenara si ten\u00eda una marca de tiempo posterior a la lectura anterior?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Cada sensor tiene una clave \u00fanica que corresponde a la \u00faltima lectura del sensor. Una lectura de las 10:43:00 AM no podr\u00eda sobrescribir una lectura de las 10:42:30 AM, aunque esta \u00faltima se hubiera recibido m\u00e1s tarde. A continuaci\u00f3n se muestran algunas lecturas de ejemplo y su orden de procesamiento (tenga en cuenta que las marcas de tiempo no est\u00e1n necesariamente en orden cronol\u00f3gico):<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">id: \"C-DI_Nautical_Speed\"\r\n{\r\n\u00a0 \u00a0 \"speed\": 15,\r\n\u00a0 \u00a0 \"unit\": \"knots\",\r\n\u00a0 \u00a0 \"timeStamp\": \"2023-03-10 10:43:00 AM\"\r\n}\r\n\r\nid: \"C-DI_Nautical_Speed\"\r\n{\r\n\u00a0 \u00a0 \"speed\": 15.1,\r\n\u00a0 \u00a0 \"unit\": \"knots\",\r\n\u00a0 \u00a0 \"timeStamp\": \"2023-03-10 11:43:00 AM\"\r\n},\r\n\r\nid: \"C-DI_Nautical_Speed\"\r\n{\r\n\u00a0 \u00a0 \"speed\": 14.9,\r\n\u00a0 \u00a0 \"unit\": \"knots\",\r\n\u00a0 \u00a0 \"timeStamp\": \"2023-03-10 10:42:30 AM\"\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">En esta entrada de blog, exploraremos c\u00f3mo las opciones multimodelo de Couchbase pueden ayudar a abordar este escenario y gestionar eficientemente las actualizaciones de datos de los sensores.<\/span><\/p>\n<h2>\u00bfQu\u00e9 es el multimodelo?<\/h2>\n<p><span style=\"font-weight: 400;\">Couchbase es quiz\u00e1s la base de datos multimodelo original, ya que combina el almacenamiento en cach\u00e9 en memoria con la persistencia de datos JSON para proporcionar un enfoque flexible a la gesti\u00f3n de datos. Couchbase puede manejar m\u00faltiples tipos de datos, como datos estructurados, semiestructurados y no estructurados, en la misma instancia de base de datos.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Con el tiempo, Couchbase ha a\u00f1adido <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/sqlplusplus\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">SQL<\/span><\/a><span style=\"font-weight: 400;\">, <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/products\/full-text-search\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">B\u00fasqueda de texto completo (FTS)<\/span><\/a><span style=\"font-weight: 400;\">, <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/products\/eventing\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Eventos<\/span><\/a><span style=\"font-weight: 400;\">, <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/products\/analytics\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Anal\u00edtica<\/span><\/a><span style=\"font-weight: 400;\"> m\u00faltiples modelos para acceder, indexar e interactuar con el mismo conjunto de datos. Este enfoque multimodelo puede hacer que Couchbase sea m\u00e1s flexible que las bases de datos tradicionales, pero tambi\u00e9n puede requerir un poco m\u00e1s de reflexi\u00f3n sobre las compensaciones en comparaci\u00f3n con los sistemas heredados (que s\u00f3lo tienen una forma de interactuar con los datos).<\/span><\/p>\n<h2>Opciones multimodelo para actualizar las lecturas de los sensores<\/h2>\n<p><span style=\"font-weight: 400;\">A la hora de actualizar las lecturas de los sensores para este caso de uso en la base de datos multimodelo de Couchbase, hay que tener en cuenta varios enfoques:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">API clave-valor con bloqueo optimista o pesimista<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">API clave-valor con transacci\u00f3n ACID<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Sentencia UPDATE de SQL<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Funci\u00f3n Eventing OnUpdate<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Todas estas opciones tienen sus propias ventajas y desventajas en t\u00e9rminos de rendimiento, complejidad y requisitos. La elecci\u00f3n del mejor enfoque depender\u00e1 de factores como el tama\u00f1o y la frecuencia de las actualizaciones, el nivel de concurrencia y los requisitos generales de rendimiento.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">En \u00faltima instancia, el mejor enfoque s\u00f3lo puede determinarse a trav\u00e9s de pruebas en el mundo real con datos en vivo o una buena aproximaci\u00f3n a los datos en vivo. Examinando las compensaciones y experimentando con las diferentes opciones, los desarrolladores pueden identificar el m\u00e9todo m\u00e1s eficaz para actualizar las lecturas de los sensores en la base de datos multimodelo de Couchbase.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Es importante se\u00f1alar que en muchos de estos escenarios, asumimos que el documento del sensor ya existe (que ser\u00e1 el escenario m\u00e1s com\u00fan en un estado estacionario). Cuando ese no sea el caso, podemos cambiar el <em>sustituir<\/em> o <em>actualizaci\u00f3n<\/em> operaci\u00f3n a <em>upsert<\/em>\u00a0para asegurarse de que el documento se crea si no existe. (Como alternativa, podr\u00eda \"sembrar\" la colecci\u00f3n con un documento para cada sensor).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dicho esto, examinemos cada posibilidad.<\/span><\/p>\n<h2>API clave-valor con bloqueo optimista o pesimista<\/h2>\n<p><span style=\"font-weight: 400;\">Una forma de actualizar las lecturas de los sensores en la base de datos multimodelo de Couchbase es mediante el bloqueo optimista o pesimista. Este mecanismo de bloqueo, presente en Couchbase desde hace mucho tiempo, utiliza una t\u00e9cnica llamada <\/span><a href=\"https:\/\/docs.couchbase.com\/dotnet-sdk\/current\/howtos\/concurrent-document-mutations.html\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">CAS (comparar e intercambiar)<\/span><\/a><span style=\"font-weight: 400;\"> para garantizar la actualizaci\u00f3n condicional de documentos individuales.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">El valor CAS es un n\u00famero arbitrario que cambia cada vez que se modifica un documento. Al hacer coincidir los valores CAS, los desarrolladores pueden actualizar condicionalmente los datos de los sensores con una sobrecarga m\u00ednima. En esta secci\u00f3n, exploraremos c\u00f3mo se puede utilizar el bloqueo optimista y pesimista para este caso de uso de datos de sensores.<\/span><\/p>\n<h3>Bloqueo optimista<\/h3>\n<p><span style=\"font-weight: 400;\">El bloqueo optimista es un m\u00e9todo sencillo para actualizar los datos de los sensores en Couchbase, con s\u00f3lo tres pasos:<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">En <\/span><b>primer paso<\/b><span style=\"font-weight: 400;\"> consiste en recuperar el documento por clave, que incluye el valor del documento y sus metadatos (incluido el valor CAS).<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">Una vez recuperado, el <\/span><b>segundo paso<\/b><span style=\"font-weight: 400;\"> es comprobar si la marca de tiempo es m\u00e1s antigua que la marca de tiempo entrante.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">Si es as\u00ed, el <\/span><b>tercer paso<\/b><span style=\"font-weight: 400;\"> consiste en sustituir el documento por el nuevo valor y enviar con \u00e9l el valor CAS.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Aqu\u00ed es donde entra la parte \"optimista\". Si los valores CAS coinciden, la operaci\u00f3n tiene \u00e9xito y los datos del sensor se actualizan. Sin embargo, si el valor CAS no coincide, significa que los datos del sensor han sido actualizados (por alg\u00fan otro hilo\/proceso) desde la \u00faltima operaci\u00f3n de lectura. En este caso, tiene la opci\u00f3n de <\/span><b>reintentar la operaci\u00f3n<\/b><span style=\"font-weight: 400;\"> desde el principio. Si no espera que el documento del sensor espec\u00edfico se actualice con frecuencia, entonces el bloqueo optimista es el camino a seguir (ya que los reintentos ser\u00edan poco frecuentes).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">He aqu\u00ed un ejemplo de bloqueo optimista con una l\u00f3gica de reintento sencilla:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">\/\/ get existing sensor reading\r\nvar currentDoc = await _collection.GetAsync(sensorId);\r\nvar currentDocCas = currentDoc.Cas;\r\nvar currentReading = currentDoc.ContentAs&lt;NauticalSpeed&gt;();\r\n\r\n\/\/ check timestamps\r\nif (newSensorReading.TimeStamp &gt; currentReading.TimeStamp)\r\n{\r\n\u00a0 \u00a0 \/\/ incoming reading is newer, update the record\r\n\u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is newer. Updating.\");\r\n\u00a0 \u00a0 var retries = 3;\r\n\u00a0 \u00a0 while (retries &gt; 0)\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 try\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 await _collection.ReplaceAsync(sensorId, newSensorReading, options =&gt; options.Cas(currentDocCas));\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 return;\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 \u00a0 \u00a0 catch (CasMismatchException)\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Console.WriteLine($\"CAS mismatch. Retries remaining: {retries}\");\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 retries--;\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 Console.WriteLine(\"Retry max exceeded. Sensor reading was not updated.\");\r\n}\r\nelse\r\n{\r\n\u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is not new. Ignoring.\");\r\n\u00a0 \u00a0 \/\/ incoming reading is not newer, so do nothing\r\n\u00a0 \u00a0 \/\/ (or possibly update a log, or whatever else you want to do)\r\n}<\/pre>\n<h3>Bloqueo pesimista<\/h3>\n<p><a href=\"https:\/\/docs.couchbase.com\/dotnet-sdk\/current\/howtos\/concurrent-document-mutations.html#pessimistic-locking\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Cierre pesimista<\/span><\/a><span style=\"font-weight: 400;\"> es otra forma de abordar el mismo problema. Al igual que el bloqueo optimista, consta de tres pasos, pero con algunas ligeras diferencias.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">En <\/span><b>primer paso<\/b><span style=\"font-weight: 400;\"> es <\/span><i><span style=\"font-weight: 400;\">obtener y bloquear<\/span><\/i><span style=\"font-weight: 400;\"> un documento por clave, tomando nota del valor CAS. A diferencia del bloqueo optimista, en el que el documento simplemente se lee, en el bloqueo pesimista el documento se bloquea expl\u00edcitamente. Esto significa que ning\u00fan otro proceso puede realizar cambios en el documento hasta que se desbloquee.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">En el <\/span><b>segundo paso<\/b><span style=\"font-weight: 400;\">Al igual que en el bloqueo optimista, se comprueba si la marca de tiempo es m\u00e1s antigua que la marca de tiempo entrante.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">Si lo es, entonces en el <\/span><b>tercer paso<\/b><span style=\"font-weight: 400;\">el documento se sustituye por el nuevo valor y se env\u00eda con el valor CAS.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">En el paso 1 del bloqueo pesimista, tambi\u00e9n hay que especificar una ventana de tiempo de espera. \u00bfPor qu\u00e9? Porque es posible que el paso 3 nunca llegue a producirse debido a un error o un bloqueo, y el documento tenga que desbloquearse finalmente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Si espera que el documento del sensor se actualice mucho, pesimista podr\u00eda ser el mejor enfoque. Pero debido al bloqueo, podr\u00eda haber una latencia reducida en otros procesos esperando a que el documento se desbloquee.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Para ilustrarlo, he aqu\u00ed un ejemplo de bloqueo pesimista en acci\u00f3n:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">\/\/ get current sensor data\r\nvar maxLockTime = TimeSpan.FromSeconds(30);\r\nvar currentDoc = await _collection.GetAndLockAsync(sensorId, maxLockTime);\r\nvar currentDocCas = currentDoc.Cas;\r\nvar currentReading = currentDoc.ContentAs&lt;NauticalSpeed&gt;();\r\n\r\n\/\/ check timestamps against new reading\r\nif (newSensorReading.TimeStamp &gt; currentReading.TimeStamp)\r\n{\r\n\u00a0 \u00a0 \/\/ incoming reading is newer, update the record\r\n\u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is newer. Updating.\");\r\n\u00a0 \u00a0 await _collection.ReplaceAsync(sensorId, newSensorReading, options =&gt; options.Cas(currentDocCas));\r\n\u00a0 \u00a0 return;\r\n}\r\nelse\r\n{\r\n\u00a0 \u00a0 await _collection.UnlockAsync(sensorId, currentDocCas);\r\n\u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is not new. Ignoring.\");\r\n\u00a0 \u00a0 \/\/ incoming reading is not newer, so do nothing\r\n\u00a0 \u00a0 \/\/ (or possibly update a log, or whatever else you want to do)\r\n}<\/pre>\n<h3>Ventajas y desventajas del bloqueo CAS<\/h3>\n<p><span style=\"font-weight: 400;\">Cuando se trata del bloqueo CAS, hay que tener en cuenta ciertas ventajas y desventajas. El bloqueo optimista funciona bien cuando los conflictos son poco frecuentes, pero tendr\u00e1s que implementar una l\u00f3gica de reintento adecuada para gestionar los posibles reintentos.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Para ayudar con esta compensaci\u00f3n, se podr\u00edan utilizar reintentos m\u00e1s avanzados o especializados. Por ejemplo, en este caso de uso, puede ser aceptable \"rendirse\" y descartar una lectura entrante del sensor si ha habido muchos reintentos y\/o la lectura es muy antigua.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">El bloqueo pesimista, por otro lado, es un enfoque \"m\u00e1s seguro\", pero requiere una comprensi\u00f3n clara de las implicaciones de rendimiento del bloqueo. El bloqueo puede aumentar la latencia en otros procesos que tengan que esperar a que se desbloquee el documento.<\/span><\/p>\n<h2>Transacci\u00f3n ACID<\/h2>\n<p><span style=\"font-weight: 400;\">Otra posible soluci\u00f3n al problema de la actualizaci\u00f3n del sensor es utilizar una transacci\u00f3n ACID. Este enfoque puede ser excesivo para la actualizaci\u00f3n de un solo documento en este caso de uso, pero podr\u00eda ser \u00fatil en diferentes casos de uso donde m\u00faltiples documentos necesitan ser actualizados at\u00f3micamente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Uno de los retos de los datos de los sensores es que pueden llegar a gran velocidad. En el tiempo que transcurre entre la comprobaci\u00f3n de los datos actuales y la actualizaci\u00f3n con los datos entrantes del sensor, podr\u00eda estar llegando otra lectura. Para evitar este problema, se puede utilizar una transacci\u00f3n ACID para actualizar los datos condicionalmente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">El ejemplo de c\u00f3digo abajo demuestra como usar una transacci\u00f3n ACID para actualizar un documento de sensor. La transacci\u00f3n asegura que s\u00f3lo una operaci\u00f3n de actualizaci\u00f3n puede ocurrir a la vez por sensor, previniendo que m\u00faltiples lecturas de sensor entrantes interfieran unas con otras.<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">var transaction = Transactions.Create(_cluster, TransactionConfigBuilder.Create()\r\n\u00a0 \u00a0 .DurabilityLevel(DurabilityLevel.None)); \/\/ set to 'none' because I'm using a single-node dev cluster\r\n\u00a0 \u00a0 \/\/ for more details see: https:\/\/docs.couchbase.com\/dotnet-sdk\/current\/howtos\/distributed-acid-transactions-from-the-sdk.html\r\n\r\nawait transaction.RunAsync(async (context) =&gt;\r\n{\r\n\u00a0 \u00a0 \/\/ get existing sensor reading\r\n\u00a0 \u00a0 var currentDoc = await context.GetAsync(_collection, sensorId);\r\n\u00a0 \u00a0 var currentReading = currentDoc.ContentAs&lt;NauticalSpeed&gt;();\r\n\r\n\u00a0 \u00a0 \/\/ check timestamps\r\n\u00a0 \u00a0 if (newSensorReading.TimeStamp &gt; currentReading.TimeStamp)\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ incoming reading is newer, update the record\r\n\u00a0 \u00a0 \u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is newer. Updating.\");\r\n\u00a0 \u00a0 \u00a0 \u00a0 await context.ReplaceAsync(currentDoc, newSensorReading);\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 else\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 Console.WriteLine(\"Incoming sensor reading is not new. Ignoring.\");\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ incoming reading is not newer, so do nothing\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ (or possibly update a log, or whatever else you want to do)\r\n\u00a0 \u00a0 }\r\n});<\/pre>\n<h2>Ventajas y desventajas de las transacciones ACID<\/h2>\n<p><span style=\"font-weight: 400;\">La API key-value deber\u00eda usarse siempre que sea posible para maximizar el rendimiento. Sin embargo, el uso de una transacci\u00f3n ACID distribuida en Couchbase conllevar\u00e1 cierta sobrecarga debido a las operaciones clave-valor adicionales ejecutadas (entre bastidores) para coordinar la transacci\u00f3n. Dado que los datos en Couchbase se distribuyen autom\u00e1ticamente, es probable que las operaciones se coordinen a trav\u00e9s de una red a m\u00faltiples servidores.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Un beneficio de usar una transacci\u00f3n ACID sobre una operaci\u00f3n CAS es que las librer\u00edas de transacciones de Couchbase ya tienen incorporada una sofisticada l\u00f3gica de reintento. Esta puede ser una forma de evitar escribir tu propia l\u00f3gica de reintento. Adicionalmente, una transacci\u00f3n ACID es recomendada (probablemente requerida, de hecho) si un caso de uso involucra actualizar m\u00faltiples documentos de sensores.<\/span><\/p>\n<h2>Operaci\u00f3n de actualizaci\u00f3n SQL<\/h2>\n<p><span style=\"font-weight: 400;\">Otro m\u00e9todo para realizar actualizaciones condicionales es utilizar una consulta UPDATE de SQL++.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">He aqu\u00ed un ejemplo de aplicaci\u00f3n:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">var retries = 3;\r\nwhile (retries &gt; 0)\r\n{\r\n\u00a0 \u00a0 try\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 await _cluster.QueryAsync&lt;dynamic&gt;(@\"UPDATE sensordata s\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 USE KEYS $sensorId\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 SET s.speed = $newSpeed, s.unit = $newUnit, s.timeStamp = $newTimeStamp\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 WHERE DATE_DIFF_STR($newTimeStamp, s.timeStamp, 'millisecond') &gt; 0\", options =&gt;\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 options.Parameter(\"sensorId\", sensorId);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 options.Parameter(\"newSpeed\", sensorReading.Speed);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 options.Parameter(\"newUnit\", sensorReading.Unit);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 options.Parameter(\"newTimeStamp\", sensorReading.TimeStamp);\r\n\u00a0 \u00a0 \u00a0 \u00a0 });\r\n\u00a0 \u00a0 \u00a0 \u00a0 return;\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 c atch (CasMismatchException)\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 Console.WriteLine($\"UPDATE CAS mismatch, tries remaining: {retries}\");\r\n\u00a0 \u00a0 \u00a0 \u00a0 retries--;\r\n\u00a0 \u00a0 }\r\n}\r\nConsole.WriteLine(\"Max retries exceeded, sensor not updated\");<\/pre>\n<p><i><span style=\"font-weight: 400;\">(Por cierto, el uso de una marca de tiempo de \u00e9poca probablemente proporcionar\u00e1 un mejor rendimiento).<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400;\">Como habr\u00e1s adivinado por el c\u00f3digo, la consulta SQL++ utiliza CAS entre bastidores, al igual que se hace con el ejemplo anterior de la API KV.<\/span><\/p>\n<h2>Ventajas y desventajas de SQL<\/h2>\n<p><span style=\"font-weight: 400;\">El enfoque de SQL++ para las actualizaciones condicionales tiene algunas desventajas. Aunque la <\/span><em><span style=\"font-weight: 400;\">UTILIZAR TECLAS<\/span><\/em><span style=\"font-weight: 400;\"> ayuda a eliminar la necesidad de un \u00edndice, la consulta a\u00fan debe ser analizada por el servicio de consulta, que <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/learn\/services-and-indexes\/services\/query-service.html#query-execution\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">implica muchos pasos<\/span><\/a><span style=\"font-weight: 400;\">. Esto puede suponer una presi\u00f3n a\u00f1adida para el sistema si otros componentes ya est\u00e1n utilizando el servicio de consulta.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">En general, dado que el enfoque SQL++ es muy similar a la API KV con la sobrecarga a\u00f1adida de analizar la consulta, puede que no sea la mejor opci\u00f3n a menos que tenga una necesidad espec\u00edfica de l\u00f3gica compleja expresada en SQL++ o si utilizar la API KV no es una opci\u00f3n.<\/span><\/p>\n<h2>Eventos<\/h2>\n<p><span style=\"font-weight: 400;\">El \u00faltimo enfoque que quiero tratar es el uso del Eventing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Eventing en Couchbase consiste en escribir funciones JavaScript que respondan a eventos de cambio de datos de forma as\u00edncrona y desplegarlas en el cluster de Couchbase.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Para este caso de uso en particular, creo que el uso de una colecci\u00f3n de \"puesta en escena\" como una ubicaci\u00f3n para las lecturas del sensor inicialmente es el camino a seguir. Esta es la secuencia:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Las lecturas entrantes de los sensores se escriben en una colecci\u00f3n de \"puesta en escena\".<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Un evento <em>OnUpdate<\/em> responde a las nuevas lecturas de los sensores.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">En <em>OnUpdate<\/em> comprueba las marcas de tiempo con el documento correspondiente de la colecci\u00f3n \"actual<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Si la marca de tiempo es m\u00e1s actual, se actualiza el documento de la colecci\u00f3n \"actual\".<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><em><span style=\"font-weight: 400;\">OnUpdate<\/span><\/em><span style=\"font-weight: 400;\"> se ejecutar\u00e1 cuando se cree un documento <\/span><i><span style=\"font-weight: 400;\">o<\/span><\/i><span style=\"font-weight: 400;\"> actualizado, por lo que est\u00e1 bien dejar el documento antiguo en la puesta en escena (esto simplifica el c\u00f3digo de eventos). Adem\u00e1s, se puede establecer un TTL en la colecci\u00f3n, de modo que si la lectura de un sensor no se actualiza en un tiempo, se limpiar\u00e1 autom\u00e1ticamente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">He aqu\u00ed un ejemplo de una funci\u00f3n eventing que funciona con este dise\u00f1o:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function OnUpdate(doc, meta) {\r\n\u00a0 \u00a0 \/\/ Only process documents with a \"timestamp\"\r\n\u00a0 \u00a0 if (doc.timestamp) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Extract timestamp and sensor ID from the staged document\r\n\u00a0 \u00a0 \u00a0 \u00a0 var stagedTimestamp = doc.timestamp;\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ note that this will loop indefinitely\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ but you can also limit it to a certain number of reties if you wish\r\n\u00a0 \u00a0 \u00a0 \u00a0 while(true) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ Get the current document for the same sensor ID from the \"destination\" collection\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var currentDoc = dst_col[meta.id];\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ If there is no current document, or the staged timestamp is later than the current timestamp, update the current document\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (!currentDoc || stagedTimestamp &gt; currentDoc.timestamp) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ dst_col is a READ+WRITE ALIAS\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 dst_col[meta.id] = doc;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ the whole document is overwritten, but you can also choose to override certain fields if you wish\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ src_col is a READ ALIAS\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var result = couchbase.get(src_col, meta);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (result.success) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (result.meta.cas == meta.cas) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ the document was unchanged in the stage collection we are done\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 doc = result.doc;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 stagedTimestamp = doc.timestamp;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } else {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (result.error.key_not_found) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ this might be okay, assuming 'staging' collection gets cleaned up or has a TTL\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ again, this will depend on what kind of retry logic you have\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } else {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 log('failure could not read stage adv. get: id',meta.id,'result',result);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 }\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Y aqu\u00ed est\u00e1 la configuraci\u00f3n para esa funci\u00f3n de eventos:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-14250\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/image_2023-04-04_132102606-674x1024.png\" alt=\"Eventing configuration window in Couchbase\" width=\"674\" height=\"1024\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/image_2023-04-04_132102606-674x1024.png 674w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/image_2023-04-04_132102606-198x300.png 198w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/image_2023-04-04_132102606-768x1167.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/image_2023-04-04_132102606-300x456.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/image_2023-04-04_132102606.png 950w\" sizes=\"auto, (max-width: 674px) 100vw, 674px\" \/><\/p>\n<h2>Compromisos en las pruebas<\/h2>\n<p><span style=\"font-weight: 400;\">De nuevo, observe que en este c\u00f3digo se utiliza un bloqueo CAS optimista. De hecho, casi se podr\u00eda decir que se trata de una versi\u00f3n JavaScript del c\u00f3digo que utiliza la API KV anterior.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Una diferencia clave es que esta funci\u00f3n se ejecuta en el propio cl\u00faster de Couchbase. Y este es el beneficio clave de eventing: no importa de d\u00f3nde vengan los datos del sensor, la funci\u00f3n Eventing de Couchbase se asegurar\u00e1 de que sean procesados. Es mantener la l\u00f3gica cerca de los datos. Si tienes dos o m\u00e1s clientes que usan la API KV en su lugar, eso significa que necesitas dos o m\u00e1s implementaciones del mismo c\u00f3digo. Esto puede conducir a problemas cuando la l\u00f3gica cambia, ya que tendr\u00e1 que ser actualizado en varios lugares.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Sin embargo, al igual que con SQL++, Eventing tiene algunos gastos generales que est\u00e1n involucrados. En este caso, m\u00faltiples colecciones, y el propio servicio de eventing. T\u00edpicamente esto podr\u00eda implicar un nodo adicional de Couchbase en producci\u00f3n. Adem\u00e1s, Eventing no est\u00e1 disponible actualmente en Couchbase Server Community.<\/span><\/p>\n<h2>Resumen<\/h2>\n<p><span style=\"font-weight: 400;\">Couchbase es una base de datos multimodelo que ofrece opciones y compensaciones para tu caso de uso. En este post, se cubri\u00f3 el caso de uso de actualizaciones de datos de sensores con 4 patrones de acceso a datos diferentes, cada uno con sus pros y sus contras:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">API de KV: eficaz y sencilla, pero puede requerir cierta l\u00f3gica de reintento.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Transacciones ACID: fiables, pero con sobrecarga<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">SQL++ - familiar, declarativo, pero tiene sobrecarga de an\u00e1lisis y ejecuci\u00f3n de consultas<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Eventing: cerca de los datos, consolida la l\u00f3gica, pero tiene la sobrecarga del servicio de eventing y las colecciones adicionales.<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Todos los ejemplos de c\u00f3digo son <\/span><a href=\"https:\/\/github.com\/couchbaselabs\/blog-source-code\/tree\/master\/Groves\/143SensorUpdateMultiModel\/src\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">disponible en GitHub<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00bfHas pensado en un enfoque diferente? Deja un comentario a continuaci\u00f3n, o comp\u00e1rtelo en el <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/developers\/community\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Couchbase Discord<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p>&nbsp;<\/p>","protected":false},"excerpt":{"rendered":"<p>Couchbase has become a popular choice for IoT use cases, thanks to its flexible multi-model data management capabilities. Recently, I was working with a customer in the cruise industry that had a unique challenge &#8211; they needed Couchbase to receive [&hellip;]<\/p>","protected":false},"author":71,"featured_media":14251,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1815,1816,2273,1812,2201],"tags":[9499,9803,1337,9247,9600],"ppma_author":[8937],"class_list":["post-14249","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-best-practices-and-tutorials","category-couchbase-server","category-eventing","category-n1ql-query","category-tools-sdks","tag-acid-transactions","tag-cas-locking","tag-iot","tag-key-value-store","tag-multimodel"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Updating Sensor Data: Exploring Couchbase&#039;s Multi-Model Options<\/title>\n<meta name=\"description\" content=\"Choosing the best multi-model approach for updating sensor readings depends on the size and frequency of the updates, the level of concurrency, and more.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/es\/updating-sensor-data-exploring-couchbases-multi-model-options\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Updating Sensor Data: Exploring Couchbase&#039;s Multi-Model Options\" \/>\n<meta property=\"og:description\" content=\"Choosing the best multi-model approach for updating sensor readings depends on the size and frequency of the updates, the level of concurrency, and more.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/updating-sensor-data-exploring-couchbases-multi-model-options\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-04T20:27:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T06:00:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Matthew Groves\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@mgroves\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Matthew Groves\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/\"},\"author\":{\"name\":\"Matthew Groves\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58\"},\"headline\":\"Updating Sensor Data: Exploring Couchbase&#8217;s Multi-Model Options\",\"datePublished\":\"2023-04-04T20:27:26+00:00\",\"dateModified\":\"2025-06-14T06:00:08+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/\"},\"wordCount\":1983,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png\",\"keywords\":[\"ACID transactions\",\"CAS locking\",\"IoT\",\"key-value store\",\"multimodel\"],\"articleSection\":[\"Application Design\",\"Best Practices and Tutorials\",\"Couchbase Server\",\"Eventing\",\"SQL++ \/ N1QL Query\",\"Tools &amp; SDKs\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/\",\"name\":\"Updating Sensor Data: Exploring Couchbase's Multi-Model Options\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png\",\"datePublished\":\"2023-04-04T20:27:26+00:00\",\"dateModified\":\"2025-06-14T06:00:08+00:00\",\"description\":\"Choosing the best multi-model approach for updating sensor readings depends on the size and frequency of the updates, the level of concurrency, and more.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png\",\"width\":1200,\"height\":628},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Updating Sensor Data: Exploring Couchbase&#8217;s Multi-Model Options\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58\",\"name\":\"Matthew Groves\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ba51e6aacc53995c323a634e4502ef54\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g\",\"caption\":\"Matthew Groves\"},\"description\":\"Matthew D. Groves is a guy who loves to code. It doesn't matter if it's C#, jQuery, or PHP: he'll submit pull requests for anything. He has been coding professionally ever since he wrote a QuickBASIC point-of-sale app for his parent's pizza shop back in the 90s. He currently works as a Senior Product Marketing Manager for Couchbase. His free time is spent with his family, watching the Reds, and getting involved in the developer community. He is the author of AOP in .NET, Pro Microservices in .NET, a Pluralsight author, and a Microsoft MVP.\",\"sameAs\":[\"https:\/\/crosscuttingconcerns.com\",\"https:\/\/x.com\/mgroves\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/es\/author\/matthew-groves\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Updating Sensor Data: Exploring Couchbase's Multi-Model Options","description":"Elegir el mejor enfoque multimodelo para actualizar las lecturas de los sensores depende del tama\u00f1o y la frecuencia de las actualizaciones, el nivel de concurrencia, etc.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/es\/updating-sensor-data-exploring-couchbases-multi-model-options\/","og_locale":"es_MX","og_type":"article","og_title":"Updating Sensor Data: Exploring Couchbase's Multi-Model Options","og_description":"Choosing the best multi-model approach for updating sensor readings depends on the size and frequency of the updates, the level of concurrency, and more.","og_url":"https:\/\/www.couchbase.com\/blog\/es\/updating-sensor-data-exploring-couchbases-multi-model-options\/","og_site_name":"The Couchbase Blog","article_published_time":"2023-04-04T20:27:26+00:00","article_modified_time":"2025-06-14T06:00:08+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png","type":"image\/png"}],"author":"Matthew Groves","twitter_card":"summary_large_image","twitter_creator":"@mgroves","twitter_misc":{"Written by":"Matthew Groves","Est. reading time":"12 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/"},"author":{"name":"Matthew Groves","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58"},"headline":"Updating Sensor Data: Exploring Couchbase&#8217;s Multi-Model Options","datePublished":"2023-04-04T20:27:26+00:00","dateModified":"2025-06-14T06:00:08+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/"},"wordCount":1983,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png","keywords":["ACID transactions","CAS locking","IoT","key-value store","multimodel"],"articleSection":["Application Design","Best Practices and Tutorials","Couchbase Server","Eventing","SQL++ \/ N1QL Query","Tools &amp; SDKs"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/","url":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/","name":"Updating Sensor Data: Exploring Couchbase's Multi-Model Options","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png","datePublished":"2023-04-04T20:27:26+00:00","dateModified":"2025-06-14T06:00:08+00:00","description":"Elegir el mejor enfoque multimodelo para actualizar las lecturas de los sensores depende del tama\u00f1o y la frecuencia de las actualizaciones, el nivel de concurrencia, etc.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/04\/couchbase-multi-model-sensor-data.png","width":1200,"height":628},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/updating-sensor-data-exploring-couchbases-multi-model-options\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Updating Sensor Data: Exploring Couchbase&#8217;s Multi-Model Options"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58","name":"Matthew Groves","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ba51e6aacc53995c323a634e4502ef54","url":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","caption":"Matthew Groves"},"description":"A Matthew D. Groves le encanta programar. No importa si se trata de C#, jQuery o PHP: enviar\u00e1 pull requests para cualquier cosa. Lleva codificando profesionalmente desde que escribi\u00f3 una aplicaci\u00f3n de punto de venta en QuickBASIC para la pizzer\u00eda de sus padres, all\u00e1 por los a\u00f1os 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.","sameAs":["https:\/\/crosscuttingconcerns.com","https:\/\/x.com\/mgroves"],"url":"https:\/\/www.couchbase.com\/blog\/es\/author\/matthew-groves\/"}]}},"authors":[{"term_id":8937,"user_id":71,"is_guest":0,"slug":"matthew-groves","display_name":"Matthew Groves","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","author_category":"","last_name":"Groves","first_name":"Matthew","job_title":"","user_url":"https:\/\/crosscuttingconcerns.com","description":"A Matthew D. Groves le encanta programar.  No importa si se trata de C#, jQuery o PHP: enviar\u00e1 pull requests para cualquier cosa.  Lleva codificando profesionalmente desde que escribi\u00f3 una aplicaci\u00f3n de punto de venta en QuickBASIC para la pizzer\u00eda de sus padres, all\u00e1 por los a\u00f1os 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."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/14249","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/71"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=14249"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/14249\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/14251"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=14249"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=14249"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=14249"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=14249"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}