Compartir vía


Modelado de datos en Azure Cosmos DB for NoSQL

Aunque las bases de datos sin esquema, como Azure Cosmos DB, facilitan el almacenamiento y la consulta de datos no estructurados y semiestructurados, piense en su modelo de datos para optimizar el rendimiento, la escalabilidad y el coste.

¿Cómo se almacenan los datos? ¿Cómo recupera y consulta los datos su aplicación? ¿La aplicación realiza muchas operaciones de lectura o escritura?

Después de leer este artículo, puede responder a las siguientes preguntas:

  • ¿Qué es el modelado de datos y por qué tendría que importarme?
  • ¿Por qué es el modelado de datos en Azure Cosmos DB distinto a una base de datos relacional?
  • ¿Cómo expresa las relaciones de datos en una base de datos no relacional?
  • ¿Cuándo se incrustan datos y cuándo realizo vinculaciones a los datos?

Números en el lenguaje JSON

Azure Cosmos DB guarda documentos en JSON, por lo que es importante determinar si se deben convertir números en cadenas antes de almacenarlos en JSON. Convierta todos los números en un String si podrían superar los límites de números de doble precisión, tal y como se define en la norma Institute of Electrical and Electronics Engineers (IEEE) 754 binary64. La especificación JSON explica por qué usar números fuera de este límite es un procedimiento incorrecto debido a problemas de interoperabilidad. Estos problemas son especialmente relevantes para la columna de clave de partición porque es inmutable y requiere la migración de datos para cambiar más adelante.

Inserción de datos

Al modelar datos en Azure Cosmos DB, trate las entidades como elementos independientes representados como documentos JSON.

Para la comparación, veamos primero cómo podríamos modelar datos en una base de datos relacional. En el siguiente ejemplo se muestra cómo podría almacenarse una persona en una base de datos relacional.

Recorte de pantalla del modelo de base de datos relacional.

Cuando se trabaja con bases de datos relacionales, la estrategia es normalizar todos los datos. La normalización de los datos implica normalmente tomar una entidad, como una persona, y dividirla en componentes discretos. En el ejemplo anterior, una persona puede tener varios registros de información de contacto, así como varios registros de dirección. Puede desglosar aún más los detalles de contacto mediante la extracción de campos comunes, como el tipo. El mismo enfoque se aplica a las direcciones. Cada registro puede clasificarse como Hogar o Negocio.

La premisa principal cuando se normalizan datos es la de evitar el almacenamiento de datos redundantes en cada registro y, en su lugar, hacer referencia a los datos. En este ejemplo, para leer a una persona, con toda su información de contacto y direcciones, tendrá que usar CONEXIONES para volver a componer (o desnormalizar) de forma eficaz los datos en tiempo de ejecución.

SELECT p.FirstName, p.LastName, a.City, cd.Detail
FROM Person p
JOIN ContactDetail cd ON cd.PersonId = p.Id
JOIN ContactDetailType cdt ON cdt.Id = cd.TypeId
JOIN Address a ON a.PersonId = p.Id

La actualización de los datos de contacto y las direcciones de una sola persona requiere operaciones de escritura en muchas tablas individuales.

Ahora veamos cómo modelamos los mismos datos como una entidad independiente en Azure Cosmos DB.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "addresses": [
        {
            "line1": "100 Some Street",
            "line2": "Unit 1",
            "city": "Seattle",
            "state": "WA",
            "zip": 98012
        }
    ],
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555", "extension": 5555}
    ]
}

Utilizando este enfoque, hemos desnormalizado el registro de persona incrustando toda la información relacionada con esta persona, como sus datos de contacto y direcciones, en un único documento JSON. Además, puesto que no estamos limitados a un esquema fijo, contamos con la flexibilidad para hacer cosas como tener información de contacto de formas diferentes por completo.

Recuperar un registro de persona completa de la base de datos es ahora una única operación de lectura frente a un contenedor único para un elemento único. La actualización de los datos de contacto y las direcciones del registro de una persona es también una operación de escritura única en un elemento único.

La desnormalización de los datos puede reducir el número de consultas y actualizaciones que su aplicación necesita para completar las operaciones habituales.

Cuándo se debe realizar la incrustación

Por lo general, utilice los modelos de datos integrados cuando:

  • Existen relaciones contenidas entre entidades.
  • Existen relaciones de uno a algunos entre entidades.
  • Los datos cambian con poca frecuencia.
  • Los datos no crecen sin límite.
  • Los datos se consultan juntos con frecuencia.

Nota

Los modelos de datos desnormalizados normalmente proporcionan un mejor rendimiento de lectura .

Cuándo no se debe incrustar

Aunque la regla general en Azure Cosmos DB es desnormalizarlo todo e incrustar todos los datos en un único elemento, este enfoque puede dar lugar a situaciones que conviene evitar.

Seleccione este fragmento JSON.

{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "comments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        …
        {"id": 100001, "author": "jane", "comment": "and on we go ..."},
        …
        {"id": 1000000001, "author": "angry", "comment": "blah angry blah angry"},
        …
        {"id": ∞ + 1, "author": "bored", "comment": "oh man, will this ever end?"},
    ]
}

Este ejemplo podría ser el aspecto que tendría una entidad de entrada con comentarios incrustados si estuviéramos modelando un blog típico, o un sistema de administración de contenido (CMS). El problema con este ejemplo es que la matriz de comentarios no está limitada, lo que significa que no hay ningún límite (práctico) para el número de comentarios que puede tener cualquier publicación única. Este diseño puede causar problemas, ya que el tamaño del elemento puede crecer infinitamente, así que evítelo.

A medida que aumenta el tamaño de los elementos, transmitir, leer y actualizar los datos a escala se convierte en un reto mayor.

En este caso sería mejor tener en cuenta el siguiente modelo de datos.

Post item:
{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "recentComments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        {"id": 3, "author": "jane", "comment": "....."}
    ]
}

Comment items:
[
    {"id": 4, "postId": "1", "author": "anon", "comment": "more goodness"},
    {"id": 5, "postId": "1", "author": "bob", "comment": "tails from the field"},
    ...
    {"id": 99, "postId": "1", "author": "angry", "comment": "blah angry blah angry"},
    {"id": 100, "postId": "2", "author": "anon", "comment": "yet more"},
    ...
    {"id": 199, "postId": "2", "author": "bored", "comment": "will this ever end?"}   
]

Este modelo tiene un elemento para cada comentario con una propiedad que contiene el identificador de la entrada. Este modelo permite que las entradas contengan cualquier número de comentarios y crezcan de forma eficiente. Los usuarios que quieran ver algo más que los comentarios más recientes deberán consultar este contenedor pasando el valor de postId, que debería ser la clave de partición del contenedor de comentarios.

Otro caso en el que incrustar datos no es una buena idea es cuando los datos incrustados se usan a menudo en distintos artículos y cambian con frecuencia.

Seleccione este fragmento JSON.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        {
            "numberHeld": 100,
            "stock": { "symbol": "zbzb", "open": 1, "high": 2, "low": 0.5 }
        },
        {
            "numberHeld": 50,
            "stock": { "symbol": "xcxc", "open": 89, "high": 93.24, "low": 88.87 }
        }
    ]
}

Este ejemplo podría representar la cartera de acciones de una persona. Hemos elegido insertar la información de las acciones en cada documento de la cartera. En un entorno en el que los datos relacionados cambian con frecuencia, incrustar datos que cambian con frecuencia va a significar que usted está actualizando constantemente cada cartera. Usando el ejemplo de una aplicación de negociación de acciones, estará actualizando cada elemento de la cartera cada vez que se negocie una acción.

La acción zbzb puede negociarse cientos de veces en un solo día, y miles de usuarios podrían tener zbzb en sus carteras. Con un modelo de datos como el del ejemplo, el sistema debe actualizar miles de documentos de la cartera muchas veces al día, lo que no se escala bien.

Datos de referencia

La incrustación de datos funciona bien en muchos casos, pero hay escenarios en los que la desnormalización de sus datos causa más problemas de los que merece la pena. Entonces, ¿qué puede hacer?

Puede crear relaciones entre entidades en las bases de datos de documentos, no solo en las bases de datos relacionales. En una base de datos de documentos, un elemento puede incluir información que conecte con datos de otros documentos. Azure Cosmos DB no está diseñado para relaciones complejas como las de las bases de datos relacionales, pero los enlaces sencillos entre elementos son posibles y pueden resultar útiles.

En el JSON hemos optado por utilizar el ejemplo de una cartera de acciones anteriores, pero esta vez hacemos referencia al artículo en existencias en la cartera en lugar de incrustarlo. De esa forma, cuando el artículo comercial cambia frecuentemente a lo largo del día, el único elemento que tiene que actualizarse es el documento de acciones único.

Person document:
{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        { "numberHeld":  100, "stockId": 1},
        { "numberHeld":  50, "stockId": 2}
    ]
}

Stock documents:
{
    "id": "1",
    "symbol": "zbzb",
    "open": 1,
    "high": 2,
    "low": 0.5,
    "vol": 11970000,
    "mkt-cap": 42000000,
    "pe": 5.89
},
{
    "id": "2",
    "symbol": "xcxc",
    "open": 89,
    "high": 93.24,
    "low": 88.87,
    "vol": 2970200,
    "mkt-cap": 1005000,
    "pe": 75.82
}

Un inconveniente de este enfoque es que su aplicación debe realizar varias peticiones a la base de datos para obtener información sobre cada acción de la cartera de una persona. Este diseño hace que la escritura de datos sea más rápida, ya que las actualizaciones se producen con frecuencia. Sin embargo, hace que la lectura o consulta de datos sea más lenta, lo que es menos importante para este sistema.

Nota

Los modelos de datos normalizados pueden requerir varios viajes de ida y vuelta al servidor.

¿Qué sucede con las claves externas?

Dado que no existe el concepto de restricción, como una clave externa, la base de datos no verifica ninguna relación entre documentos; estos vínculos son efectivamente "débiles". Si quiere asegurarse de que los datos a los que se refiere un elemento existen realmente, deberá realizar este paso en su aplicación, o utilizando desencadenadores del lado del servidor o procedimientos almacenados en Azure Cosmos DB.

Cuándo se debe establecer referencias

Por lo general, se deben utilizar modelos de datos normalizados cuando:

  • Representación de relaciones de uno a muchos.
  • Se realiza una representación de las relaciones de muchos a muchos.
  • Los datos relacionados cambian con frecuencia.
  • Los datos de referencia podrían ser ilimitados.

Nota

Normalmente, la normalización proporciona un mejor rendimiento de escritura .

¿Dónde coloco la relación?

El crecimiento de la relación ayuda a determinar en qué elemento almacenar la referencia.

Si observamos el JSON que sirve como modelo para los editores y los libros.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press",
    "books": [ 1, 2, 3, ..., 100, ..., 1000]
}

Book documents:
{"id": "1", "name": "Azure Cosmos DB 101" }
{"id": "2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "3", "name": "Taking over the world one JSON doc at a time" }
...
{"id": "100", "name": "Learn about Azure Cosmos DB" }
...
{"id": "1000", "name": "Deep Dive into Azure Cosmos DB" }

Si el número de libros por editorial es pequeño y el crecimiento es limitado, almacenar la referencia del libro dentro del artículo de la editorial puede resultar útil. Sin embargo, si el número de libros por editor no tiene un límite, este modelo de datos provocaría matrices crecientes y mutables como ocurre en el documento de editor del ejemplo.

El cambio de estructura da como resultado un modelo que representa los mismos datos pero evita las grandes colecciones mutables.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press"
}

Book documents:
{"id": "1","name": "Azure Cosmos DB 101", "pub-id": "mspress"}
{"id": "2","name": "Azure Cosmos DB for RDBMS Users", "pub-id": "mspress"}
{"id": "3","name": "Taking over the world one JSON doc at a time", "pub-id": "mspress"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

En este ejemplo, el documento del editor ya no contiene una colección no limitada. En su lugar, cada documento de libro incluye una referencia a su editor.

Cómo modelar relaciones de varios a varios

En una base de datos relacional, las relaciones de muchos a muchos suelen modelarse con tablas de unión. Estas relaciones solo unen registros de otras tablas.

Recorte de pantalla que muestra cómo unir tablas.

Podría verse tentado a replicar lo mismo con documentos y producir un modelo de datos que tenga un aspecto similar al siguiente.

Author documents:
{"id": "a1", "name": "Thomas Andersen" }
{"id": "a2", "name": "William Wakefield" }

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101" }
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "b3", "name": "Taking over the world one JSON doc at a time" }
{"id": "b4", "name": "Learn about Azure Cosmos DB" }
{"id": "b5", "name": "Deep Dive into Azure Cosmos DB" }

Joining documents:
{"authorId": "a1", "bookId": "b1" }
{"authorId": "a2", "bookId": "b1" }
{"authorId": "a1", "bookId": "b2" }
{"authorId": "a1", "bookId": "b3" }

Este enfoque funciona, pero cargar un autor con sus libros o un libro con su autor siempre requiere al menos dos consultas adicionales a la base de datos. Una consulta al elemento que se va a unir y luego otra consulta para obtener el elemento real que se va a unir.

Si esta combinación lo único que hace es pegar entre sí dos datos, ¿por qué no quitarla completamente? Tenga en cuenta el siguiente ejemplo.

Author documents:
{"id": "a1", "name": "Thomas Andersen", "books": ["b1", "b2", "b3"]}
{"id": "a2", "name": "William Wakefield", "books": ["b1", "b4"]}

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101", "authors": ["a1", "a2"]}
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users", "authors": ["a1"]}
{"id": "b3", "name": "Learn about Azure Cosmos DB", "authors": ["a1"]}
{"id": "b4", "name": "Deep Dive into Azure Cosmos DB", "authors": ["a2"]}

Con este modelo, puede ver fácilmente qué libros escribió un autor con solo mirar su documento. También puede ver qué autores escribieron un libro comprobando el documento del libro. No es necesario usar una tabla de combinación independiente ni realizar consultas adicionales. Este modelo hace que sea más rápido y sencillo que la aplicación obtenga los datos que necesita.

Modelos de datos híbridos

Exploramos la incrustación (o desnormalización) y la referenciación (o normalización) de datos. Cada enfoque ofrece ventajas e implica contrapartidas.

No siempre tiene que ser una cosa o la otra. No dude en mezclar un poco las cosas.

En función de los patrones de uso y las cargas de trabajo específicas de su aplicación, puede tener sentido mezclar datos integrados y referenciados. Este enfoque podría simplificar la lógica de la aplicación, reducir los viajes de ida y vuelta del servidor y mantener un buen rendimiento.

Considere el siguiente JSON.

Author documents:
{
    "id": "a1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "countOfBooks": 3,
    "books": ["b1", "b2", "b3"],
    "images": [
        {"thumbnail": "https://....png"}
        {"profile": "https://....png"}
        {"large": "https://....png"}
    ]
},
{
    "id": "a2",
    "firstName": "William",
    "lastName": "Wakefield",
    "countOfBooks": 1,
    "books": ["b1"],
    "images": [
        {"thumbnail": "https://....png"}
    ]
}

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
        {"id": "a2", "name": "William Wakefield", "thumbnailUrl": "https://....png"}
    ]
},
{
    "id": "b2",
    "name": "Azure Cosmos DB for RDBMS Users",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
    ]
}

Aquí hemos seguido (en su mayor parte) el modelo incrustado, en el que los datos de otras entidades se incrustan en el documento de nivel superior, pero se hace referencia a otros datos.

Si observa el documento del libro, podemos ver algunos campos interesantes cuando nos centramos en la matriz de autores. Hay un campo id que es el campo que se usa para volver a hacer referencia a un documento de autor, una práctica estándar en un modelo normalizado, pero también tenemos name y thumbnailUrl. Podríamos usar solo id y dejar que la aplicación recupere cualquier información adicional que necesite del elemento correspondiente al autor utilizando el "enlace". Sin embargo, dado que la aplicación muestra el nombre del autor y una imagen en miniatura con cada libro, la desnormalización de algunos datos del autor reduce el número de viajes de ida y vuelta al servidor por cada libro de la lista.

Si el autor cambia de nombre o actualiza su foto, tendría que actualizar todos los libros que haya publicado. Sin embargo, para esta aplicación, suponiendo que los autores rara vez cambian sus nombres, este compromiso es una decisión de diseño aceptable.

En el ejemplo, hay valores agregadosprecalculados para ahorrar un procesamiento costoso durante una operación de lectura. En el ejemplo, algunos de los datos incrustados en el elemento de autor son datos que se calculan en tiempo de ejecución. Cada vez que se publica un nuevo libro, se crea un elemento de libro y el campo countOfBooks se establece en un valor calculado en función del número de documentos de libro que existen para un autor concreto. Esta optimización sería adecuada en sistemas en los que se realizan muchas operaciones de lectura, donde podemos permitirnos hacer cálculos en las escrituras para optimizarlas.

La capacidad de tener un modelo con campos calculados previamente es posible porque Azure Cosmos DB admite transacciones de varios documentos. Muchos almacenes NoSQL no pueden realizar transacciones entre documentos y, por tanto, abogan por decisiones de diseño como "incrustar siempre todo" debido a esta limitación. Con Azure Cosmos DB, puede utilizar los desencadenadores de servidor o los procedimientos almacenados que insertan los libros y actualizan los autores dentro de una transacción ACID. Ahora no tiene que incrustarlo todo en un solo artículo para asegurarse de que sus datos siguen siendo coherentes.

Distinguir entre los distintos tipos de elementos

En algunos escenarios, es posible que quiera mezclar distintos tipos de elementos en la misma colección; esta elección de diseño suele darse cuando desea que varios documentos relacionados se encuentren en la misma partición. Por ejemplo, podría colocar los libros y las reseñas en la misma colección de libros y dividirla por bookId. En tal situación, normalmente querrá añadir un campo a sus documentos que identifique su tipo para diferenciarlos.

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
}
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}

Azure Synapse Link para Azure Cosmos DB es una funcionalidad de procesamiento analítico y transaccional híbrido (HTAP) nativo de nube que permite ejecutar análisis casi en tiempo real de datos operativos en Azure Cosmos DB. Azure Synapse Link crea una integración perfecta entre Azure Cosmos DB y Azure Synapse Analytics.

Esta integración se produce a través del almacén analítico de Azure Cosmos DB, una representación en columnas de los datos transaccionales que permite análisis a gran escala sin ningún impacto en las cargas de trabajo transaccionales. El almacén analítico le permite ejecutar consultas rápidas y asequibles en grandes conjuntos de datos. No es necesario copiar los datos ni preocuparse por ralentizar la base de datos principal. Cuando activa el almacén analítico para un contenedor, cada cambio que realiza en sus datos se copia en el almacén analítico casi de inmediato. No es necesario configurar la fuente de cambios ni ejecutar tareas de extracción, transformación y carga (ETL). El sistema mantiene ambos almacenes sincronizados por usted.

Con Azure Synapse Link, ahora puede conectarse directamente a los contenedores de Azure Cosmos DB desde Azure Synapse Analytics y acceder al almacén analítico sin costo por unidad de solicitud (RU). Azure Synapse Analytics admite actualmente Azure Synapse Link con Apache Spark en Synapse y grupos de SQL sin servidor. Si tiene una cuenta de Azure Cosmos DB distribuida de forma global, después de habilitar el almacén analítico para un contenedor, estará disponible en todas las regiones de esa cuenta.

Inferencia automática del esquema del almacén analítico

El almacén transaccional de Azure Cosmos DB es un almacén de datos semiestructurados orientado a filas, mientras que el almacén analítico utiliza un formato columnar y estructurado. Esta conversión se realiza automáticamente para los clientes, con las reglas de inferencia de esquema del almacén analítico. Hay límites en el proceso de conversión: número máximo de niveles anidados, número máximo de propiedades, tipos de datos no admitidos y mucho más.

Nota

En el contexto del almacén analítico, consideramos las siguientes estructuras como propiedad:

  • "Elementos" JSON o "pares cadena-valor separados por :"
  • Objetos JSON, delimitados por { y }
  • Matrices JSON, delimitadas por [ y ]

Puede minimizar el impacto de las conversiones de inferencia de esquema y maximizar sus capacidades analíticas mediante las técnicas siguientes.

Normalización

La normalización pierde relevancia porque Azure Synapse Link permite unir contenedores mediante T-SQL o Spark SQL. Las ventajas esperadas de la normalización son:

  • Menor huella de datos en el almacenamiento tanto transaccional como analítico.
  • Transacciones más pequeñas.
  • Menos propiedades por documento.
  • Estructuras de datos con menos niveles anidados.

Tener menos propiedades y menos niveles en sus datos agiliza las consultas analíticas. También ayuda a garantizar que todas las partes de sus datos se incluyan en el almacén analítico. Tal y como se describe en el artículo sobre reglas de inferencia automática de esquema, hay límites en el número de niveles y propiedades que se representan en el almacén analítico.

Otro factor importante para la normalización es que los grupos de SQL sin servidor de Azure Synapse admiten conjuntos de resultados con hasta 1000 columnas y exponer columnas anidadas también cuenta para ese límite. En otras palabras, tanto el almacén analítico como los grupos sin servidores de Synapse SQL tienen un límite de 1000 propiedades.

Pero ¿qué hacer puesto que la desnormalización es una técnica importante de modelado de datos para Azure Cosmos DB? La respuesta es que debe encontrar el equilibrio adecuado para sus cargas de trabajo transaccionales y analíticas.

Clave de partición

La clave de partición (PK) de Azure Cosmos DB no se usa en el almacén analítico. Y ahora puede usar la creación de particiones personalizadas del almacén analítico en copias del almacén analítico con cualquier PK que desee. Debido a este aislamiento, puede elegir una PK para los datos transaccionales con el foco en la ingesta de datos y las lecturas puntuales, mientras que las consultas entre particiones se pueden realizar con Azure Synapse Link. Veamos un ejemplo:

En un hipotético escenario global de IoT, device id sirve como una buena clave de partición porque todos los dispositivos generan un volumen de datos similar, lo que evita problemas de particiones frecuentes. Pero si quiere analizar los datos de más de un dispositivo, como "todos los datos de ayer" o "totales por ciudad", es posible que tenga problemas, ya que son consultas entre particiones. Esas consultas pueden perjudicar el rendimiento transaccional, ya que utilizan parte de tu capacidad de procesamiento en unidades de solicitud para ejecutarse. Sin embargo, con Azure Synapse Link, puede ejecutar estas consultas analíticas sin costos por unidad de solicitud. El formato columnar del almacén analítico está optimizado para consultas analíticas y Azure Synapse Link admite un gran rendimiento con los tiempos de ejecución de Azure Synapse Analytics.

Nombres de propiedades y tipos de datos

En el artículo reglas de inferencia automática de esquema se enumeran los tipos de datos admitidos. Aunque los tiempos de ejecución de Azure Synapse pueden procesar los tipos de datos compatibles de forma diferente, los tipos de datos no compatibles bloquean la representación en el almacén analítico. Un ejemplo es: al usar cadenas DateTime que siguen el estándar ISO 8601 UTC, los grupos de Spark en Azure Synapse representan estas columnas como string y los grupos sin servidor SQL en Azure Synapse representan estas columnas como varchar(8000).

Otro desafío es que Azure Synapse Spark no acepta todos los caracteres. Aunque se aceptan espacios en blanco, caracteres como dos puntos, acento grave y coma no lo son. Supongamos que el elemento tiene una propiedad denominada "Nombre, apellidos". Esta propiedad se representa en el almacén analítico y el grupo sin servidor de Synapse SQL puede leerla sin ningún problema. Sin embargo, como se encuentra en un almacén analítico, Azure Synapse Spark no puede leer ningún dato del almacén analítico, incluidas todas las demás propiedades. Al final, no puede usar Azure Synapse Spark cuando tiene una propiedad con los caracteres no admitidos en sus nombres.

Aplanamiento de datos

Cada propiedad del nivel superior de los datos de Azure Cosmos DB se convierte en una columna del almacén analítico. Las propiedades dentro de objetos o matrices anidados se almacenan como JSON en el almacén analítico, conservando su estructura. Las estructuras anidadas exigen un procesamiento adicional de los entornos de ejecución de Azure Synapse para aplanar los datos en formato estructurado, lo que puede ser un reto en escenarios de macrodatos.

El elemento solo tiene dos columnas en el almacén analítico, id y contactDetails. El resto de datos, email y phone, requieren un procesamiento adicional mediante funciones SQL para poder leerse individualmente.


{
    "id": "1",
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555"}
    ]
}

El elemento tiene tres columnas en el almacén analítico: id, email y phone. Todos los datos son accesibles directamente como columnas.


{
    "id": "1",
    "email": "thomas@andersen.com",
    "phone": "+1 555 555-5555"
}

Organización de los datos en capas

Azure Synapse Link le permite reducir los costos desde las siguientes perspectivas:

  • Menos consultas que se ejecutan en la base de datos transaccional.
  • Una PK optimizada para la ingesta de datos y las lecturas puntuales, lo que reduce la huella de datos, los escenarios de particiones activas y las divisiones de particiones.
  • Organización de los datos en capas, porque el TTL analítico (ATTL) es independiente del TTL transaccional (TTTL). Puede mantener los datos transaccionales en el almacén transaccional durante unos días, semanas o meses, y mantener los datos en el almacén analítico durante años o para siempre. El formato por columnas del almacén analítico proporciona una compresión natural de los datos, del 50 % al 90 %. Y su costo por GB es aproximadamente un 10 % del precio real del almacén transaccional. Para más información sobre las limitaciones de copia de seguridad actuales, consulte Introducción al almacén analítico.
  • No hay trabajos de ETL en ejecución en su entorno, lo que significa que no necesita asignar unidades de solicitud para ellos.

Redundancia controlada

Esta técnica es una gran alternativa para situaciones en las que ya existe un modelo de datos y no se puede cambiar. El modelo de datos actual no funciona bien con el almacén analítico. Esta ventaja existe porque el almacén analítico tiene reglas que limitan cuántos niveles de datos se pueden anidar y cuántas propiedades puede tener cada documento. Si sus datos son demasiado complejos o tienen demasiados campos, es posible que alguna información importante no se incluya en el almacén analítico. Si este es su caso, puede usar la fuente de cambios de Azure Cosmos DB para replicar sus datos en otro contenedor, aplicando las transformaciones necesarias para un modelo de datos compatible con Azure Synapse Link. Veamos un ejemplo:

Escenario

El contenedor CustomersOrdersAndItems se usa para almacenar pedidos en línea, incluidos los detalles del cliente y de los artículos: dirección de facturación, dirección de entrega, método de entrega, estado de entrega, precio de los artículos, etc. Solo se representan las primeras 1000 propiedades y la información clave no se incluye en el almacén analítico, lo que bloquea el uso de Azure Synapse Link. El contenedor tiene petabytes de registros; no es posible modificar la aplicación ni remodelar los datos.

Otro aspecto del problema es el gran volumen de datos. El departamento de análisis usa miles de millones de filas constantemente, lo que les impide usar TTTL para la eliminación de datos antiguos. Mantener todo el historial de datos en la base de datos transaccional debido a las necesidades analíticas les obliga a aumentar constantemente el aprovisionamiento de unidades de solicitud, lo que afecta a los costos. Las cargas de trabajo transaccionales y analíticas compiten por los mismos recursos al mismo tiempo.

¿Qué puede hacer?

Solución con Change Feed

  • El equipo de ingeniería decidió usar Change Feed para rellenar tres nuevos contenedores: Customers, Orders y Items. Con la fuente de cambios, están normalizando y aplanando los datos. La información innecesaria se quita del modelo de datos y cada contenedor tiene cerca de 100 propiedades, lo que evita la pérdida de datos debido a los límites de inferencia automática del esquema.
  • Estos nuevos contenedores tienen habilitado el almacén analítico, y el Departamento de análisis usa Synapse Analytics para leer los datos. Esto reduce el uso de unidades de solicitud porque las consultas analíticas se ejecutan en Synapse Apache Spark y en grupos de SQL sin servidor.
  • El contenedor CustomersOrdersAndItems ahora tiene un conjunto de un período de vida (TTL) para conservar los datos solo durante seis meses, lo que permite otra reducción del uso de unidades de solicitud, ya que hay un mínimo de una unidad de solicitud por GB en Azure Cosmos DB. Menos datos, menos unidades de solicitud.

Puntos clave

Lo más importante de este artículo es que el modelado de datos en un escenario sin esquemas es tan importante como siempre.

Como no hay ninguna manera única de representar un elemento de datos en una pantalla, no hay una única forma de modelar los datos. Debe comprender la aplicación y cómo genera, consume y procesa los datos. Al aplicar las directrices que se presentan aquí, puede crear un modelo que aborde las necesidades inmediatas de la aplicación. Cuando cambie la aplicación, use la flexibilidad de una base de datos sin esquemas para adaptar y evolucionar fácilmente el modelo de datos.