Uwaga
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.
Oprócz semantycznego projektowania modelu i złożoności zapytań wydajność usługi Direct Lake zależy w szczególności od dobrze dostrojonych tabel delty w celu wydajnego i szybkiego ładowania kolumn (transkodowania) i optymalnego wykonywania zapytań. Upewnij się, że zastosowano optymalizację V-Order. Ponadto należy zachować małą liczbę plików Parquet, używać dużych grup wierszy i dążyć do zminimalizowania wpływu aktualizacji danych w dzienniku Delta. Są to typowe najlepsze praktyki, które mogą pomóc w zapewnieniu szybkiego wykonywania zapytań w zimnych, półciepłych, ciepłych i gorących stanach trybu Direct Lake.
W tym artykule wyjaśniono, jak wydajność usługi Direct Lake zależy od kondycji tabeli delty i wydajnych aktualizacji danych. Zrozumienie tych zależności ma kluczowe znaczenie. Dowiesz się, że układ danych w plikach Parquet jest równie ważny dla wydajności zapytań, jak to dobry semantyczny projekt modelu i dobrze dostrojone miary wyrażeń analizy danych (DAX).
Co musisz wiedzieć
W tym artykule założono, że znasz już następujące pojęcia:
- Tabele Delta w usłudze OneLake: szczegółowe informacje o tabelach Delta w usłudze OneLake są dostępne w dokumentacji podstaw usługi Microsoft Fabric.
- Pliki Parquet, grupy wierszy i dziennik delty: format pliku Parquet, w tym sposób organizowania danych w grupach wierszy i fragmentach kolumn oraz sposób obsługi metadanych, jest objaśniony w dokumentacji formatu pliku Parquet. Zapoznaj się również z dokumentacją protokołu dziennika transakcji usługi Delta.
- Optymalizacja tabel Delta i V-Order: zapoznaj się z dokumentacją usługi Fabric Lakehouse dotyczącą konserwacji tabel Delta, dla takich jak optymalizacja tabel Delta Lake i V-Order.
- Strukturyzacja i transkodowanie: Odświeżanie modelu semantycznego Direct Lake (strukturyzacja) i ładowanie danych kolumnowych na żądanie (transkodowanie) to ważne pojęcia, omówione na poziomie wprowadzającym w Direct Lake overview article.
- Silnik Formuł i Silnik Przechowywania: Po wykonaniu zapytania języka DAX, Silnik Formuł generuje plan zapytania i uzyskuje niezbędne dane oraz początkowe agregacje z Silnika Przechowywania. Optymalizacje omówione w tym artykule koncentrują się na silniku magazynowania. Aby dowiedzieć się więcej na temat silnika formuł i silnika pamięci, zapoznaj się z dokumentacją dewelopera usług Analysis Services.
- VertiPaq i VertiScan: w trybie importu i w trybie Direct Lake silnik magazynujący używa silnika VertiPaq do utrzymania kolumnowego magazynu w pamięci. VertiScan umożliwia silnikowi formuł interakcję z VertiPaq. Aby uzyskać więcej informacji, zobacz dokumentację dla deweloperów usług Analysis Services.
- Kodowanie słownika: Zarówno pliki Parquet, jak i VertiPaq używają kodowania słownika, czyli techniki kompresji danych stosowanej do poszczególnych kolumn różnych typów danych, takich jak int, long, date i char. Działa przez przechowywanie każdej unikatowej wartości kolumny w pamięci jako liczby całkowitej przy użyciu kodowania długości przebiegu (RLE) / kodowania hybrydowego (Bit-Packing). VertiPaq zawsze używa kodowania słownika, ale Parquet może przełączyć się na zwykłe kodowanie lub kodowanie różnicowe w pewnych okolicznościach, zgodnie z wyjaśnieniem w dokumentacji Parquet File-Format, co wymagałoby, aby usługa Direct Lake ponownie zakodowała dane, wpływając odpowiednio na wydajność transkodowania.
- Fragmenty kolumn i segmenty kolumn: Odnoszą się do sposobu, w jaki Parquet i VertiPaq przechowują dane kolumn, w celu wydajnego pobierania danych. Każda kolumna w tabeli jest podzielona na mniejsze fragmenty, które mogą być przetwarzane i kompresowane niezależnie. VertiPaq nazywa te fragmenty segmentami. Zestaw wierszy schematu DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS służy do pobierania informacji o segmentach kolumn w modelu semantycznym Direct Lake.
- Python i Jupyter Notebooks: Jupyter Notebooks zapewniają interaktywne środowisko do pisania i uruchamiania kodu Pythona. Podstawowa wiedza na temat języka Python jest przydatna, jeśli chcesz postępować zgodnie z fragmentami kodu w dalszej części tego rozdziału. Aby uzyskać więcej informacji, zobacz dokumentację języka Python. Aby uzyskać informacje na temat korzystania z notesów w usłudze Microsoft Fabric, zobacz Jak używać notesów — Microsoft Fabric.
Co wpływa na wydajność zapytań w usłudze Direct Lake
W tej sekcji podsumowano główne czynniki wpływające na wydajność usługi Direct Lake. Kolejne sekcje zawierają bardziej szczegółowe wyjaśnienia:
- Kompresja V-Order: skuteczność kompresji może mieć wpływ na wydajność zapytań, ponieważ lepsza kompresja prowadzi do szybszego ładowania danych i bardziej wydajnego przetwarzania zapytań. Ładowanie danych jest szybkie, ponieważ skompresowane dane przesyłane strumieniowo zwiększają wydajność transkodowania. Wydajność zapytań jest również optymalna, ponieważ kompresja V-Order umożliwia programowi VertiScan obliczenie wyników bezpośrednio na skompresowanych danych, pomijając krok dekompresji.
- Typy danych: użycie odpowiednich typów danych dla kolumn może poprawić kompresję i wydajność. Na przykład użyj typów danych całkowitych zamiast ciągów, jeśli to możliwe, i unikaj przechowywania liczb całkowitych jako ciągów.
- Rozmiar i liczba segmentów: VertiPaq przechowuje dane kolumn w segmentach. Duża liczba mniejszych segmentów może negatywnie wpłynąć na wydajność zapytań. W przypadku dużych tabel usługa Direct Lake preferuje duże rozmiary segmentów, takie jak od 1 miliona do 16 milionów wierszy.
- Kardynalność kolumn: Kolumny o wysokiej kardynalności (kolumny z wieloma unikatowymi wartościami) mogą spowolnić wydajność. Zmniejszanie liczebności tam, gdzie to możliwe, może pomóc.
- Indeksy i agregacje: kolumny z niższą kardynalnością korzystają z kodowania słownika, co może przyspieszyć wykonywanie zapytań, zmniejszając ilość danych, które należy skanować.
- Tryb awaryjny DirectQuery: operacje trybu awaryjnego mogą spowodować spowolnienie wydajności zapytań, ponieważ dane muszą być teraz pobierane z punktu końcowego analizy SQL źródła danych Fabric. Co więcej, mechanizm awaryjny opiera się na hybrydowych planach zapytań, aby wspierać zarówno DirectQuery, jak i VertiScan, kosztem wydajności, nawet gdy Direct Lake nie musi przełączać się na mechanizm awaryjny. Jeśli to możliwe, wyłącz awaryjny mechanizm DirectQuery, aby uniknąć planów zapytań hybrydowych.
-
Stopień rezydencji pamięci: modele semantyczne typu Direct Lake mogą być w stanie zimnym, półciepłym, ciepłym lub gorącym, z coraz większą wydajnością od zimnego do gorącego. Szybkie przejście z zimna do ciepła jest kluczem do dobrej wydajności Direct Lake.
- Zimno: sklep VertiPaq jest pusty. Wszystkie dane wymagane do udzielenia odpowiedzi na zapytanie języka DAX muszą zostać załadowane z tabel delty.
- Semiwarm: usługa Direct Lake pomija tylko te segmenty kolumn podczas ramowania należące do usuniętych grup wierszy. Oznacza to, że należy załadować tylko zaktualizowane lub nowo dodane dane. Model semantyczny Direct Lake może również przechodzić w stan półciepły w wyniku presji pamięci, gdy musi zwolnić segmenty i połączyć indeksy.
- Ciepło: dane kolumny wymagane do udzielenia odpowiedzi na zapytanie języka DAX są już w pełni załadowane do pamięci.
- Gorąca: dane kolumny są już w pełni załadowane do pamięci, cache VertiScan są wypełniane, a zapytania DAX trafiają do cache'ów.
- Wykorzystanie pamięci: usługa Direct Lake musi załadować wszystkie dane kolumn wymagane do udzielenia odpowiedzi na zapytanie języka DAX do pamięci, co może wyczerpać dostępne zasoby pamięci. W przypadku niewystarczającej ilości pamięci usługa Direct Lake musi zwolnić wcześniej załadowane dane kolumn, które usługa Direct Lake może następnie ponownie załadować dla kolejnych zapytań języka DAX. Odpowiednie ustalanie rozmiaru modeli semantycznych usługi Direct Lake może pomóc uniknąć częstego ponownego ładowania.
Wydajność przechowywania pamięci i zapytań
Direct Lake działa najlepiej w stanie ciepłym lub gorącym, podczas gdy w stanie zimnym działa wolniej. Direct Lake unika powrotu do zimnego, jak najwięcej dzięki użyciu przyrostowych ramek.
Bootstrap
Po początkowym załadowaniu modelu semantycznego żadne dane kolumn nie są jeszcze w pamięci. Direct Lake jest zimny. Gdy klient przesyła zapytanie języka DAX do modelu semantycznego usługi Direct Lake w stanie zimnym, usługa Direct Lake musi wykonać następujące główne zadania, aby można było przetworzyć i odpowiedzieć na zapytanie języka DAX:
Transkodowanie słownika VertiPaq. Usługa Direct Lake musi scalić lokalne słowniki Parquet dla każdego fragmentu kolumny, aby utworzyć globalny słownik VertiPaq dla kolumny. Ta operacja scalająca wpływa na czas odpowiedzi na zapytanie.
Ładowanie fragmentów kolumn Parquet do segmentów kolumn. W większości przypadków jest to bezpośrednie ponowne mapowanie identyfikatorów danych Parquet na identyfikatory VertiPaq, gdy obie strony mogą używać kodowania RLE/Bit-Packing Hybrid. Jeśli słowniki Parquet używają zwykłego kodowania, VertiPaq musi przekonwertować wartości na RLE/Bit-Packing kodowanie hybrydowe, co trwa dłużej.
- Wydajność usługi Direct Lake jest optymalna w przypadku plików Parquet uporządkowanych według metody V-Ordering, ponieważ zwiększa to jakość kompresji RLE. Usługa Direct Lake może ładować gęsto upakowane dane V-Ordered szybciej niż mniej skompresowane dane.
Generowanie indeksów sprzężenia. Jeśli zapytanie języka DAX uzyskuje dostęp do kolumn z wielu tabel, usługa Direct Lake musi skompilować indeksy sprzężenia zgodnie z relacjami tabeli, aby program VertiScan mógł poprawnie połączyć tabele. Aby skompilować indeksy sprzężenia, usługa Direct Lake musi załadować słowniki kolumn kluczy uczestniczących w relacji oraz segmenty kolumny klucza podstawowego (kolumnę po jednej stronie relacji tabeli).
Stosowanie wektorów usuwania Delta. Jeśli źródłowa tabela delty używa wektorów usuwania, usługa Direct Lake musi załadować te wektory usuwania, aby upewnić się, że usunięte dane są wykluczone z przetwarzania zapytań.
Notatka
Zimny stan można również wywołać przez wysłanie polecenia
processClear
, a następnie poleceniaprocessFull
XMLA do modelu. PolecenieProcessClear
usuwa wszystkie dane i skojarzenie z wersją tabeli delta w ramce. PolecenieProcessFull
XMLA wykonuje przygotowanie ram w celu powiązania modelu z najnowszą dostępną wersją zatwierdzoną Delta.
Ramowanie przyrostowe
Podczas ramowania usługa Direct Lake analizuje dziennik Delta każdej tabeli Delty i usuwa załadowane segmenty kolumn oraz indeksy sprzężeń tylko wtedy, gdy źródłowe dane uległy zmianie. Słowniki są zachowywane, aby uniknąć niepotrzebnego transkodowania, a nowe wartości są po prostu dodawane do istniejących słowników. To incrementalne podejście do tworzenia ramek zmniejsza obciążenie ponownego ładowania i przynosi korzyści dla wydajności zimnych zapytań.
Efektywność ramek przyrostowych można analizować przy użyciu funkcji języka DAX, która zawija zestaw wierszy schematu. Wykonaj następujące kroki, aby zapewnić znaczące wyniki:
- Wykonaj zapytanie względem modelu semantycznego usługi Direct Lake, aby upewnić się, że jest w stanie ciepłym lub gorącym.
- Zaktualizuj tabelę Delta, którą chcesz zbadać, i odśwież model semantyczny Direct Lake, aby wykonać operację framowania.
- Uruchom zapytanie DAX, aby pobrać informacje o segmencie kolumn, wykorzystując funkcję
INFO.STORAGETABLECOLUMNSEGMENTS()
, jak pokazano na poniższym zrzucie ekranu. Zrzut ekranu używa małej przykładowej tabeli do celów ilustracyjnych. Każda kolumna ma tylko jeden segment. Segmenty nie rezydują w pamięci. Oznacza to prawdziwy stan zimny. Jeśli model był ciepły przed utworzeniem ramek, oznacza to, że tabela Delta została zaktualizowana przy użyciu destrukcyjnego wzorca ładowania danych, co uniemożliwia użycie przyrostowego tworzenia ramek. Wzorce aktualizacji tabeli delty zostały omówione w dalszej części tego artykułu.
Notatka
Gdy tabela delty nie odbiera aktualizacji, nie jest konieczne ponowne ładowanie kolumn już w pamięci. W przypadku stosowania niedestrukcyjnych wzorców aktualizacji zapytania wykazują znacznie mniejszy wpływ na wydajność po procesie tworzenia ramek, ponieważ inkrementalne tworzenie ram w zasadzie umożliwia usłudze Direct Lake aktualizację znacznych części istniejących danych w pamięci.
Pełna rezydencja w pamięci
W momencie gdy słowniki, segmenty kolumn i indeksy sprzężeń są załadowane, usługa Direct Lake osiąga stan ciepłego startu z wydajnością zapytań dorównującą trybowi importu. W obu trybach liczba i rozmiar segmentów kolumn odgrywają kluczową rolę w optymalizacji wydajności zapytań.
Różnice w tabeli Delta
Pliki Parquet organizują dane według kolumn zamiast wierszy. Usługa Direct Lake organizuje również dane według kolumn. Dopasowanie ułatwia bezproblemową integrację, ale istnieją ważne różnice, w szczególności dotyczące grup wierszy i słowników.
Grupy wierszy a segmenty kolumn
Grupa wierszy w pliku Parquet składa się z fragmentów kolumn, a każdy fragment zawiera dane dla określonej kolumny. Segment kolumny w modelu semantycznym, z drugiej strony, zawiera również fragment danych kolumny.
Istnieje bezpośrednia relacja między całkowitą liczbą grup wierszy tabeli delty a liczbą segmentów dla każdej kolumny odpowiadającej tabeli modelu semantycznego. Jeśli na przykład tabela delty we wszystkich bieżących plikach Parquet zawiera trzy grupy wierszy w sumie, odpowiednia tabela modelu semantycznego ma trzy segmenty na kolumnę, jak pokazano na poniższym diagramie. Innymi słowy, jeśli tabela delty ma dużą liczbę małych grup wierszy, odpowiednia tabela modelu semantycznego również będzie miała dużą liczbę małych segmentów kolumn. Negatywnie wpłynie to na wydajność zapytań.
Notatka
Ponieważ usługa Direct Lake preferuje duże segmenty kolumn, grupy wierszy źródłowych tabel Delta powinny być jak największe.
Słowniki lokalne a słowniki globalne
Łączna liczba grup wierszy tabeli delty ma również bezpośredni wpływ na wydajność transkodowania słownika, ponieważ pliki Parquet używają lokalnych słowników, podczas gdy modele semantyczne usługi Direct Lake używają słownika globalnego dla każdej kolumny, jak pokazano na poniższym diagramie. Im większa liczba grup wierszy, tym większa liczba lokalnych słowników, które usługa Direct Lake musi scalić w celu utworzenia słownika globalnego, i tym dłużej trwa transkodowanie.
Wzorce aktualizacji tabeli delty
Metoda używana do pozyskiwania danych do tabeli Delta może znacznie wpływać na wydajność przyrostowego ramowania. Na przykład użycie opcji Zastąp podczas ładowania danych do istniejącej tabeli powoduje usunięcie Delta log przy każdym ładowaniu. Oznacza to, że Direct Lake nie może używać ramowania przyrostowego i musi ponownie załadować wszystkie dane, słowniki i indeksy sprzężenia. Takie destruktywne wzorce aktualizacji negatywnie wpływają na wydajność zapytań.
W tej sekcji opisano wzorce aktualizacji tabel Delta, które umożliwiają usłudze Direct Lake użycie ramek przyrostowych, zachowując elementy magazynu kolumn VertiPaq, takie jak słowniki, segmenty kolumn i indeksy sprzężenia, w celu zmaksymalizowania wydajności transkodowania i zwiększenia wydajności zimnych zapytań.
Przetwarzanie wsadowe bez partycjonowania
Ten wzorzec aktualizacji zbiera i przetwarza dane w dużych partiach w zaplanowanych odstępach czasu, na przykład co tydzień lub co miesiąc. Po nadejściu nowych danych stare dane są często usuwane w sposób kroczący lub przesuwany, aby zachować rozmiar tabeli pod kontrolą. Jednak usunięcie starych danych może stanowić wyzwanie, jeśli dane są rozłożone na większość plików Parquet. Na przykład usunięcie jednego dnia z 30 dni może mieć wpływ na 95% plików Parquet zamiast 5%. W tym przypadku usługa Direct Lake musiałaby ponownie wczytać 95% danych nawet przy relatywnie małej operacji usuwania. Ten sam problem dotyczy również aktualizacji istniejących wierszy, ponieważ aktualizacje są połączeniem usunięć i dodawania. Efekt operacji usuwania i aktualizacji można analizować przy użyciu narzędzia Delta Analyzer, jak wyjaśniono w dalszej części tego artykułu.
Przetwarzanie wsadowe z partycjonowaniem
Partycjonowanie tabeli Delta może pomóc zmniejszyć wpływ operacji usuwania, ponieważ tabela jest podzielona na mniejsze pliki Parquet przechowywane w folderach na podstawie unikalnych wartości w kolumnie partycji. Często używane kolumny partycji obejmują datę, region lub inne kategorie wymiarowe. W poprzednim przykładzie usuwania jednego dnia z 30 dni, tabela Delta partycjonowana według daty ograniczy usunięcia tylko do plików Parquet partycji dotyczącej tego dnia. Należy jednak pamiętać, że rozbudowane partycjonowanie może spowodować znaczne zwiększenie liczby plików Parquet i grup wierszy, co powoduje nadmierny wzrost segmentów kolumn w modelu semantycznym usługi Direct Lake, co negatywnie wpływa na wydajność zapytań. Wybór kolumny partycji o niskiej kardynalności ma kluczowe znaczenie dla wydajności zapytań. Najlepszym rozwiązaniem jest, aby kolumna miała mniej niż 100–200 odrębnych wartości.
Ładowanie przyrostowe
W przypadku ładowania przyrostowego proces aktualizacji wstawia tylko nowe dane do tabeli delty bez wpływu na istniejące pliki Parquet i grupy wierszy. Nie ma żadnych usuniętych elementów. Usługa Direct Lake może ładować nowe dane przyrostowo bez konieczności odrzucania i ponownego ładowania istniejących elementów magazynu kolumn VertiPaq. Ta metoda dobrze sprawdza się w przypadku tworzenia przyrostowych ramek w usłudze Direct Lake. Partycjonowanie tabeli Delta nie jest konieczne.
Przetwarzanie strumieniowe
Przetwarzanie danych niemal w czasie rzeczywistym, w miarę ich nadejścia, może spowodować rozprzestrzenianie się małych plików Parquet i grup wierszy, co może negatywnie wpłynąć na wydajność usługi Direct Lake. Podobnie jak w przypadku wzorca ładowania przyrostowego, nie jest konieczne partycjonowanie tabeli delty. Jednak częste utrzymanie tabeli jest niezbędne, aby zapewnić, że liczba plików Parquet i grup wierszy pozostaje w ustalonych limitach określonych w artykule Omówienie usługi Direct Lake. Innymi słowy, nie zapomnij regularnie uruchamiać optymalizacji platformy Spark, na przykład codziennie lub częściej. Optymalizacja platformy Spark zostanie ponownie omówiona w następnej sekcji.
Notatka
Rzeczywista analiza w czasie rzeczywistym jest najlepiej zaimplementowana przy użyciu strumieni zdarzeń, baz danych KQL i Eventhouse. Aby uzyskać wskazówki, zapoznaj się z dokumentacją analizyReal-Time w usłudze Microsoft Fabric .
Konserwacja tabeli delty
Kluczowe zadania konserwacji obejmują opróżnianie i optymalizowanie tabel delty. Aby zautomatyzować operacje konserwacji, możesz użyć interfejsów API usługi Lakehouse zgodnie z opisem w dokumentacji zarządzanie usługą Lakehouse za pomocą interfejsu API REST usługi Microsoft Fabric .
Odkurzanie
Oczyszczanie usuwa pliki Parquet, które nie są już uwzględniane w bieżącej wersji zatwierdzenia Delta Lake i które są starsze niż ustawiony próg przechowywania. Usunięcie tych plików Parquet nie ma wpływu na wydajność usługi Direct Lake, ponieważ usługa Direct Lake ładuje tylko pliki Parquet, które znajdują się w bieżącej wersji zatwierdzenia. W przypadku codziennego uruchamiania funkcji VACUUM z wartościami domyślnymi wersje zatwierdzenia funkcji Delta z ostatnich siedmiu dni są zachowywane na potrzeby podróży czasowej.
Ważny
Ze względu na to, że model semantyczny Direct Lake odwołuje się do określonej wersji zatwierdzenia Delta, należy upewnić się, że tabela Delta przechowuje tę wersję do momentu ponownego odświeżenia modelu, aby zaktualizować go do bieżącej wersji. W przeciwnym razie użytkownicy napotykają błędy zapytań, gdy model semantyczny usługi Direct Lake próbuje uzyskać dostęp do plików Parquet, które już nie istnieją.
Optymalizacja platformy Spark
Optymalizacja tabeli Delta scala wiele małych plików Parquet w mniej dużych plików. Ponieważ może to mieć wpływ na wydajność usługi Cold Direct Lake, dobrym rozwiązaniem jest częste optymalizowanie, na przykład w weekendy lub na koniec miesiąca. Optymalizuj częściej, jeśli małe pliki Parquet szybko się gromadzą (częste niewielkie aktualizacje), aby zapewnić, że tabela Delta pozostanie w dopuszczalnych granicach.
Partycjonowanie może pomóc zminimalizować efekt optymalizacji na przyrostowe struktury, ponieważ partycjonowanie skutecznie grupuje dane. Na przykład partycjonowanie dużej tabeli delty na podstawie kolumny date_key o niskiej kardynalności ogranicza cotygodniową konserwację do maksymalnie siedmiu partycji. Tabela Delta zachowałaby większość istniejących plików Parquet. Usługa Direct Lake musiałaby ponownie załadować tylko siedem dni danych.
Analizowanie aktualizacji tabeli delty
Użyj narzędzia Delta Analyzer lub podobnych narzędzi, aby dowiedzieć się, jak aktualizacje tabeli delty wpływają na pliki Parquet i grupy wierszy. Narzędzie Delta Analyzer umożliwia śledzenie ewolucji plików Parquet, grup wierszy, fragmentów kolumn i kolumn w odpowiedzi na operacje dołączania, aktualizowania i usuwania . Narzędzie Delta Analyzer jest dostępne jako autonomiczny notes Jupyter Notebook. Jest ona również dostępna w bibliotece semantic-link-labs. W poniższych sekcjach używane są semantic-link-labs. Tę bibliotekę można łatwo zainstalować w notebooku za pomocą polecenia %pip install semantic-link-labs
.
Rozmiar grupy wierszy
Idealny rozmiar grupy wierszy dla modeli semantycznych Direct Lake wynosi od 1 miliona do 16 milionów wierszy, ale Fabric może używać większych rozmiarów grup wierszy dla dużych tabel, jeśli dane są podatne na kompresję. Ogólnie rzecz biorąc, nie zalecamy zmiany domyślnego rozmiaru grupy wierszy. Najlepiej zezwolić, aby Fabric zarządzał układem tabeli Delta. Ale to również dobry pomysł, aby dokładnie sprawdzić.
Poniższy kod Python może służyć jako punkt wyjścia do analizowania rozmiarów grup wierszy i innych szczegółów tabeli Delta w magazynie danych połączonym z notesem w środowisku Fabric. W poniższej tabeli przedstawiono dane wyjściowe przykładowej tabeli z 1 miliardami wierszy.
import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output
table_name = "<Provide your table name>"
# Load the Delta table and run Delta Analyzer
df = spark.read.format("delta").load(f"Tables/{table_name}")
da_results = labs.delta_analyzer(table_name)
# Display the table summary in an HTML table.
clear_output()
df1 = da_results['Summary'].iloc[0]
html_table = "<table border='1'><tr><th>Column Name</th><th>{table_name}</th></tr>"
for column in da_results['Summary'].columns:
html_table += f"<tr><td>{column}</td><td>{df1[column]}</td></tr>"
html_table += "</table>"
display(HTML(html_table))
Wyjście:
Parametr | Wartość |
---|---|
Nazwa tabeli | sales_1 |
liczba wierszy | 1000000000 |
Grupy wierszy | 24 |
Pliki Parquet | 8 |
Maksymalna liczba wierszy na grupę wierszy | 51210000 |
Minimalna liczba wierszy na grupę wierszy | 22580000 |
Średnia liczba wierszy na grupę wierszy | 41666666.666666664 |
Włączono VOrder | Prawda |
Całkowity rozmiar | 7700808430 |
Sygnatura czasowa | 2025-03-24 03:01:02.794979 |
Podsumowanie narzędzia Delta Analyzer przedstawia średni rozmiar grupy wierszy wynoszący około 40 milionów wierszy. Jest to większe niż zalecany maksymalny rozmiar grupy wierszy wynoszący 16 milionów wierszy. Na szczęście większy rozmiar grupy wierszy nie powoduje znaczących problemów dla Direct Lake. Większe grupy wierszy ułatwiają operacje segmentu ciągłego z minimalnym narzutem w silniku przechowywania. Z drugiej strony małe grupy wierszy, te znacznie poniżej 1 miliona wierszy, mogą powodować problemy z wydajnością.
Ważniejsze w poprzednim przykładzie jest to, że Fabric rozdzielił grupy wierszy na osiem plików Parquet. Jest to zgodne z liczbą rdzeni w infrastrukturze Fabric, co umożliwia obsługę wydajnych równoległych operacji odczytu. Ważne jest również, aby rozmiary poszczególnych grup wierszy nie odbiegały zbyt daleko od średniej. Znaczne różnice mogą powodować nierównomierne obciążenie VertiScan, co skutkuje mniej optymalną wydajnością zapytań.
Aktualizacje przesuwnego okna
Dla celów ilustracyjnych poniższy przykład kodu w języku Python symuluje aktualizację okna kroczącego. Kod usuwa wiersze z najstarszym identyfikatorem DateID z przykładowej tabeli delty. Następnie aktualizuje identyfikator DateID tych wierszy i wstawia je ponownie do przykładowej tabeli jako ostatnie wiersze.
from pyspark.sql.functions import lit
table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")
# Get the rows of the oldest DateID.
rows_df = table_df[table_df["DateID"] == 20200101]
rows_df = spark.createDataFrame(rows_df.rdd, schema=rows_df.schema)
# Delete these rows from the table
table_df.createOrReplaceTempView(f"{table_name}_view")
spark.sql(f"DELETE From {table_name}_view WHERE DateID = '20200101'")
# Update the DateID and append the rows as new data
rows_df = rows_df.withColumn("DateID", lit(20250101))
rows_df.write.format("delta").mode("append").save(f"Tables/{table_name}")
Funkcja get_delta_table_history
w bibliotece semantic-link-labs może pomóc w analizie efektu tej aktualizacji okna kroczącego. Zapoznaj się z poniższym przykładem kodu w języku Python. Zobacz również tabelę z danymi wyjściowymi po fragmencie kodu.
import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output
table_name = "<Provide your table name>"
da_results = labs.get_delta_table_history(table_name)
# Create a single HTML table for specified columns
html_table = "<table border='1'>"
# Add data rows for specified columns
for index, row in da_results.iterrows():
for column in ['Version', 'Operation', 'Operation Parameters', 'Operation Metrics']:
if column == 'Version':
html_table += f"<tr><td><b>Version</b></td><td><b>{row[column]}</b></td></tr>"
else:
html_table += f"<tr><td>{column}</td><td>{row[column]}</td></tr>"
html_table += "</table>"
# Display the HTML table
display(HTML(html_table))
Wyjście:
wersja | Opis | Wartość |
---|---|---|
2 | Operacja | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4103076'} | |
1 | Operacja | USUŃ |
Parametry operacji | {'predykat': '["(DateID#3910 = 20200101)"]'} | |
Metryki operacji | {'numRemovedFiles': "8", "numRemovedBytes": "7700855198", "numCopiedRows": "999451335", "numDeletionVectorsAdded": "0", "numDeletionVectorsRemoved": "0", "numAddedChangeFiles": "0", "executionTimeMs": "123446", "numDeletionVectorsUpdated": "0", "numDeletedRows": "548665", "scanTimeMs": "4820", "numAddedFiles": "18", "numAddedBytes": "7696900084", "rewriteTimeMs": "198625"} | |
0 | Operacja | PISAĆ |
Parametry operacji | {'mode': 'Overwrite', 'partitionBy': '[]'} | |
Metryki operacji | {'numFiles': '8', 'numOutputRows': '10000000000', 'numOutputBytes': '7700892169'} |
W powyższej historii analizatora delty pokazano, że ta tabela delty ma teraz następujące trzy wersje:
- Wersja 0: jest to oryginalna wersja z ośmioma plikami Parquet i grupami wierszy 24, jak opisano w poprzedniej sekcji.
-
Wersja 1: ta wersja odzwierciedla operację usuwania . Chociaż tylko jeden dzień danych (DateID = '20200101') został usunięty z przykładowej tabeli z pięcioma latami transakcji sprzedaży, wszystkie osiem plików Parquet zostały naruszone. W metrykach operacji
numRemovedFiles
jest osiem [plików Parquet], anumAddedFiles
wynosi 18 [plików Parquet]. Oznacza to, że operacja usuwania zastąpiła oryginalne osiem plików Parquet 18 nowymi plikami Parquet. - Wersja 3: Metryki operacji pokazują, że do tabeli Delta dodano jeszcze jeden plik Parquet z 548 665 wierszami.
Po aktualizacji przesuwnego okna najnowsza wersja zatwierdzenia Delta obejmuje 19 plików Parquet i 21 grup wierszy, z rozmiarami od 500 tysięcy do 50 milionów wierszy. Aktualizacja okna kroczącego 548 665 wierszy dotyczyła całej tabeli Delta zawierającej 1 miliard wierszy. Zastąpiono wszystkie pliki Parquet i grupy wierszy. W tym przypadku nie można oczekiwać, że przyrostowe ramkowanie poprawi wydajność w zimnym stanie, a zwiększona zmienność rozmiarów grup wierszy jest mało prawdopodobna, aby zapewnić poprawę wydajności w ciepłym stanie.
Aktualizacje tabeli delty
Poniższy kod języka Python aktualizuje tabelę delty w taki sam sposób, jak opisano w poprzedniej sekcji. Na pierwszy rzut oka funkcja 'aktualizacji' zmienia tylko wartość identyfikatora DateID w istniejących wierszach, które odpowiadają danemu identyfikatorowi DateID. Pliki Parquet są jednak niezmienne i nie można ich modyfikować. W tle proces aktualizacji usuwa istniejące pliki Parquet i dodaje nowe pliki Parquet. Wynik i efekt są takie same jak w przypadku aktualizacji okna kroczącego.
from pyspark.sql.functions import col, when
from delta.tables import DeltaTable
# Load the Delta table
table_name = "<Provide your table name>"
delta_table = DeltaTable.forPath(spark, f"Tables/{table_name}")
# Define the condition and the column to update
condition = col("DateID") == 20200101
column_name = "DateID"
new_value = 20250101
# Update the DateID column based on the condition
delta_table.update(
condition,
{column_name: when(condition, new_value).otherwise(col(column_name))}
)
Aktualizacje partycjonowanych okien kroczących
Partycjonowanie może pomóc zmniejszyć efekt aktualizacji tabeli. Użycie kluczy daty może być kuszące, ale szybkie sprawdzenie kardynalności może ujawnić, że nie jest to najlepszy wybór. Na przykład przykładowa tabela omówiona do tej pory zawiera transakcje sprzedaży z ostatnich pięciu lat, co odpowiada około 1800 odrębnym wartościom dat. Ta kardynalność jest zbyt wysoka. Kolumna partycji powinna mieć mniej niż 200 odrębnych wartości.
column_name = 'DateID'
table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")
distinct_count = table_df.select(column_name).distinct().count()
print(f"The '{column_name}' column has {distinct_count} distinct values.")
if distinct_count <= 200:
print(f"The '{column_name}' column is a good partitioning candidate.")
table_df.write.format("delta").partitionBy(column_name).save(f"Tables/{table_name}_by_date_id")
print(f"Table '{table_name}_by_date_id' partitioned and saved successfully.")
else:
print(f"The cardinality of the '{column_name}' column is possibly too high.")
Wyjście:
The 'DateID' column has 1825 distinct values.
The cardinality of the 'DateID' column is possibly too high.
Jeśli nie ma odpowiedniej kolumny partycji, można ją utworzyć sztucznie, zmniejszając kardynalność istniejącej kolumny. Poniższy kod języka Python dodaje kolumnę Month (Miesiąc), usuwając dwie ostatnie cyfry identyfikatora DateID. Spowoduje to wygenerowanie 60 odrębnych wartości. Przykładowy kod zapisuje tabelę delty partycjonowaną według kolumny Month (Miesiąc).
from pyspark.sql.functions import col, expr
column_name = 'DateID'
table_name = "sales_1"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")
partition_column = 'Month'
partitioned_table = f"{table_name}_by_month"
table_df = table_df.withColumn(partition_column, expr(f"int({column_name} / 100)"))
distinct_count = table_df.select(partition_column).distinct().count()
print(f"The '{partition_column}' column has {distinct_count} distinct values.")
if distinct_count <= 200:
print(f"The '{partition_column}' column is a good partitioning candidate.")
table_df.write.format("delta").partitionBy(partition_column).save(f"Tables/{partitioned_table}")
print(f"Table '{partitioned_table}' partitioned and saved successfully.")
else:
print(f"The cardinality of the '{partition_column}' column is possibly too high.")
Wyjście:
The 'Month' column has 60 distinct values.
The 'Month' column is a good partitioning candidate.
Table 'sales_1_by_month' partitioned and saved successfully.
Podsumowanie narzędzia Delta Analyzer teraz pokazuje, że rozmieszczenie tabeli Delta jest dobrze dostosowane do usługi Direct Lake. Średni rozmiar grupy wierszy wynosi około 16 milionów wierszy, a średnie odchylenie bezwzględne w rozmiarach grup wierszy oraz rozmiarach segmentów jest mniejsze niż 1 milion wierszy.
Parametr | Wartość |
---|---|
Nazwa tabeli | sprzedaż_1_według_miesiąca |
liczba wierszy | 1000000000 |
Grupy wierszy | 60 |
Pliki Parquet | 60 |
Maksymalna liczba wierszy na grupę wierszy | 16997436 |
Minimalna liczba wierszy na grupę wierszy | 15339311 |
Średnia liczba wierszy na grupę wierszy | 16666666.666666666 |
Włączono VOrder | Prawda |
Całkowity rozmiar | 7447946016 |
Sygnatura czasowa | 2025-03-24 03:01:02.794979 |
Po aktualizacji okna operacyjnego względem podzielonej na partycje przykładowej tabeli historia analizatora delty pokazuje, że dotyczy to tylko jednego pliku Parquet. Zobacz następującą tabelę danych wyjściowych. Wersja 2 zawiera dokładnie 16 445 655 wierszy skopiowanych ze starego pliku Parquet do zastępczego pliku Parquet, a wersja 3 dodaje nowy plik Parquet z 548 665 wierszami. W sumie usługa Direct Lake musi ponownie załadować około 17 milionów wierszy, co stanowi znaczną poprawę w porównaniu z ponownym ładowaniem 1 miliarda wierszy bez partycjonowania.
wersja | Opis | Wartość |
---|---|---|
2 | Operacja | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4103076'} | |
1 | Operacja | USUŃ |
Parametry operacji | {'predykat': '["(DateID#3910 = 20200101)"]'} | |
Metryki operacji | {'numRemovedFiles': '1', "numRemovedBytes": "126464179", "numCopiedRows": "16445655", "numDeletionVectorsAdded": "0", "numDeletionVectorsRemoved": "0", "numAddedChangeFiles": "0", "executionTimeMs": "19065", "numDeletionVectorsUpdated": "0", "numDeletedRows": "548665", "scanTimeMs": "1926", "numAddedFiles": "1", "numAddedBytes": "121275513", "rewriteTimeMs": "17138"} | |
0 | Operacja | PISAĆ |
Parametry operacji | {'mode': 'Overwrite', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '60', 'numOutputRows': '10000000000', 'numOutputBytes': '7447681467'} |
Wzorzec tylko do dołączania, po którym następuje optymalizacja platformy Spark
Wzorce tylko do dołączania nie mają wpływu na istniejące pliki Parquet. Działają dobrze z przyrostowym ramkowaniem Direct Lake. Nie zapomnij jednak zoptymalizować tabel delty w celu skonsolidowania plików Parquet i grup wierszy, jak opisano wcześniej w tym artykule. Małe i częste dołączania mogą szybko gromadzić pliki i mogą zniekształcać jednolitość rozmiarów grup wierszy.
W poniższych danych wyjściowych przedstawiono historię analizatora delty dla tabeli niepartycyjnej w porównaniu z tabelą partycjonowaną. Historia zawiera siedem dodatków i jedną następną operację optymalizacji.
wersja | Opis | Wartość w układzie domyślnym | Wartość w układzie partycjonowanym |
---|---|---|---|
8 | Operacja | OPTYMALIZOWAĆ | OPTYMALIZOWAĆ |
Parametry operacji | {'predykat': '[]', 'auto': 'false', 'clusterBy': '[]', 'vorder': 'true', 'zOrderBy': '[]'} | {'predykat': '["('Month >= 202501)"]', 'auto': 'false', 'clusterBy': '[]', 'vorder': 'true', 'zOrderBy': '[]'} | |
Metryki operacji | {'numRemovedFiles': '8', 'numRemovedBytes': '991234561', 'p25FileSize': '990694179', "numDeletionVectorsRemoved": "0", "minFileSize": "990694179", "numAddedFiles": "1", "maxFileSize": "990694179", "p75FileSize": "990694179", "p50FileSize": "990694179", "numAddedBytes": "990694179"} | {'numRemovedFiles': '7', 'numRemovedBytes': '28658548', 'p25FileSize': '28308495', "numDeletionVectorsRemoved": "0", "minFileSize": "28308495", "numAddedFiles": "1", "maxFileSize": "28308495", "p75FileSize": "28308495", "p50FileSize": "28308495", "numAddedBytes": "28308495"} | |
7 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '547453', 'numOutputBytes': '4091802'} | {'numFiles': '1', 'numOutputRows': '547453', 'numOutputBytes': '4091802'} | |
6 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548176', 'numOutputBytes': '4095497'} | {'numFiles': '1', 'numOutputRows': '548176', 'numOutputBytes': '4095497'} | |
5 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '547952', 'numOutputBytes': '4090107'} | {'numFiles': '1', 'numOutputRows': '547952', 'numOutputBytes': '4093015'} | |
4 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548631', 'numOutputBytes': '4093134'} | {'numFiles': '1', 'numOutputRows': '548631', 'numOutputBytes': '4094376'} | |
3 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548671', 'numOutputBytes': '4101221'} | { 'numFiles': '1', 'numOutputRows': '548671', 'numOutputBytes': '4101221' } | |
2 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '546530', 'numOutputBytes': '4081589'} | {'numFiles': '1', 'numOutputRows': '546530', 'numOutputBytes': '4081589'} | |
1 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Append', 'partitionBy': '[]'} | {'mode': 'Append', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4101048'} | {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4101048'} | |
0 | Operacja | PISAĆ | PISAĆ |
Parametry operacji | {'mode': 'Overwrite', 'partitionBy': '[]'} | {'mode': 'Overwrite', 'partitionBy': '["Month"]'} | |
Metryki operacji | {'numFiles': '8', 'numOutputRows': '10000000000', 'numOutputBytes': '7700855198'} | {'numFiles': '60', 'numOutputRows': '10000000000', 'numOutputBytes': '7447681467'} |
Patrząc na metryki operacji wersji 8, warto zwrócić uwagę, że operacja optymalizacji dla niepartycyjnej tabeli scaliła osiem plików Parquet wpływających na około 1 GB danych, podczas gdy optymalizacja operacji tabeli partycjonowanej scaliła siedem plików Parquet mających wpływ tylko na około 25 MB danych. Wynika z tego, że usługa Direct Lake będzie działać lepiej z partycjonowaną tabelą.
Uwagi i ograniczenia
Zagadnienia i ograniczenia dotyczące optymalizacji wydajności usługi Direct Lake są następujące:
- Unikaj niszczących wzorców aktualizacji w dużych tabelach delty, aby zachować przyrostowe ramy działania w Direct Lake.
- Małe tabele Delta nie muszą być zoptymalizowane pod kątem przyrostowego przetwarzania.
- Celuj w rozmiar grupy wierszy od 1 miliona do 16 milionów, aby utworzyć segmenty kolumn w usłudze Direct Lake. Usługa Direct Lake preferuje duże segmenty kolumn.
- Unikaj kolumn partycji o wysokiej kardynalności, ponieważ transkodowanie usługi Direct Lake jest mniej wydajne w przypadku wielu małych plików Parquet i grup wierszy niż w przypadku mniejszej liczby dużych plików Parquet i grup wierszy.
- Ze względu na nieprzewidziane zapotrzebowanie na zasoby obliczeniowe i pamięci model semantyczny może zostać ponownie załadowany do innego węzła klastra sieci szkieletowej w stanie zimnym.
- Usługa Direct Lake nie używa statystyk delta\Parquet do pomijania grup wierszy\plików, aby zoptymalizować ładowanie danych.