Gestión de documentos de diseño
El almacén de datos JSON escalable de IBM Cloudant tiene varios mecanismos de consultas, que generan índices que se crean y se mantienen por separado de los datos principales.
Artículo de Glynn Bird, especialista en desarrollo de IBM Cloudant, glynn@cloudant.com.
La indexación no se realiza de inmediato cuando se guarda un documento. La indexación se planifica para que se genere más tarde, lo que ofrece un mejor rendimiento de escritura sin bloqueos.
- Las vistas MapReduce son índices del conjunto de datos con pares de valores claves que se guardan en un BTree para que se puedan recuperar de forma eficiente mediante una clave o rango de claves.
- Los índices de búsqueda se crean con Apache Lucene para permitir la búsqueda de texto libre, el uso de facetas y consultas ad hoc complejas.
Los índices de búsqueda y vistas MapReduce de IBM® Cloudant® for IBM Cloud® se configuran añadiendo los documentos de diseño a una base de datos. Los documentos de diseño son documentos JSON que incluyen las instrucciones sobre cómo se debe crear la vista o el índice. Vamos a examinar un ejemplo sencillo. Supongamos que tiene una colección simple de documentos de datos, parecida a la del siguiente ejemplo.
Consulte un ejemplo de un documento de datos simple:
{
"_id": "23966717-5A6F-E581-AF79-BB55D6BBB613",
"_rev": "1-96daf2e7c7c0c277d0a63c49b57919bc",
"doc_name": "Markdown Reference",
"body": "Lorem Ipsum",
"ts": 1422358827
}
Cada documento de datos incluye un nombre, un cuerpo y una indicación de fecha y hora. Va a crear una vista MapReduce para clasificar sus documentos por indicación de fecha y hora.
Puede ordenar sus documentos según la indicación de fecha y hora creando una función de correlación.
Consulte una función de correlación de ejemplo que devuelva el campo de indicación de fecha y hora del documento, si está presente:
function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}
La función emite la indicación de fecha y hora del documento para que pueda utilizarla como clave para el índice. Ya que no estamos interesados en el valor del índice, Se emite null
. Como resultado proporcionamos un índice ordenado
por hora en el conjunto de documentos.
Vamos a llamar a esta vista by_ts
y ponerlo en un documento de diseño que se llama fetch
.
Consulte un documento de diseño de ejemplo que define una vista mediante una función de correlación:
{
"_id": "_design/fetch",
"views": {
"by_ts": {
"map": "function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}"
}
},
"language": "javascript"
}
El resultado es que el código de correlación se ha convertido en una serie compatible con JSON y se ha incluido en un documento de diseño.
Una vez guardado el documento de diseño, IBM Cloudant desencadena procesos del lado del servidor para crear la vista fetch/by_ts
. Se crea esta vista mediante la iteración por cada documento de la base de datos y enviándolos a la función
de correlación de JavaScript. La función devuelve el par key-value
emitido. A medida que la iteración continúa, cada par key-value
se almacena en un índice B-Tree. Una vez creado el índice por primera vez, las siguientes
reindexaciones se realizan solo sobre los documentos nuevos y actualizados. Los documentos suprimidos se eliminan de la indexación. Este proceso de ahorro de tiempo se conoce como MapReduce incremental, tal como se muestra en el siguiente diagrama:
Merece la pena recordar los puntos siguientes:
- La construcción de un índice se produce de forma asíncrona. IBM Cloudant confirma que el documento de diseño se ha guardado. Para comprobar el progreso de creación del índice, debe sondear el punto final
_active_tasks
de IBM Cloudant. - Cuantos más datos tenga, más tardará el índice en estar listo.
- Mientras la compilación del índice inicial está en curso, las consultas que se realizan sobre el índice se bloquean.
- La consulta de una vista activa la 'correlación' de los documentos que no se han indexado de forma incremental. De este modo se garantiza que va a obtener una vista actualizada de los datos. Consulte el apartado sobre el parámetro
stale
siguiente para ver las excepciones a esta regla.
Varias vistas en el mismo documento de diseño
Si define varias vistas en el mismo documento de diseño, se crean de forma eficiente al mismo tiempo. Cada documento solo se lee una vez y se pasa por la función Map de cada vista. Si utiliza este enfoque, tenga en cuenta que la modificación de un documento de diseño invalida todas las vistas de MapReduce existentes que están definidas en el documento. Este proceso invalida las vistas MapReduce incluso si algunas de las vistas permanecen sin modificar.
Si las vistas MapReduce se tienen que modificar independientemente unas de otras, coloque sus definiciones en distintos documentos de diseño.
Este comportamiento no se aplica a los índices de búsqueda de Lucene. Se pueden modificar dentro del mismo documento de diseño sin invalidar otros índices no modificados del mismo documento.
Gestión de cambios en un documento de diseño
Suponga que en el futuro decide cambiar el diseño de la vista. Ahora, en lugar de devolver el resultado de indicación de fecha y hora real, solo nos interesa el recuento del número de documentos que se ajustan a los criterios. Para conseguir
esta cuenta, la función de correlación no se modifica, pero ahora utiliza una función reduce
de _count
.
Consulte un documento de diseño de ejemplo que utiliza una función de reducción:
{
"_id": "_design/fetch",
"_rev": "2-a2324c9e74a76d2a16179c56f5315dba",
"views": {
"by_ts": {
"map": "function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}
}",
"reduce": "_count"
},
"language": "javascript"
}
Cuando se guarda este documento de diseño, IBM Cloudant invalida completamente el índice antiguo y empieza a crear el nuevo índice desde cero, iterando cada documento a su vez. Al igual que sucede con la construcción original, el tiempo que tarda depende del número de documentos de la base de datos. la vista hasta que termina. La compilación también bloquea las consultas entrantes en esa vista hasta que finaliza.
Pero hay un problema...
Si hay una aplicación que está accediendo a esta vista en tiempo real, es posible que experimente un dilema de despliegue:
- Es posible que la versión 1 del código, que se basa en el documento de diseño original, deje de funcionar porque la vista antigua se invalida.
- La versión 2 del código utiliza el nuevo documento de diseño. Esta versión no se puede publicar inmediatamente porque todavía no se ha terminado de compilar la vista nueva. Recuerde que el proceso de compilación tarda más si la base de datos incluye muchos documentos.
- Un problema más sutil que afecta al código es que las versiones 1 y 2 esperan datos de resultados diferentes de la vista: La versión 1 espera una lista de documentos coincidentes, mientras que la versión 2 espera un recuento 'reducido' de resultados.
Coordinación de cambios en documentos de diseño
Puede hacer frente a este problema de control de cambios de dos maneras.
Documentos de diseño con versiones
Una solución consiste en utilizar nombres de documentos de diseño con versiones:
- El código se ha escrito inicialmente para que utilice una vista llamada
_design/fetchv1
. - Cuando publica una versión nueva, crea una vista nueva denominada
_design/fetchv2
y consultamos la vista para asegurarnos de que realiza la compilación. - IBM Cloudant sondea
_active_tasks
hasta que finaliza la tarea de crear el nuevo índice. - Ahora está listo para utilizar el código que depende de la segunda vista.
- Suprimimos
_design/fetchv1
cuando estamos seguros de que ya no se necesita.
La utilización de documentos de diseño versionados es una forma sencilla de gestionar el control de cambios en los documentos de diseño, siempre que no olvide eliminar las versiones antiguas de una fecha anterior.
Documentos de diseño de Move and switch
Otro enfoque se basa en el hecho de que IBM Cloudant reconoce cuándo tiene dos documentos de diseño idénticos, y no pierde tiempo ni recursos en volver a crear las vistas que ya tiene. Es decir, si toma su documento de diseño _design/fetch
y creamos un duplicado exacto _design/fetch_OLD
, ambos puntos finales funcionarán de forma intercambiable sin tener que activar la reindexación.
Para conmutar la vista nueva, siga estos pasos:
- Cree una copia duplicada del documento de diseño que desea cambiar, por ejemplo añadiendo
_OLD
a su nombre:_design/fetch_OLD
. - Coloque el documento de diseño "de entrada" en la base de datos utilizando un nombre con el sufijo
_NEW
:_design/fetch_NEW
. - Consulte la vista
fetch_NEW
para asegurarse de que ha empezado la compilación. - Sondee el punto final
_active_tasks
y espere hasta que el índice se haya terminado de crear. - Coloque una copia duplicada del nuevo documento de diseño en
_design/fetch
. - Suprima el documento de diseño
_design/fetch_NEW
. - Suprima el documento de diseño
_design/fetch_OLD
.
Herramientas Move and switch
El script de la línea de mandatos Node.js couchmigrate
automatiza el procedimiento de Move and switch
. Se puede instalar con el siguiente mandato:
npm install -g couchmigrate
Para utilizar el couchmigrate
script primero define la URL de la instancia CouchDB/{{sitedata.keyword.cloudant_short_notm estableciendo una variable de entorno llamada COUCH_URL
. Ejecute el mandato siguiente para definir
el URL para la instancia de {{site.data.keyword.cloudant_short_notm}}:
export COUCH_URL=https://127.0.0.1:5984
El URL debe empezar por https://
y puede incluir credenciales de autenticación. Ejecute el mandato siguiente para definir el URL de la instancia de IBM Cloudant con las credenciales de autenticación:
export COUCH_URL="https://$ACCOUNT:$PASSWORD@$HOST.cloudant.com"
Si supone que tiene un documento de diseño en formato JSON, que se ha almacenado en un archivo, puede ejecutar el mandato migrate.
En este ejemplo,
db
specifies the name of the database to change, and dd
specifies the path to the design document file. Ejecute el mandato couchmigrate
:
couchmigrate --db mydb --dd /path/to/my/dd.json
El script coordina el procedimiento Move and switch
, y espera hasta que la vista se compila antes de que se devuelva. Si el documento de diseño entrante es el mismo que el que le corresponde, el script devuelve información de inmediato.
El código fuente del script está disponible aquí:
couchmigrate
.
El parámetro 'stale
'
Si un índice está completo pero se añaden nuevos registros a la base de datos, se planifica la actualización del índice como proceso de fondo. El estado de la base de datos aparece en el diagrama siguiente:
Cuando se consulta la vista, existen las opciones siguientes.
- El comportamiento predeterminado consiste en garantizar que el índice está actualizado con los últimos documentos de la base de datos antes de devolver la respuesta. Cuando consulta la vista, IBM Cloudant primero indexa los 250 documentos nuevos y, a continuación, devuelve la respuesta.
- Una alternativa consiste en añadir el parámetro
stale=ok
a la llamada a la API. Este parámetro significa,return me the data that is already indexed. I don't care about the latest updates.
En otras palabras, cuando se consulta la vista constale=ok
, IBM Cloudant devuelve la respuesta inmediatamente, sin ninguna reindexación adicional. - Una segunda alternativa consiste en añadir el parámetro
stale=update_after
a la llamada a la API. Este parámetro significa,return me the data that is already indexed, and then reindex any new documents.
En otras palabras, cuando se consulta la vista constale=update_after
, IBM Cloudant devuelve la respuesta inmediatamente y, a continuación, planifica una tarea en segundo plano para indexar los nuevos datos.
La adición de stale=ok
o stale=update_after
puede ser una buena manera de obtener respuestas de una vista con mayor rapidez, pero a costa de que los datos no estén tan actualizados.
El comportamiento predeterminado distribuye la carga equitativamente entre los nodos del clúster de IBM Cloudant. Si utiliza las opciones alternativas stale=ok
o stale=update_after
, se puede favorecer un subconjunto
de nodos del clúster para devolver resultados coherentes de entre el conjunto finalmente coherente. El parámetro stale
no es una solución perfecta para todos los casos prácticos. Sin embargo, puede proporcionar respuestas rápidas
en conjuntos de datos que se modifican constantemente si la aplicación acepta resultados no tan actualizados. Si la velocidad del cambio de datos es pequeña, la adición stale=ok
o stale=update_after
no supondrá una
ventaja en cuanto a rendimiento y puede distribuir la carga de forma poco equitativa en clústeres de gran tamaño.
Evite utilizar stale=ok
o stale=update_after
siempre que sea posible porque el comportamiento predeterminado proporciona los datos más reciente y distribuye los datos en el clúster. También puede advertir a la app cliente
de que hay una tarea de proceso de gran cantidad de datos en curso (por ejemplo, durante una actualización masiva de datos regular) conmutando temporalmente a stale=ok
durante ese tiempo. La app puede revertir el comportamiento
predeterminado posteriormente.
La opción stale
sigue estando disponible, pero también están disponibles las opciones stable
y update
, que son más útiles y se deben utilizar en su lugar. Para obtener más información, consulte Acceso a una vista obsoleta.