Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Het beleid voor rijfilters en kolommaskers introduceert logica die wordt uitgevoerd op het moment van query's. De prestaties zijn dus afhankelijk van hoe u uw beleid ontwerpt. Er is geen enkele juiste benadering voor elke workload. De beste aanpak is afhankelijk van uw gegevensvolume, querypatronen, hoe uw gebruikers werken met beveiligde tabellen en het gewenste maskerings- of filtergedrag. In de volgende secties worden de meest voorkomende prestatieoverwegingen behandeld. Gebruik deze als controlelijst bij het ontwerpen van uw beleid en test met representatieve query's voordat u implementeert in productie.
Overzicht van prestaties
| Consideratie | Description |
|---|---|
| UDF-complexiteit verminderen | Complexe UDF-logica kan de queryprestaties remmen; eenvoudige functies presteren beter. |
| Aanpak voor het targeten van hoofdrolspelers | Bepaal of u op principe-gebaseerde logica wilt implementeren in de clausules van TO/EXCEPT het beleid of binnen de UDF met behulp van identiteitsfuncties. |
| Deterministische, foutveilige expressies gebruiken | Niet-deterministische functies en expressies die fouten kunnen veroorzaken, verminderen de mogelijkheid van de optimalisatie om resultaten op te cachen en bewerkingen opnieuw te ordenen. |
| Avoid Python UDF's | Gebruik WAAR mogelijk SQL UDF's in plaats van Python UDF's. |
| Opzoektabellen klein houden | UDF's die verwijzen naar externe tabellen, presteren het beste wanneer deze tabellen klein genoeg zijn om te uitzenden. |
| Predicaatpushdown voor beveiligde tabellen begrijpen | Queries tegen beveiligde tabellen profiteren mogelijk niet van partitie pruning of vloeibare clustering als predicaten neveneffecten hebben. |
| Waar mogelijk kolommaskers opnieuw gebruiken | Elk afzonderlijk masker in een tabel voegt overhead toe; het hergebruik van dezelfde functie in kolommen kan dit verminderen. |
| Regex-maskering voorkomen voor grote tekstvelden | Regex-gebaseerde maskering op geserialiseerde documenten dwingt de engine om de volledige payload voor elke rij te scannen en opnieuw te schrijven. |
UDF-complexiteit verminderen
De UDF in een ABAC-beleid wordt uitgevoerd voor elke rij (rijfilters) of elke overeenkomende kolomwaarde (kolommaskers) tijdens de uitvoering van de query. De complexiteit van de UDF is rechtstreeks van invloed op queryprestaties.
Do:
- Houd UDFs eenvoudig. Geef de voorkeur aan basisinstructies
CASEen eenvoudige Booleaanse expressies. - Verwijs zo veel mogelijk naar doeltabelkolommen in UDF's (User Defined Functions). Hiermee wordt predicaatpushdown ingeschakeld.
- Als uw UDF moet verwijzen naar externe tabellen, houdt u een externe verwijzing klein genoeg om uit te zenden. Zorg ervoor dat tabellen waarnaar wordt verwezen, zijn geoptimaliseerd en gepartitioneerd zodat deze overeenkomen met het toegangspatroon van het beleid. Partitioneer bijvoorbeeld een opzoektabel voor beleid op basis van de gebruikersnaam.
- Vermijd nesten op meerdere niveaus en onnodige functie-aanroepen. Gebruik zo veel mogelijk ingebouwde SQL-functies.
Vermijden:
- Externe API-aanroepen of zoekopdrachten naar andere databases in UDF's. Netwerkoproepen kunnen extra latentie en time-outs veroorzaken.
- Complexe subqueries ofwel joins voor grote tabellen. Hiermee voorkomt u broadcast-hash-joins en forceert u geneste lusdeelnames.
- Zware reguliere expressies op grote tekstvelden. Zie Regex voor grote tekstvelden.
- Zoekacties voor metagegevens per rij, bijvoorbeeld het uitvoeren van
information_schemaquery's.
Aanpak voor het richten op principalen
Wanneer u een ABAC-beleid schrijft, bepaalt u waar u op principal gebaseerde logica wilt implementeren: in de componenten van TO/EXCEPT het beleid of in de UDF met behulp van identiteitsfuncties zoals current_user() en .is_account_group_member()
In het algemeen gebruikt u de clausules van het TO/EXCEPT beleid om te definiëren op welke principals het beleid van toepassing is. Hierdoor blijft de beleidsdefinitie eenvoudiger en de UDF is gericht op gegevenstransformatie, filteren of maskeren. De EXCEPT component elimineert het beleid volledig voor vrijgestelde gebruikers, wat betekent dat er geen UDF-uitvoering voor deze gebruikers is.
Wanneer de voorwaardelijke logica te complex is voor de principal-componenten van het beleid, zijn identiteitsfuncties binnen de UDF een mogelijk alternatief. Deze functies worden eenmaal opgelost tijdens de queryanalyse, niet per rij. Meerdere aanroepen naar identiteitsfuncties, zoals is_account_group_member() met verschillende groepsargumenten, resulteren in één UC API-aanroep, dus de invloed op de prestaties is doorgaans minimaal.
De volgende UDF is efficiënt omdat deze alleen afhankelijk is van identiteitsfuncties, die eenmaal worden opgelost tijdens de queryanalyse:
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;
De volgende UDF is daarentegen langzamer omdat deze bevoegdheden in een secundaire tabel codeert, waarvoor een extra opzoekactie voor tabellen is vereist:
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;
Deterministische, foutveilige expressies gebruiken
Gebruik deterministische expressies die geen fouten kunnen genereren in UDF's die voor beleid worden gebruikt en in query's voor beveiligde tabellen.
Niet-deterministische functies (functies die verschillende resultaten retourneren voor dezelfde invoer, zoals rand() of now()) verhinderen dat de optimizer resultaten in de cache opspoort of constant vouwen toepast. Zowel SQL als Python UDF's ondersteunen het trefwoord DETERMINISTIC in de instructie CREATE FUNCTION. Voor SQL UDF's leidt de optimizer automatisch determinisme af van de hoofdtekst van de functie, maar u kunt deze ook expliciet instellen. Voor Python UDF's kan de optimizer de hoofdtekst van de functie niet inspecteren, dus het expliciet markeren van een Python UDF als deterministisch is belangrijk om het opslaan van resultaten voor aanroepen met identieke argumenten mogelijk te maken.
Sommige expressies veroorzaken fouten als de invoer niet geldig is, zoals ANSI-deling waarbij de noemer nul is. Wanneer de SQL-compiler deze mogelijkheid detecteert, kunnen bewerkingen zoals filters niet naar beneden in het queryplan worden geduwd. Als u dit doet, kunnen er fouten optreden die informatie over waarden onthullen voordat filteren of maskeren van kracht wordt. Gebruik foutveilige alternatieven, zoals try_divide in plaats van /, try_cast in plaats van CAST, en try_to_number in plaats van to_number. Deze retourneert NULL bij falen in plaats van een uitzondering te genereren, waardoor de optimizer expressies optimaal kan herschikken en samenvoegen.
Avoid Python UDF's
Vermijd waar mogelijk Python UDF's in ABAC-beleid. Python UDF's moeten worden ingepakt in een SQL UDF om te worden gebruikt in beleidsregels. Ze zijn over het algemeen ook langzamer dan SQL UDFs, omdat de optimizer deze niet kan inline plaatsen of optimaliseren, en de Python-functie wordt uitgevoerd voor elke rij in de doeltabel.
Als een Python UDF onvermijdelijk is, raadpleegt u Deterministische, foutveilige expressies voor het markeren als DETERMINISTIC om resultaatcaching mogelijk te maken.
Opzoektabellen klein houden
Een veelvoorkomend patroon is het controleren van toegangsrechten voor een kleine opzoektabel (bijvoorbeeld een tabel die gebruikers toewijst aan toegestane prioriteitsniveaus). Als de opzoektabel aanzienlijk kleiner is dan de doeltabel, converteert de optimizer de subquery naar een broadcast-hash-join. De opzoektabel wordt gekopieerd naar elke uitvoerder en opgeslagen in het geheugen als een hashmap, waardoor u snel kunt filteren tijdens de tabelscan. Zie Opzoektabellen in UDF's voor ABAC-beleid voor een codevoorbeeld.
- Als de opzoektabel groot is, valt de optimizer terug op een shuffle join, wat dit proces langzamer maakt.
- Als het opzoekpredicaat complex is (geen eenvoudige gelijkheidscontrole), kan broadcast-join ook niet in aanmerking komen.
- Zelfs bij broadcast hash join zijn er voor elke rij nog steeds kosten verbonden aan het opzoeken van een hash-tabel tijdens de uitvoering.
Het begrijpen van predicaatpushdown bij beveiligde tabellen
Predicaat pushdown is een prestatieoptimalisatie waarbij de engine de filtervoorwaarden naar de opslaglaag verplaatst. Hierdoor kan de engine volledige partities van gegevens overslaan die niet overeenkomen met uw query, waardoor I/O aanzienlijk wordt verminderd en de uitvoering wordt versneld.
Voor tabellen die worden beveiligd door rijfilters en kolommaskers, is deze optimalisatie complexer. Dit is de meest voorkomende bron van prestatieproblemen met beveiligde tabellen en de moeilijkste om te verhelpen, omdat beleidsauteurs niet kunnen bepalen welke query's gebruikers uitvoeren op beveiligde tabellen.
Hoe de SecureView barrière invloed heeft op predicaatpushdown
Zowel ABAC - als rijfilters op tabelniveau en kolommaskers gebruiken een SecureView barrière om te voorkomen dat predicaten met bijwerkingen over de beleidsgrens worden geschoven. Dit beschermt tegen gegevenslekken aan de zijkant, maar kan ook partities verwijderen en optimalisaties voor liquide clustering blokkeren, waardoor volledige tabelscans kunnen worden afgedwongen. Dit geldt zelfs wanneer de beleid-UDF oplost naar een constante true, wat betekent dat er daardoor geen rijen daadwerkelijk worden gefilterd. De aanwezigheid van een beleid op een tafel introduceert de SecureView barrière.
Filters die worden beïnvloed door de barrière
Over het algemeen kan de optimizer alleen neveneffectvrije predicaten door de SecureView barrière pushen.
-
Omlaag gepusht (snel): Eenvoudige gelijkheidsvergelijkingen (
WHERE col = 'value') en basisbereikvergelijkingen (WHERE col > 100). Deze zijn vrij van bijwerkingen en lopen geen risico op het lekken van gegevens. -
Geblokkeerd (langzamer): Predicaten die functies aanroepen (
WHERE date_format(col, 'yyyy-MM-dd') = '1995-07-29') of impliciete typecasts introduceren. Deze worden boven deSecureViewbarrière bewaard, wat betekent dat de engine de tabel moet scannen voordat het filter wordt toegepast.
In het volgende voorbeeld ziet u het verschil. Overweeg een tabel met een partitiesleutel in o_orderdate en een query die filtert met behulp van date_format:
EXPLAIN SELECT * FROM orders
WHERE date_format(o_orderdate, 'yyyy-MM-dd') = '1995-07-29'
Zonder beleid verschijnt het date_format-predicaat in PartitionFilters binnen de PhotonScan-knooppunt, wat betekent dat partitie-uitsnijding actief is.
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(date_format(cast(o_orderdate as timestamp), yyyy-MM-dd, ...))]
Met een beleid (zelfs een beleid dat altijd true terugkeert) blokkeert de SecureView barrière het predicaat. Het wordt verplaatst naar een PhotonFilter boven de scan in plaats van in PartitionFilterste blijven, wat resulteert in een volledige tabelscan:
+- PhotonFilter (date_format(cast(o_orderdate as timestamp),
yyyy-MM-dd, ...) = 1995-07-29)
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate)]
Een eenvoudiger predicaat zoals WHERE o_orderdate = '1995-07-29' heeft geen bijwerkingen en kan nog steeds naar beneden worden geschoven, zelfs met de SecureView barrière:
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(o_orderdate = 1995-07-29)]
Gebruik indien mogelijk eenvoudige gelijkheidspredicaten voor beveiligde tabellen. Voor vrijgestelde gebruikers gebruikt u de EXCEPT component in het beleid om de SecureView barrière volledig te elimineren, waardoor volledige predicaatpushdown wordt hersteld.
Waar mogelijk kolommaskers opnieuw gebruiken
Als u veel afzonderlijke kolommaskers toepast op één tabel, verhoogt dit de kosten per kolom. Masker alleen kolommen die echt gevoelige gegevens bevatten.
Wanneer meerdere kolommen dezelfde transformatie vereisen (bijvoorbeeld redacteren of NULL vervangen door een vaste tekenreeks), gebruikt u dezelfde maskeringsfunctie in plaats van een afzonderlijke functie per kolom te maken.
Azure Databricks herkent beleidsregels die verwijzen naar dezelfde UDF met dezelfde argumenten als hetzelfde effectieve masker, zodat het hergebruik van functies onnodige overhead voorkomt.
Regex-maskering voorkomen voor grote tekstvelden
Het gebruik regexp_replace in een kolommasker om elementen in een geserialiseerd document (XML of JSON opgeslagen als een STRING-kolom) te redacteren, is duur.
regexp_replace doorloopt de volledige tekenreeks voor elke rij. De optimizer behandelt de kolom STRING als een ondoorzichtige waarde en kan ongebruikte gedeelten van het document niet verwijderen. De engine leest en herschrijft de hele nettolading, zelfs wanneer de query slechts enkele velden nodig heeft.
-- 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;
Materialiseer in plaats daarvan de gevoelige velden in getypte kolommen in een afzonderlijke tabel en pas vervolgens kolommaskers toe op die scalaire kolommen. De maskerfunctie werkt vervolgens op één kleine waarde per rij in plaats van het hele geserialiseerde document.
-- 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;
Als u de gegevens kunt opslaan als een structkolom in plaats van XML, gebruikt u het flexibele maskeringspatroon VARIANT om afzonderlijke velden in de struct te redacteren. Zie struct kolommen maskeren met VARIANT.
UDF-prestaties testen
Testen op schaal
Test UDF-prestaties op ten minste 1 miljoen rijen voordat u naar productie implementeert. Naast synthetische schaaltests voert u query's uit die de werkelijke workload vertegenwoordigen die u in de beveiligde tabel verwacht. Breng incrementele wijzigingen aan in uw beleidsfuncties en meet het effect van elke wijziging in plaats van alleen de uiteindelijke versie te testen.
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;
Vervang your_mask_function door de UDF die u aan het testen bent. Vergelijk resultaten met en zonder dat het beleid is toegepast om de overhead van het beleid te isoleren.