Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Zasady filtrowania wierszy i maski kolumn wprowadzają logikę uruchamianą w czasie wykonywania zapytań, więc wydajność zależy od sposobu projektowania zasad. Nie ma jednego odpowiedniego podejścia dla każdego obciążenia. Najlepsze podejście zależy od ilości danych, wzorców zapytań, sposobu interakcji użytkowników z chronionymi tabelami oraz żądanego zachowania maskowania lub filtrowania. W poniższych sekcjach opisano najczęstsze zagadnienia dotyczące wydajności. Użyj ich jako listy kontrolnej podczas projektowania zasad i przetestuj z reprezentatywnymi zapytaniami przed wdrożeniem w środowisku produkcyjnym.
Omówienie wydajności
| Kwestie wymagające rozważenia | Description |
|---|---|
| Zmniejszenie złożoności UDF | Złożona logika UDF może hamować wydajność zapytań; proste funkcje działają lepiej. |
| Podejście do określania docelowych uczestników | Zdecyduj, czy zaimplementować logikę opartą na zasadach w klauzulach zasad TO/EXCEPT, czy wewnątrz UDF przy użyciu funkcji tożsamości. |
| Używanie deterministycznych, bezpiecznych od błędów wyrażeń | Funkcje i wyrażenia niedeterministyczne, które mogą zgłaszać błędy, zmniejszają zdolność optymalizatora do buforowania wyników i zmiany kolejności operacji. |
| Avoid Python UDF | Używaj UDF-ów SQL zamiast UDF-ów Pythona zawsze, gdy jest to możliwe. |
| Trzymaj tablice przeszukiwania małe | Funkcje definiowane przez użytkownika (UDF), które odnoszą się do tabel zewnętrznych, działają najlepiej, gdy te tabele są wystarczająco małe, aby można je było rozpowszechniać. |
| Omówienie wypychania predykatu w tabelach chronionych | Zapytania dotyczące tabel chronionych mogą nie wykorzystywać oczyszczania partycji ani płynnego klastrowania, jeśli predykaty mają skutki uboczne. |
| Ponowne używanie masek kolumn tam, gdzie jest to możliwe | Każda odrębna maska w tabeli dodaje obciążenie; ponowne wykorzystanie tej samej funkcji między kolumnami może ją zmniejszyć. |
| Unikaj maskowania wyrażeń regularnych w dużych polach tekstowych | Maskowanie oparte na wyrażeniach regularnych na dokumentach zserializowanych zmusza silnik do skanowania i przekształcania całego ładunku dla każdego wiersza. |
Zmniejszanie złożoności UDF
Funkcja UDF w zasadach ABAC jest wykonywana dla każdego wiersza (filtrów wierszy) lub każdej zgodnej wartości kolumny (maski kolumn) podczas wykonywania zapytania. Złożoność funkcji zdefiniowanej przez użytkownika bezpośrednio wpływa na wydajność zapytań.
Do:
- Utrzymuj UDFy proste. Preferuj podstawowe
CASEinstrukcje i proste wyrażenia logiczne. - Odwołuj się tylko do kolumn tabeli docelowej w funkcjach zdefiniowanych przez użytkownika, jak najwięcej. Umożliwia to przesuwanie predykatu.
- Jeśli funkcja UDF musi odwoływać się do tabel zewnętrznych, zachowaj wszystkie odwołania zewnętrzne wystarczająco małe, aby rozgłasić. Upewnij się, że przywoływane tabele są zoptymalizowane i podzielone na partycje, aby odpowiadały wzorcowi dostępu określonemu w polityce. Na przykład podzielić tabelę wyszukiwania zasad według nazwy użytkownika.
- Unikaj zagnieżdżania wielopoziomowego i niepotrzebnych wywołań funkcji. Używaj wbudowanych funkcji SQL, jak najwięcej.
Uniknąć:
- Zewnętrzne wywołania API lub zapytania do innych baz danych w funkcjach zdefiniowanych przez użytkownika. Wywołania sieciowe mogą wprowadzać dodatkowe opóźnienia i przekroczenia czasowe.
- Złożone podzapytania lub łączenia z dużymi tabelami. Zapobiegają łączeniom skrótów rozgłoszeniowych i wymuszają łączenia pętli zagnieżdżonych.
- Intensywne użycie wyrażeń regularnych w dużych polach tekstowych. Zobacz Regex w dużych polach tekstowych.
- Wyszukiwanie metadanych w poszczególnych wierszach, na przykład wykonywanie zapytań.
information_schema
Podejście do określania/przygotowywania strategii dla głównych podmiotów
Podczas pisania zasad ABAC decydujesz, gdzie zaimplementować logikę opartą na podmiotach: w klauzulach zasad TO/EXCEPT, lub wewnątrz UDF (funkcji zdefiniowanej przez użytkownika) przy użyciu funkcji tożsamości, takich jak current_user() i is_account_group_member().
Ogólnie rzecz biorąc, użyj klauzul polityki TO/EXCEPT, aby określić, do których podmiotów zabezpieczeń odnosi się polityka. Dzięki temu definicja zasad jest prostsza, a funkcja UDF koncentruje się na przekształcaniu, filtrowaniu lub maskowaniu danych. Klauzula EXCEPT eliminuje politykę całkowicie dla użytkowników zwolnionych z niej, co oznacza brak wykonywania funkcji definiowanych przez użytkownika dla tych użytkowników.
Jeśli logika warunkowa jest zbyt złożona dla klauzul głównych zasad, funkcje tożsamości wewnątrz funkcji zdefiniowanej przez użytkownika są możliwe alternatywnie. Te funkcje są rozwiązywane raz podczas analizy zapytań, a nie na wiersz. Wiele wywołań funkcji tożsamości, takich jak is_account_group_member() w przypadku różnych argumentów grupy, powoduje pojedyncze wywołanie interfejsu API UC, więc wpływ na wydajność jest zwykle minimalny.
Następująca funkcja UDF jest wydajna, ponieważ opiera się tylko na funkcjach tożsamości, które są rozwiązywane raz podczas analizy zapytań:
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;
Natomiast następująca funkcja zdefiniowana przez użytkownika jest wolniejsza, ponieważ koduje uprawnienia w tabeli pomocniczej, co wymaga dodatkowego wyszukiwania tabeli.
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;
Używanie deterministycznych, bezpiecznych od błędów wyrażeń
Używaj wyrażeń deterministycznych, które nie mogą zgłaszać błędów w funkcjach zdefiniowanych przez użytkownika w ramach zasad oraz w zapytaniach dotyczących chronionych tabel.
Funkcje niedeterministyczne (funkcje, które zwracają różne wyniki dla tych samych danych wejściowych, takich jak rand() lub now()), uniemożliwiają optymalizatorowi buforowanie wyników lub stosowanie stałego składania. Funkcje UDF SQL i Python obsługują słowo kluczowe DETERMINISTIC w instrukcji CREATE FUNCTION. W przypadku funkcji zdefiniowanych przez użytkownika SQL optymalizator automatycznie uzyskuje determinizm z treści funkcji, ale można go również jawnie ustawić. W przypadku funkcji UDF w Pythonie optymalizator nie może sprawdzić treści funkcji, dlatego jawne oznaczenie funkcji UDF w Pythonie jako deterministycznej jest ważne, aby umożliwić buforowanie wyników dla wywołań z identycznymi argumentami.
Niektóre wyrażenia zgłaszają błędy, jeśli dane wejściowe nie są prawidłowe, takie jak podział ANSI w mianowniku zerowym. Gdy kompilator SQL wykryje tę możliwość, nie może przesuwać operacji, takich jak filtry, do planu zapytania. Może to spowodować błędy, które ujawniają informacje o wartościach przed zastosowaniem filtrowania lub maskowania. Użyj bezpiecznych od błędów alternatyw, takich jak try_divide zamiast /, try_cast zamiast CAST, i try_to_number zamiast to_number.
NULL Powrót po awarii zamiast rzutowania, co pozwala optymalizatorowi swobodnie rozmieszczać i składać wyrażenia.
Unikaj funkcji UDF w języku Python
Unikaj funkcji użytkownika w Pythonie w zasadach opartych na atrybutach zawsze, gdy jest to możliwe. Python funkcje zdefiniowane przez użytkownika muszą być zawijane w SQL UDF do użycia w zasadach. Są one ogólnie również wolniejsze niż funkcje zdefiniowane przez użytkownika SQL, ponieważ optymalizator nie może ich przetworzyć w linii ani zoptymalizować, a funkcja Python jest wykonywana dla każdego wiersza w tabeli docelowej.
Jeśli nie da się uniknąć użycia funkcji zdefiniowanej przez użytkownika w Pythonie, zobacz Deterministic, error-safe expressions, aby dowiedzieć się, jak ją oznaczyć jako DETERMINISTIC, co umożliwi buforowanie wyników.
Zachowaj małe tablice wyszukiwania
Typowym wzorcem jest sprawdzenie praw dostępu w oparciu o małą tabelę pomocniczą (na przykład tabelę, która mapuje użytkowników na dozwolone poziomy priorytetów). Jeśli tabela odnośników jest znacznie mniejsza niż tabela docelowa, optymalizator konwertuje podzapytywanie na sprzężenia skrótu emisji. Tabela wyszukiwania jest kopiowana do każdego wykonawcy i przechowywana w pamięci jako mapa skrótu, która umożliwia szybkie filtrowanie podczas skanowania tabeli. Aby zapoznać się z przykładem kodu, zapoznaj się z Tablicami wyszukiwania w funkcjach zdefiniowanych przez użytkownika zasad ABAC.
- Jeśli tabela przeszukiwania jest duża, optymalizator wraca do sprzężenia shuffle, co jest wolniejsze.
- Jeśli predykat odnośnika jest złożony (a nie prosty sprawdzanie równości), sprzężenia emisji mogą również stać się niekwalifikowane.
- Nawet w przypadku sprzężenia skrótu emisji każdy wiersz nadal wiąże się z kosztem wyszukiwania tabeli skrótów podczas wykonywania.
Omówienie wypychania predykatu w tabelach chronionych
Wypychanie predykatu to optymalizacja wydajności, w której silnik przesuwa warunki filtrowania na warstwę magazynowania. Dzięki temu aparat może pominąć całe partycje danych, które nie są zgodne z zapytaniem, co znacznie zmniejsza liczbę operacji we/wy i przyspiesza wykonywanie.
W przypadku tabel chronionych przez filtry wierszy i maski kolumn ta optymalizacja jest bardziej złożona. Jest to najczęstsze źródło problemów z wydajnością chronionych tabel i najtrudniejsze do rozwiązania, ponieważ autorzy zasad nie mogą kontrolować, jakie zapytania są uruchamiane przez użytkowników przed chronionymi tabelami.
Jak bariera SecureView wpływa na optymalizację przetwarzania predykatów
Zarówno ABAC, jak i filtry wierszy na poziomie tabeli oraz maski kolumn używają SecureView bariery, aby zapobiec przenikaniu predykatów o skutkach ubocznych przez granice zasad. Chroni to przed wyciekiem danych poprzez kanały boczne, ale może również blokować optymalizacje oczyszczania partycji i klastrowania, co może wymusić skanowanie pełnej tabeli. Ma to zastosowanie nawet wtedy, gdy funkcja UDF polityki rozwiązuje się jako stała true (co oznacza, że wiersze nie są faktycznie filtrowane). Obecność polityki w tabeli wprowadza barierę SecureView .
Filtry dotknięte barierą
Ogólnie rzecz biorąc, optymalizator może przepuszczać tylko predykaty bez skutków ubocznych przez barierę SecureView.
-
Przesunięcie w dół (szybkie): proste porównania równości (
WHERE col = 'value') i podstawowe porównania zakresu (WHERE col > 100). Są one wolne od skutków ubocznych i nie ryzykują wycieku danych. -
Zablokowane (wolniejsze): Predykaty wywołujące funkcje (
WHERE date_format(col, 'yyyy-MM-dd') = '1995-07-29') lub wprowadzające rzutowania typu niejawnego. Są one przechowywane powyżej barierySecureView, co oznacza, że silnik musi przeskanować tabelę przed zastosowaniem filtru.
W poniższym przykładzie przedstawiono różnicę. Rozważ tabelę z kluczem partycji na o_orderdate i zapytaniem filtrującym przy użyciu date_format:
EXPLAIN SELECT * FROM orders
WHERE date_format(o_orderdate, 'yyyy-MM-dd') = '1995-07-29'
Bez zasad predykat pojawia się w date_format wewnątrz węzła PartitionFilters, co oznacza, że PhotonScan oczyszczanie partycji jest aktywne:
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(date_format(cast(o_orderdate as timestamp), yyyy-MM-dd, ...))]
W przypadku zasady (nawet takiej, która zawsze zwraca true), bariera SecureView blokuje predykat. Przechodzi on do powyższego PhotonFilter skanowania zamiast pozostawać w PartitionFiltersobiekcie , co powoduje pełne skanowanie tabeli:
+- PhotonFilter (date_format(cast(o_orderdate as timestamp),
yyyy-MM-dd, ...) = 1995-07-29)
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate)]
Prostszy predykat jak WHERE o_orderdate = '1995-07-29' nie ma skutków ubocznych i nadal można go przenieść pomimo bariery SecureView.
+- PhotonSecureView orders
+- PhotonScan parquet orders[...]
PartitionFilters: [isnotnull(o_orderdate),
(o_orderdate = 1995-07-29)]
Używaj prostych predykatów równości w tabelach chronionych, jeśli jest to możliwe. W przypadku wykluczonych użytkowników należy użyć EXCEPT klauzuli w zasadach, aby całkowicie wyeliminować barierę SecureView , która przywraca pełne wypychanie predykatu.
Ponowne używanie masek kolumn tam, gdzie jest to możliwe
Zastosowanie wielu odrębnych masek kolumn do pojedynczej tabeli zwiększa koszt przypadający na każdą kolumnę. Maskuj tylko kolumny zawierające naprawdę poufne dane.
Jeśli wiele kolumn wymaga tej samej transformacji (na przykład zredagowanie do NULL lub zastąpienie ciągiem stałym), użyj ponownie tej samej funkcji maskowania, zamiast tworzenia oddzielnej funkcji dla każdej kolumny.
Azure Databricks rozpoznaje zasady, które odwołują się do tego samego UDF z tymi samymi argumentami jako tę samą efektywną maskę, dlatego ponowne użycie UDF pozwala uniknąć niepotrzebnego obciążenia.
Unikaj maskowania wyrażeń regularnych w dużych polach tekstowych
Używanie maski kolumny regexp_replace w celu ukrywania elementów w dokumencie serializowanym (XML lub JSON przechowywanym jako kolumna STRING) jest kosztowne.
regexp_replace Przechodzi przez pełny ciąg znaków dla każdego wiersza. Optymalizator traktuje kolumnę STRING jako nieprzezroczystą wartość i nie może przycinać nieużywanych fragmentów dokumentu. Aparat odczytuje i ponownie zapisuje cały ładunek, nawet jeśli zapytanie wymaga tylko kilku pól.
-- 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;
Zamiast tego zmaterializuj poufne pola w kolumnach wpisanych w oddzielnej tabeli, a następnie zastosuj maski kolumn do tych kolumn skalarnych. Następnie funkcja maski działa na pojedynczej małej wartości na wiersz, a nie na całym serializowanym dokumencie.
-- 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;
Jeśli dane można przechowywać jako kolumnę struktury zamiast XML, użyj elastycznego wzorca maskowania VARIANT, aby zredagować poszczególne pola w strukturze. Zobacz Maskuj kolumny struktury przy użyciu VARIANT.
Test wydajności UDF
Testowanie na dużą skalę
Przetestuj wydajność funkcji zdefiniowanej przez użytkownika na co najmniej 1 milionach wierszy przed wdrożeniem w środowisku produkcyjnym. Oprócz syntetycznych testów skalowania uruchom zapytania reprezentujące rzeczywiste obciążenie oczekiwane w chronionej tabeli. Wprowadź przyrostowe zmiany w funkcjach zasad i zmierz efekt każdej zmiany, zamiast testować tylko ostateczną wersję.
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;
Zastąp your_mask_function UDF, który testujesz. Porównaj wyniki z zastosowaną polityką i bez niej, aby odizolować narzut polityki.