Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Las directivas de filtro de fila y máscara de columna presentan lógica que se ejecuta en el momento de la consulta, por lo que el rendimiento depende de cómo diseñe las directivas. No hay ningún enfoque adecuado para cada carga de trabajo. El mejor enfoque depende del volumen de datos, los patrones de consulta, cómo interactúan los usuarios con las tablas protegidas y el comportamiento deseado de enmascaramiento o filtrado. En las secciones siguientes se tratan las consideraciones de rendimiento más comunes. Úselas como lista de comprobación al diseñar las directivas y probarlas con consultas representativas antes de realizar la implementación en producción.
Información general sobre rendimiento
| Consideración | Description |
|---|---|
| Reducción de la complejidad de UDF | La lógica de UDF compleja puede impedir el rendimiento de las consultas; las funciones sencillas funcionan mejor. |
| Enfoque para los principales de destino | Decida si debe implementar la lógica basada en principios en las cláusulas de TO/EXCEPT de la directiva o dentro de la UDF mediante funciones de identidad. |
| Uso de expresiones deterministas y seguras para errores | Las funciones y expresiones no deterministas que pueden producir errores reducen la capacidad del optimizador para almacenar en caché los resultados y las operaciones de reordenación. |
| Evitar UDFs de Python | Use UDF de SQL en lugar de Python UDF siempre que sea posible. |
| Mantener pequeñas tablas de búsqueda | Las UDF que hacen referencia a tablas externas funcionan mejor cuando esas tablas son lo suficientemente pequeñas como para difundirlas. |
| Comprender optimizaciones de predicados en tablas protegidas | Las consultas en tablas protegidas pueden no beneficiarse de la eliminación de particiones o la agrupación en clústeres líquidos si los predicados tienen efectos secundarios. |
| Reutiliza máscaras de columna cuando sea posible | Cada máscara distinta de una tabla agrega sobrecarga; reutilizar la misma función entre columnas puede reducirla. |
| Evitar enmascaramiento de expresiones regulares en campos de texto grandes | El enmascaramiento basado en regex en documentos serializados obliga al motor a examinar y reescribir toda la carga de cada fila. |
Reducción de la complejidad de UDF
La UDF de una directiva de ABAC se ejecuta para cada fila (filtros de fila) o cada valor de columna coincidente (máscaras de columna) durante la ejecución de la consulta. La complejidad de la UDF afecta directamente al rendimiento de las consultas.
Sí:
- Mantenga las UDF sencillas. Favorezca las declaraciones básicas
CASEy las expresiones booleanas simples. - Haga referencia solo a columnas de tabla de destino en UDF tanto como sea posible. Esto habilita la inserción del predicado.
- Si la UDF debe hacer referencia a tablas externas, asegúrese de que cualquier referencia externa sea lo suficientemente pequeña como para ser transmitida eficientemente. Asegúrese de que las tablas a las que se hace referencia estén optimizadas y particionadas para que coincidan con el patrón de acceso de la directiva. Por ejemplo, cree particiones de una tabla de búsqueda de directivas por nombre de usuario.
- Evite el anidamiento de múltiples niveles y las invocaciones de funciones innecesarias. Use las funciones SQL integradas tanto como sea posible.
Evitar:
- Llamadas api externas o búsquedas a otras bases de datos en UDF. Las llamadas de red pueden introducir latencia adicional y tiempos de espera.
- Subconsultas complejas o combinaciones con tablas grandes. Estos impiden las combinaciones hash de difusión y fuerzan las combinaciones de bucle anidadas.
- Regex pesado en campos de texto grandes. Consulte Regex en campos de texto grandes.
- Búsquedas de metadatos por fila, por ejemplo, consultando
information_schema.
Enfoque para los principales de destino
Al escribir una política de ABAC, decide dónde implementar la lógica basada en el sujeto: en las cláusulas de la política TO/EXCEPT, o dentro de la UDF utilizando funciones de identidad como current_user() y is_account_group_member().
En general, use las cláusulas de la directiva TO/EXCEPT para definir a qué entidades de seguridad se aplica la directiva. Esto simplifica la definición de directiva y la UDF se centra en la transformación, el filtrado o el enmascaramiento de datos. La EXCEPT cláusula elimina la directiva por completo para los usuarios exentos, lo que significa que no hay ejecución de UDF para esos usuarios.
Cuando la lógica condicional es demasiado compleja para las cláusulas principales de la directiva, las funciones de identidad dentro de la UDF son una alternativa posible. Estas funciones se resuelven una vez durante el análisis de consultas, no por fila. Varias llamadas a funciones de identidad como is_account_group_member() con distintos argumentos de grupo dan como resultado una sola llamada a la API de UC, por lo que el impacto en el rendimiento suele ser mínimo.
La siguiente UDF es eficaz porque solo se basa en funciones de identidad, que se resuelven una vez durante el análisis de consultas:
CREATE OR REPLACE FUNCTION rowfilter()
RETURNS BOOLEAN
RETURN
CASE
WHEN is_account_group_member('auditors') OR is_account_group_member('external-auditors') THEN true
WHEN is_account_group_member('low-privileged') THEN false
WHEN session_user() = 'admin@organization.com' THEN true
ELSE false
END;
Por el contrario, la siguiente función UDF es más lenta porque codifica los privilegios en una tabla secundaria, lo que requiere una búsqueda de tabla adicional:
CREATE OR REPLACE FUNCTION rowfilter()
RETURNS BOOLEAN
RETURN
CASE WHEN EXISTS(SELECT 1 FROM access_lease WHERE user = session_user()) THEN true
ELSE false END;
Uso de expresiones deterministas y seguras para errores
Utilice expresiones deterministas que no puedan producir errores en las funciones UDF de política y al realizar consultas sobre tablas protegidas.
Las funciones no deterministas (funciones que devuelven resultados diferentes para la misma entrada, como rand() o now()) impiden que el optimizador almacene resultados en caché o aplique el plegado constante. Las UDFs de SQL y Python admiten la palabra clave DETERMINISTIC en la instrucción CREATE FUNCTION. Para las UDF de SQL, el optimizador deriva determinismo del cuerpo de la función automáticamente, pero también puede establecerlo explícitamente. Para Python UDF, el optimizador no puede inspeccionar el cuerpo de la función, por lo que marcar explícitamente una UDF de Python como determinista es importante habilitar el almacenamiento en caché de resultados para las llamadas con argumentos idénticos.
Algunas expresiones producen errores si las entradas no son válidas, como la división ANSI en un denominador cero. Cuando el compilador de SQL detecta esta posibilidad, no puede insertar operaciones como filtros en el plan de consulta. Si lo hace, podría desencadenar errores que revelan información sobre los valores antes de que el filtrado o el enmascaramiento surtan efecto. Use alternativas seguras para errores como try_divide en lugar de /, try_cast en lugar de CASTy try_to_number en lugar de to_number. Estos devuelven NULL en caso de fallo en lugar de lanzar excepciones, lo que permite al optimizador reorganizar y plegar expresiones libremente.
Evitar UDFs de Python
Evite UDFs de Python en políticas de ABAC siempre que sea posible. Los UDF de Python deben estar incluidos en un UDF de SQL para ser utilizados en las políticas. También suelen ser más lentos que las UDF de SQL porque el optimizador no puede insertarlas ni optimizarlas, y la función Python se ejecuta para cada fila de la tabla de destino.
Si una UDF de Python es inevitable, consulte expresiones determinísticas y seguras frente a errores para aprender cómo marcarla como DETERMINISTIC y habilitar el almacenamiento en caché de resultados.
Mantener pequeñas tablas de búsqueda
Un patrón común es comprobar los derechos de acceso en una tabla de búsqueda pequeña (por ejemplo, una tabla que asigna a los usuarios a los niveles de prioridad permitidos). Si la tabla de búsqueda es significativamente menor que la tabla de destino, el optimizador convierte la subconsulta en un join hash distribuido. La tabla de búsqueda se copia en cada ejecutor y se almacena en memoria como un mapa hash, lo que permite el filtrado rápido durante el examen de la tabla. Para obtener un ejemplo de código, consulte tablas de búsqueda en UDFs de política ABAC.
- Si la tabla de búsqueda es grande, el optimizador vuelve a una combinación aleatoria, que es más lenta.
- Si el predicado de búsqueda es complejo (no una comprobación de igualdad simple), la unión de difusión también puede no ser elegible.
- Incluso con la combinación hash de difusión, cada fila sigue incurriendo en el costo de una búsqueda en la tabla hash durante la ejecución.
Comprender el empuje de predicado en tablas protegidas
El empuje de predicado es una optimización del rendimiento en la que el motor empuja las condiciones de filtro hacia la capa de almacenamiento. Esto permite al motor omitir particiones completas de datos que no coinciden con la consulta, lo que reduce significativamente la E/S y acelera la ejecución.
En el caso de las tablas protegidas por filtros de fila y máscaras de columna, esta optimización es más compleja. Este es el origen más común de problemas de rendimiento con tablas protegidas y el más difícil de solucionar, ya que los autores de directivas no pueden controlar qué consultas ejecutan los usuarios en tablas protegidas.
Cómo la barrera SecureView afecta al empuje de predicado
Tanto los filtros de fila de ABAC como los filtros de fila de nivel de tabla y las máscaras de columna usan una SecureView barrera para evitar que los predicados con efectos secundarios se inserten a través del límite de la directiva. Esto protege contra la fuga de datos a través de canales laterales, pero también puede bloquear la eliminación de particiones y las optimizaciones de agrupación en clústeres líquidos, lo que puede forzar escaneos completos de tabla. Esto se aplica incluso cuando la UDF de política se resuelve en una constante true (lo que significa que realmente no se filtran filas). La presencia de una política en una tabla presenta la SecureView barrera.
Filtros afectados por la barrera
Por lo general, el optimizador solo puede insertar predicados sin efectos secundarios a través de la SecureView barrera.
-
Optimizado (rápido): comparaciones de igualdad simples (
WHERE col = 'value') y comparaciones básicas de rango (WHERE col > 100). Estos son libres de efectos secundarios y no arriesgan la pérdida de datos. -
Bloqueado (más lento): predicados que llaman a funciones (
WHERE date_format(col, 'yyyy-MM-dd') = '1995-07-29') o introducen conversiones de tipos implícitas. Estos se mantienen por encima de laSecureViewbarrera, lo que significa que el motor debe examinar la tabla antes de aplicar el filtro.
En el ejemplo siguiente se muestra la diferencia. Considere una tabla con una clave de partición en o_orderdate y una consulta que filtra mediante date_format:
EXPLAIN SELECT * FROM orders
WHERE date_format(o_orderdate, 'yyyy-MM-dd') = '1995-07-29'
Sin una directiva, el predicado aparece dentro date_format del PartitionFilters nodo, lo que significa que la PhotonScan eliminación de particiones está activa:
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(date_format(cast(o_orderdate as timestamp), yyyy-MM-dd, ...))]
Con una directiva (incluso una que siempre devuelve true), la SecureView barrera bloquea el predicado. Se desplaza a un PhotonFilter por encima del escaneo en lugar de permanecer en PartitionFilters, lo que da como resultado un escaneo completo de tabla:
+- PhotonFilter (date_format(cast(o_orderdate as timestamp),
yyyy-MM-dd, ...) = 1995-07-29)
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate)]
Un predicado más sencillo como WHERE o_orderdate = '1995-07-29' no tiene efectos secundarios y todavía se puede presionar incluso con la SecureView barrera en su lugar:
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(o_orderdate = 1995-07-29)]
Use predicados de igualdad simples en tablas protegidas siempre que sea posible. Para los usuarios exentos, utilice la cláusula EXCEPT en la política para eliminar completamente la barrera SecureView, lo que restaura el empuje completo del predicado.
Reutiliza las máscaras de columna siempre que sea posible
La aplicación de muchas máscaras de columna distintas a una sola tabla incrementa el costo por columna. Enmascara solo las columnas que contienen datos realmente confidenciales.
Cuando varias columnas requieren la misma transformación (por ejemplo, redactando o NULL reemplazando por una cadena fija), reutilice la misma función de enmascaramiento en lugar de crear una función independiente por columna.
Azure Databricks reconoce directivas que hacen referencia a la misma UDF con los mismos argumentos que la misma máscara eficaz, por lo que la reutilización de funciones evita una sobrecarga innecesaria.
Evitar enmascaramiento de expresiones regulares en campos de texto grandes
El uso de regexp_replace dentro de una máscara de columna para censurar elementos dentro de un documento serializado (XML o JSON almacenado como una columna de tipo STRING) es costoso.
regexp_replace recorre la cadena completa para cada fila. El optimizador trata la columna STRING como un valor opaco y no puede eliminar partes sin usar del documento. El motor lee y vuelve a escribir toda la carga incluso cuando la consulta solo necesita unos pocos campos.
-- Expensive: regex masking on serialized XML
CREATE FUNCTION mask_xml_pii(raw_xml STRING)
RETURNS STRING
RETURN CASE
WHEN is_account_group_member('sensitive_data_viewers') THEN raw_xml
ELSE regexp_replace(raw_xml, '<SSN>[^<]*</SSN>', '<SSN>***</SSN>')
END;
En su lugar, materialice los campos confidenciales en columnas tipadas en una tabla independiente, y a continuación, aplique máscaras para las columnas escalares. A continuación, la función de enmascarar actúa sobre un único valor pequeño por fila en lugar de en todo el documento serializado.
-- Source table stores raw XML as STRING
-- Example XML: <person><SSN>123-45-6789</SSN><name>Alice</name><dob>1990-01-01</dob></person>
-- Recommended: extract fields into a table, then mask scalar values
CREATE TABLE person_data AS
SELECT
id,
xpath_string(raw_xml, 'person/SSN') AS ssn,
xpath_string(raw_xml, 'person/name') AS name,
xpath_string(raw_xml, 'person/dob') AS date_of_birth,
raw_xml
FROM raw_records;
-- Simple scalar mask, applied to each extracted column
CREATE FUNCTION redact(val STRING) RETURNS STRING
RETURN CASE
WHEN is_account_group_member('sensitive_data_viewers') THEN val
ELSE '***'
END;
Si puede almacenar los datos como una columna de estructura en lugar de XML, use el patrón de enmascaramiento flexible VARIANT para censurar campos individuales dentro de la estructura. Consulte Enmascarar columnas de estructura con VARIANT.
Prueba del rendimiento de UDF
Pruebas a gran escala
Pruebe el rendimiento de UDF en al menos 1 millón de filas antes de realizar la implementación en producción. Además de las pruebas de escala sintética, ejecutar consultas que representen la carga de trabajo real esperada en la tabla protegida. Realice cambios incrementales en las funciones de directiva y mida el efecto de cada cambio en lugar de probar solo la versión final.
WITH test_data AS (
SELECT
id,
your_mask_function(id) AS masked_id,
current_timestamp() AS ts
FROM (
SELECT CONCAT('ID', LPAD(CAST(id AS STRING), 6, '0')) AS id
FROM range(1000000)
)
)
SELECT
COUNT(*) AS rows_processed,
MAX(ts) - MIN(ts) AS total_duration
FROM test_data;
Reemplace your_mask_function por la UDF que está probando. Compare los resultados con y sin la política aplicada para aislar la sobrecarga de esta.