Caso de publicidad|1 billón de datos, consulta <10s, sobre la postura correcta de construir un sistema de publicidad basado en OLAP

A medida que el dividendo del tráfico se desvanece gradualmente, más y más empresas de publicidad y profesionales han comenzado a explorar nuevos caminos de marketing refinado, reemplazando el tráfico completo anterior y el bombardeo publicitario extenso. El marketing refinado significa seleccionar el público objetivo más potencial entre cientos de millones de personas, lo que sin duda plantea un gran desafío técnico para las capacidades del almacén de datos que brindan soporte básico al motor.

fondo

El análisis de selección de multitudes es la función principal de la plataforma de retratos de clientes (CDP). Los analistas usan varias combinaciones de etiquetas para seleccionar el grupo de personas más adecuado y luego impulsan los anuncios para lograr efectos de entrega precisos. Al mismo tiempo, debido al diferente tamaño del conjunto de resultados de consultas multitudinarias bajo diferentes combinaciones de etiquetas, los analistas necesitan pasar por múltiples ajustes lógicos para obtener el "mejor" paquete multitudinario en una entrega de publicidad. Bajo tales operaciones de alta frecuencia, la plataforma de retratos generalmente encuentra dos problemas:

  • En primer lugar, debido a que dicho análisis de consulta es temporal y la cantidad de varias combinaciones de etiquetas es enorme, el cálculo previo fuera de línea no puede satisfacer este tipo de flexibilidad.

  • En segundo lugar, dado que este tipo de consulta es un escenario en tiempo real, el rendimiento de la consulta se vuelve muy crítico. Por lo general, una consulta es a nivel de minutos, lo que lleva mucho tiempo y no puede satisfacer las necesidades de los analistas.

En este artículo, compartiremos la solución de la consulta de selección de círculo de multitud en escenarios OLAP de análisis en tiempo real y presentaremos cómo usar ByteHouse para acelerar dichas consultas. Desde la perspectiva del rendimiento de los datos, según los datos de prueba de mil millones de usuarios, la consulta de multitud P99 de ByteHouse es inferior a 10 s, lo que muestra un rendimiento excelente.

modelo de escena

Una estructura de datos que admite la selección de grupos es más o menos la siguiente:

a50d561e686d9cf25ddcd212b379ae21.png

La información de registro de usuario ingresa al lago de datos a través del flujo de usuarios, y la información de comportamiento del usuario ingresa al lago de datos a través del flujo de eventos. Luego a través de tareas de producción de etiquetas, etiquetamos a cada usuario.

Debido al tiempo real y la flexibilidad de la consulta instantánea, los datos transformados generalmente se escriben en motores OLAP, como ByteHouse, para proporcionar una consulta SQL flexible y en tiempo real. Cuando los usuarios analizan, generalmente visualizan y construyen la lógica de etiquetas desde la interfaz de la aplicación de la plataforma de retratos, y luego la aplicación de la plataforma convierte estas lógicas en SQL y las envía a ByteHouse para su procesamiento.

Desde la perspectiva del modelo de datos, la mayoría de los formatos almacenados en el almacén de datos o lago de datos se basan en etiquetas de identificación, por ejemplo:

id_usuario sexo edad etiquetas
10001 F 20 []
10002 METRO 22 [día_1,día_2]
10003 F 23 [etiqueta_1]
10004 METRO 24 [etiqueta_2]
10005 F 25 [día_1,día_2]

En el análisis de multitudes, los siguientes patrones basados ​​en etiquetas serían más apropiados, por ejemplo:

etiquetas usuarios activos
etiqueta_1 [10002, 10003, 10005]
etiqueta_2 [10002,10005]

Los datos generalmente se almacenan en función del usuario como cuerpo principal. Esta situación da como resultado una gran cantidad de usuarios y muchos campos innecesarios. Luego, cuando el usuario filtra la multitud combinando etiquetas (etiqueta), casi todas las filas deben escanearse, lo que hace que la sobrecarga de rendimiento aumente con el aumento de etiquetas y usuarios.

Cuando los datos toman las etiquetas como cuerpo principal, hay dos cambios relativamente grandes:

  • En primer lugar, solo se conservarán las dimensiones relacionadas con la multitud y se eliminará otra información como el sexo, la edad, etc.

  • En segundo lugar, active_users almacena todos los ID de usuario en forma de matriz. Un beneficio importante de esta operación es reducir el número de filas y el tamaño de los datos.

Bajo este modelo, la selección de usuarios de acuerdo con la combinación de etiquetas se convertirá en una operación de intersección y complemento del conjunto, y el rendimiento mejorará significativamente en comparación con el primer modelo.

Tipo de mapa de bits de ByteHouse

El segundo modelo de almacenamiento puede usar el siguiente ByteHouse SQL para crear tablas:

CREATE TABLE id_tags (
    tags            String,
    active_users    Array<UInt64>
) Engine = CnchMergeTree() order by tags

La consulta de selección de multitudes, como encontrar la cantidad de personas que satisfacen tanto tag_1 como tag_2, se puede realizar con el siguiente SQL:

WITH (SELECT active_users as tag_1
        FROM id_tags
        WHERE tags = 'tag_1') as tag_1_user,
WITH(SELECT active_users as tag_2
        FROM id_tags
        WHERE tags = 'tag_2') as tag_2_user,
SELECT length(arrayIntersect(tag_1_user, tag_2_user))

Aunque este modelo puede simplificar algunas operaciones, la selección de cada etiqueta requiere una subconsulta (con parte). Este método tiene una gran cantidad de desperdicio para los escaneos de tablas y está relacionado linealmente con la cantidad de etiquetas.

Para resolver este problema, ByteHouse tiene un tipo de mapa de bits incorporado, que puede usar bits directamente para indicar si una etiqueta puede existir.

Siguiendo el ejemplo anterior, después de usar BitMap, la declaración de creación de la tabla se cambia a:

CREATE TABLE id_tags (
    tags            String,
    active_users    BitMap64
) Engine = CnchMergeTree() order by tags

Tenga en cuenta aquí que solo cambiamos el tipo de active_users de Array a BitMap64, y el resto permaneció sin cambios.

Para la misma consulta de "encontrar el número de personas que cumplen tanto tag_1 como tag_2", utilice la siguiente consulta:

SELECT bitmapCount('tag_1&tag_2')
FROM tag_uids_map

Usamos bits en lugar de la matriz original para que la consulta se pueda optimizar para que se realice en un solo escaneo de tabla.

Según los escenarios en línea internos de ByteDance, observamos que la optimización de consultas anterior puede mejorar el rendimiento entre 10 y 50 veces en escenarios de etiquetas múltiples.

importación de datos

No existe una diferencia significativa entre escribir datos en una tabla de mapa de bits y una tabla normal. Por ejemplo, el método de inserción de lotes pequeños se puede utilizar de la siguiente manera:

INSERT INTO TABLE id_tags values ('tag_1', [2,4,6]),('tag_2', [1,3,5])

Debido a que active_users en id_tags se define como el tipo de BitMap64, los valores de matriz [1,3,5], [2,4,6] se convertirán automáticamente a BitMap64. Los cálculos y el almacenamiento posteriores serán del tipo BitMap64.

Al importar grandes lotes de archivos, podemos utilizar el servicio de importación proporcionado por ByteHouse.Actualmente, tanto los modos de importación fuera de línea (TOS, LASFS) como en tiempo real (Kafka) admiten la importación de datos de mapa de bits. La escritura de secuencias (como la escritura directa de Flink) se puede escribir en forma de inserción a través de la interfaz JDBC.

funciones relacionadas

Además de admitir datos de tipo BitMap para operaciones de intersección y complemento, ByteHouse también tiene una gran cantidad de funciones de columna integradas, como bitmapColumnAndrecibir una columna de mapa de bits y realizar andoperaciones en todos los mapas de bits de la columna, y bitmapColumnCardinalitydevolver la cantidad de elementos en todos mapas de bits en una columna. Para obtener más información, consulte la documentación oficial.

Introducción al Principio de BitEngine

Análisis de estructura de mapa de bits

Suponiendo que una ID de usuario está representada por un número entero sin signo de 32 bits, el uso de almacenamiento de bits convencional requiere 2^32 bits ~ 512 MB de espacio. Si cada etiqueta debe corresponder a 512 MB de espacio, cuando aumente el número de etiquetas, la capacidad de almacenamiento será enorme. De hecho, muy pocas empresas encontrarán 2^32 alrededor de 4 mil millones de usuarios, por lo que la distribución de ID de usuario en escenarios reales es muy escasa.

En función de esta característica, podemos usar el mapa de bits Roaring para comprimir aún más este espacio. Como se muestra abajo:

57ba51ac41ce7effe4ed27178a8a70d2.png

En el mapa de bits Roaring de 32 bits, los primeros 16 bits se utilizan para la creación de depósitos. Si no hay datos en este rango de valores, el depósito no se creará y los últimos 16 bits se almacenarán en el contenedor correspondiente. Hay dos tipos de Contenedores:

  • Contenedor de matriz: cuando la cantidad de datos es pequeña (generalmente menos de 8K de capacidad), ahorra más espacio

  • El contenedor de mapa de bits es adecuado para almacenar datos densos y ocupa poco espacio

Al calcular, solo necesita calcular los valores en algunos cubos. Cuando se expande a roaringbitmap de 64 bits, podemos usar un mapa <uint32_t, Roaring> para admitirlo. Los primeros 32 bits se usan como la clave del mapa, y los últimos 32 bits se almacenan en roaringbitmap.

optimización del diccionario

En la mayoría de los escenarios, el mapa de bits rugiente anterior ya tiene un buen rendimiento. Pero en la escena real de los bytes, encontramos que debido a que el ID_usuario no se genera continuamente, la proporción del número de contenedores de matriz será muy alta. La operación de intersección y complemento de dos poblaciones dispersas se convierte en el cálculo de dos matrices ordenadas. Comparado con el cálculo simple de bits, este cálculo todavía tiene diferencias obvias en el rendimiento.

Por lo tanto, en ByteHouse, codificamos los datos a través de un diccionario para que los datos estén más centralizados.

La forma de habilitar la optimización del diccionario es la siguiente:

CREATE TABLE id_tags (
    tags            String,
    active_users    BitMap64 BitEngineEncode
) Engine = CnchMergeTree() order by tags

En esencia, el servicio de diccionario es un mapeo de ontología. Puede buscar el valor a través de la clave, y también puede buscar la clave a través del valor, donde la clave es el valor original y el valor es el valor codificado. Después de habilitar la codificación, ByteHouse se basará en un archivo de diccionario. De forma predeterminada, ByteHouse mantendrá un archivo de diccionario internamente.

Cuando se actualiza la tabla inferior, el archivo de diccionario interno también se actualiza de forma asíncrona. ByteHouse también permite a los usuarios mantener diccionarios externos, que no se ampliarán aquí.

Resumir

El análisis de multitudes es la función básica de la plataforma de retratos. Este artículo presenta cómo usar el tipo de mapa de bits incorporado de ByteHouse para admitir consultas y análisis de retratos en tiempo real. Actualmente, ByteHouse Cloud Data Warehouse y Enterprise Edition han aterrizado en Volcano Engine. En el futuro, Volcano Engine continuará brindando a los clientes ByteDance y las mejores prácticas externas a través de ByteHouse, y creará una plataforma interactiva de análisis de big data para hacer frente a las necesidades comerciales complejas y cambiantes y los escenarios de datos de crecimiento de alta velocidad.

Supongo que te gusta

Origin blog.csdn.net/ByteDanceTech/article/details/132242022
Recomendado
Clasificación