Znajdź nieskompresowany rozmiar wszystkich tabel w bazie danych

12

W Dynamics AX istnieje mechanizm buforowania, w którym tabele można skonfigurować tak, aby były ładowane do pamięci i buforowane. Ta pamięć podręczna jest ograniczona do pewnej liczby KB, aby zapobiec problemom z pamięcią. Ustawienie, o którym mówię, jest wywoływane entiretablecachei ładuje cały stół do pamięci, gdy tylko żądany jest pojedynczy rekord.

Do niedawna polegaliśmy na niektórych skryptach, aby sprawdzić rozmiar tabel, które mają to ustawienie, aby sprawdzić, czy rozmiar tabeli przekracza ten limit.

Teraz jednak wchodzi w grę kompresja i rzeczy takie jak sp_spaceused lub sys.allocation_units wydają się raportować przestrzeń faktycznie używaną przez skompresowane dane.

Oczywiście serwer aplikacji pracuje z nieskompresowanymi danymi, więc rozmiar danych na dysku w SQL Server jest nieistotny. Potrzebuję rzeczywistego rozmiaru, jaki będą miały nieskompresowane dane.

Wiem o sp_estimate_data_compression_savings, ale jak sama nazwa wskazuje, to tylko szacunek.
Wolałbym mieć jak najbardziej poprawny rozmiar.

Jedynym sposobem, w jaki mogłem wymyślić, był jakiś zawiły dynamiczny SQL tworzący nieskompresowane tabele o tej samej strukturze co skompresowane tabele, wstawiający skompresowane dane do tej tabeli cienia, a następnie sprawdzający rozmiar tej tabeli cienia.
Nie trzeba dodawać, że jest to nieco żmudne i zajmuje trochę czasu, aby uruchomić bazę danych zawierającą kilkaset GB.

Powershell może być opcją, ale nie chciałbym iterować po wszystkich tabelach, aby wykonać select *na nich sprawdzanie rozmiaru skryptu, ponieważ to po prostu zalałoby pamięć podręczną i prawdopodobnie zabrałoby to również dużo czasu.

Krótko mówiąc, potrzebuję sposobu, aby uzyskać rozmiar dla każdej tabeli, ponieważ będzie ona raz nieskompresowana, a fragmentacja poza równaniem przedstawionym aplikacji, jeśli to możliwe. Jestem otwarty na różne podejścia, preferowany jest T-SQL, ale nie jestem przeciwny Powershellowi ani innym kreatywnym podejściom.

Załóżmy, że bufor w aplikacji ma rozmiar danych. Bigint ma zawsze rozmiar biginta, a typ danych znakowych to 2 bajty na znak (Unicode). Dane BLOB również przyjmują rozmiar danych, wyliczenie jest zasadniczo liczbą całkowitą, a dane liczbowe są liczbowe (38,12), a data-godzina jest wielkością godziny / godziny. Ponadto nie ma żadnych NULLwartości, są one przechowywane jako pusty ciąg 1900-01-01lub zero.

Nie ma dokumentacji dotyczącej tego, jak jest to realizowane, ale założenia są oparte na niektórych testach i skryptach używanych przez PFE i zespół wsparcia (które również najwyraźniej ignorują kompresję, ponieważ kontrola jest wbudowana w aplikację, a aplikacja nie może powiedzieć jeśli podstawowe dane są skompresowane), które również sprawdzają rozmiary tabel. Ten link na przykład stwierdza:

Unikaj używania pamięci podręcznej EntireTable dla dużych tabel (w AX 2009 ponad 128 KB lub 16 stron, w AX 2012 w ustawieniach aplikacji „rozmiar całej pamięci podręcznej tabeli” (domyślnie: 32 KB lub 4 strony)) - zamiast tego przejdź do zapisywania buforowania.

Tom V - spróbuj topanswers.xyz
źródło
3
Jest to zhackowane, ale być może odtworzona kopia z wyłączoną kompresją byłaby najbardziej precyzyjna. Następnie testujesz przywracanie, dzięki czemu wyglądasz jak TOP 1 DBA.
Erik Darling
Uwierz, że to byłby twój najlepszy zakład. Mogą istnieć sposoby, aby spróbować matematyki. Ile wierszy według zdefiniowanych kolumnowych typów danych i długości należy pomnożyć, a następnie dodać do indeksów itp. Jest to o wiele więcej pracy niż tworzenie skryptów przywracania i wyłączanie kompresji sugerowanej powyżej przez @sp_BlitzErik. A kto nie chciałby być TOP 1 DBA?
Mike Walsh
SUMA (długość danych ()) dla wszystkich kolumn uzyskać nieskompresowany rozmiar danych?
Tapakah Ua
@sp_BlitzErik To może być odpowiedź zamiast komentarza.
Tom V - spróbuj topanswers.xyz

Odpowiedzi:

7

Potrzebuję rzeczywistego rozmiaru, jaki będą miały nieskompresowane dane.
...
Wolałbym mieć możliwie jak największy rozmiar.

Chociaż pragnienie tych informacji jest z pewnością zrozumiałe, uzyskanie tych informacji, szczególnie w kontekście „poprawnych, jak to możliwe” jest trudniejsze niż wszyscy się spodziewają z powodu błędnych założeń. Niezależnie od tego, czy wykonujemy pomysł nieskompresowanej tabeli cieni wspomnianej w pytaniu, czy sugestię @ sp_BlitzErik w komentarzu na temat przywracania DB i rozpakowywania tam w celu sprawdzenia, nie należy zakładać, że rozmiar nieskompresowanej tabeli == rozmiar wspomnianych danych w pamięci na serwerze aplikacji:

  1. Czy wszystkie wiersze w tabeli są buforowane? Czy tylko w zasięgu? Zakłada się tutaj, że to wszystko i może to być poprawne, ale uznałem, że należy przynajmniej wspomnieć, że może tak nie być (chyba że dokumentacja stanowi inaczej, ale jest to drobna kwestia, po prostu nie chcę nie ma o tym mowy).

    Pytanie zostało zaktualizowane w celu stwierdzenia: tak, wszystkie wiersze są buforowane.

  2. Struktura nad głową

    1. Po stronie DB:
      strona i narzut na wiersz po stronie DB: to, ile wierszy mieści się na stronie, zależy od wielu czynników, które mogą podważyć szacunki. Nawet przy FILLFACTORwartości 100 (lub 0), nadal może pozostać trochę niewykorzystanego miejsca na stronie, ponieważ nie wystarcza ono na cały wiersz. I to jest dodatek do nagłówka strony. Ponadto, jeśli jakakolwiek funkcja izolacji migawki jest włączona, będzie, moim zdaniem, dodatkowe 13 bajtów na wiersz zajmowanych przez numer wersji, i to podważy szacunki. Istnieją inne drobiazgi związane z rzeczywistym rozmiarem wiersza (bitmapa NULL, kolumny o zmiennej długości itp.), Ale wspomniane do tej pory elementy powinny same o tym mówić.
    2. Po stronie serwera aplikacji:
      jaki typ kolekcji jest używany do przechowywania wyników w pamięci podręcznej? Zakładam, że jest to aplikacja .NET, więc czy to jest DataTable? Ogólna lista? SortedDictionary? Każdy rodzaj kolekcji ma inną liczbę podsłuchaną. Nie spodziewałbym się, że żadna z opcji będzie musiała odzwierciedlać koszty ogólne strony i wiersza po stronie bazy danych, szczególnie w skali (jestem pewien, że niewielka ilość wierszy może nie mieć wystarczającej liczby różnych elementów, ale nie szukasz różnic w setkach bajtów lub zaledwie kilku kB).
  3. Typy danych
    1. Po stronie DB:
      CHAR/ VARCHARdane są przechowywane z 1 bajtem na znak (na razie ignorując znaki dwubajtowe). XMLjest zoptymalizowany, aby nie zajmował prawie tyle miejsca, ile sugerowałaby reprezentacja tekstu. Ten typ danych tworzy słownik nazw elementów i atrybutów i zastępuje faktyczne odniesienia do nich w dokumencie ich odpowiednimi identyfikatorami (właściwie to całkiem miłe). W przeciwnym razie wszystkie wartości ciągów to UTF-16 (2 lub 4 bajty na „znak”), podobnie jak NCHAR/ NVARCHAR. DATETIME2ma od 6 do 8 bajtów. DECIMALma od 5 do 17 bajtów (w zależności od precyzji).
    2. Po stronie serwera aplikacji:
      ciągi znaków (ponownie, przy założeniu .NET) są zawsze w formacie UTF-16. Nie ma optymalizacji dla łańcuchów 8-bitowych, takich jak co VARCHARtrzyma. ALE ciągi mogą być również „internowane”, co jest udostępnioną kopią, do której można się odwoływać wiele razy (ale nie wiem, czy to działa na ciągi w kolekcjach, a jeśli tak, to czy działa na wszystkie typy kolekcji). XMLmogą, ale nie muszą być przechowywane w ten sam sposób w pamięci (będę musiał to sprawdzić). DateTimeZawsze 8 bajtów (takie jak t-SQL DATETIME, ale nie tak DATE, TIMElub DATETIME2). Decimalma zawsze 16 bajtów .

Wszystko po to, by powiedzieć: po stronie DB praktycznie nie można nic zrobić, aby uzyskać nawet dość dokładny rozmiar pamięci po stronie serwera aplikacji. Musisz znaleźć sposób na przesłuchanie samego serwera aplikacji po załadowaniu określonej tabeli, więc wiedz, jak duża jest. I nie jestem pewien, czy debugger pozwoli ci zobaczyć rozmiar środowiska wykonawczego wypełnionej kolekcji. Jeśli nie, to jedynym sposobem na zbliżenie jest przejście przez wszystkie wiersze tabeli, pomnożenie każdej kolumny przez odpowiedni rozmiar .NET (np. INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃 itd.), Ale to wciąż pozostawia pytanie narzutów kolekcji oraz każdego elementu kolekcji.

Biorąc pod uwagę nową definicję w pytaniu, prawdopodobnie można wykonać następujące zapytanie, aby się zbliżyć. I nie ma znaczenia, czy tabela jest skompresowana, czy nie, ale to od każdej osoby zależy, czy skanowanie wszystkich wierszy jest odpowiednie na produkcji (może to zrobić z przywracania lub poza godzinami szczytu):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Pamiętaj jednak, że nie uwzględnia to obciążenia kolekcji ani elementu kolekcji. I nie jestem pewien, czy możemy uzyskać tę wartość bez debuggera (lub czegoś takiego jak ILSpy, ale nie polecam tego, ponieważ może to naruszać umowę EULA w zależności od lokalnych przepisów).

Solomon Rutzky
źródło
W końcu wdrożyliśmy kontrole w kodzie, aby mieć pewność, że rozmiar bufora jest prezentowany aplikacji.
Tom V - spróbuj topanswers.xyz
6

Z twojego pytania wynika, że ​​masz maksymalny rozmiar pamięci podręcznej Si nie chcesz ładować tabel do pamięci podręcznej, które przekraczają ten rozmiar. Jeśli to prawda, nie musisz znać dokładnego rozmiaru każdego stołu. Musisz tylko wiedzieć, czy tabela jest większa lub mniejsza niż maksymalny rozmiar pamięci podręcznej S. Jest to znacznie łatwiejszy problem w zależności od definicji kolumn tabel i liczby wierszy.

Zgadzam się ze świetną odpowiedzią Solomona Rutzky'ego na to, że patrzenie na nieskompresowane dane nie jest właściwą drogą i może być trudno znaleźć dobre przybliżenie prawdziwej wielkości tabeli w pamięci podręcznej. Będę jednak pracował w ramach pytania i założę, że możesz opracować formułę, która będzie wystarczająco bliska na podstawie definicji kolumn dla statycznych typów danych i rzeczywistej długości dynamicznych kolumn.

Jeśli masz takie odwzorowanie typów danych na rozmiar pamięci podręcznej, powinieneś być w stanie ocenić niektóre tabele, nawet nie patrząc na zawarte w nich dane:

  1. Jeśli tabela ma tylko statyczne typy danych (bez ciągów ani obiektów blob), można przybliżać liczbę wierszy, patrząc sys.partitionsi obliczając rozmiar tabeli przy użyciu definicji kolumn.
  2. Jeśli tabela z dużą ilością wierszy ma wystarczającą liczbę statycznych typów danych, możesz być w stanie wyeliminować ją jako zbyt dużą bez patrzenia na jej dane. Na przykład tabela z 10 milionami wierszy i 5 BIGINTkolumnami może mieć rozmiar tych danych o wielkości 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M bajtów, które mogą być większe niż limit wielkości pamięci podręcznej S. Nie ma znaczenia, czy ma również kilka kolumn ciągów.
  3. Jeśli tabela z kilkoma wierszami jest wystarczająco mała, możesz być w stanie potwierdzić, że jest poniżej limitu, po prostu zakładając, że każdy dynamiczny typ danych ma maksymalny możliwy rozmiar. Na przykład tabela zawierająca 100 wierszy z BIGINTkolumną i NVARCHAR(20)kolumną nie może przekraczać 100 * (8 + 2 * 20) = 4800 bajtów.
  4. Może być prawdą, że jeśli tabela ma skompresowany rozmiar w programie SQL Server, który jest większy z jakiegoś powodu S, jest bardzo mało prawdopodobne, aby zmieścił się w pamięci podręcznej. Musisz wykonać testy, aby dowiedzieć się, czy taka wartość istnieje.
  5. Możesz mieć szczęście, że wszystkie dynamiczne kolumny mają statystyki na ich temat. Statystyki zawierają informacje o średniej długości, które mogą być wystarczająco dokładne dla twoich celów.

Może być konieczne zapytanie o dane tabel, które nie spełniają żadnego z powyższych kryteriów. Istnieje kilka sztuczek, których można użyć, aby zminimalizować wpływ na wydajność. Powiedziałbym, że masz tutaj dwa konkurujące ze sobą priorytety: cenisz dokładność, ale także nie chcesz skanować wszystkich danych w bazie danych. Możliwe, że można dodać bufor do obliczeń. Nie wiem, czy bardziej dopuszczalne jest wykluczenie tabeli, która jest nieco poniżej maksymalnego rozmiaru pamięci podręcznej, Sczy uwzględnienie tabeli, która jest nieco powyżej maksymalnego rozmiaru pamięci podręcznej.

Oto kilka pomysłów na szybsze wykonywanie zapytań dotyczących danych w tabeli:

  1. W przypadku dużych tabel możesz użyć TABLESAMPLEtak długo, jak długo twoja próbka jest wystarczająco duża.
  2. W przypadku dużych tabel z kluczem klastrowym przydatne może być przetwarzanie ich partiami w kluczu klastrowym. Niestety nie znam sposobu, aby obliczyć, SUM()który kończy się wcześniej, na podstawie wartości tego agregatu. Widziałem tylko taką pracę ROW_NUMBER(). Ale możesz zeskanować pierwsze 10% tabeli, zaoszczędzić obliczony rozmiar danych, zeskanować kolejne 10% i tak dalej. W przypadku tabel, które są zbyt duże dla pamięci podręcznej, możesz być w stanie zaoszczędzić znaczną ilość pracy dzięki temu podejściu, kończąc wcześniej.
  3. W przypadku niektórych tabel możesz mieć szczęście, że masz indeksy obejmujące wszystkie dynamiczne kolumny. W zależności od wielkości wiersza lub innych czynników skanowanie każdego indeksu na raz może być szybsze niż skanowanie tabeli. Możesz także zakończyć ten proces wcześniej, jeśli rozmiar tabeli jest zbyt duży po odczytaniu indeksu w jednej kolumnie.
  4. Średnie długości twoich dynamicznych kolumn mogą się bardzo nie zmieniać w czasie. Praktyczne może być zaoszczędzenie średnich długości, które obliczasz, i użycie tych wartości w swoich obliczeniach przez chwilę. Możesz zresetować te wartości na podstawie aktywności DML w tabelach lub na podstawie innych danych.
  5. Jeśli możliwe jest uruchomienie testów na wszystkich tabelach w celu opracowania algorytmu, być może będziesz w stanie skorzystać z wzorców w danych. Na przykład, jeśli przetwarzasz tabele zaczynające się od najmniejszych, może się okazać, że kiedy przetworzysz 10 (podniosłem ten numer) tabel w rzędzie, które są zbyt duże dla pamięci podręcznej, jest bardzo mało prawdopodobne, aby jakiekolwiek większe tabele pasowały do Pamięć podręczna. Może to być dopuszczalne, jeśli wykluczenie kilku tabel, które mogłyby zmieścić się w pamięci podręcznej, jest w porządku.

Zdaję sobie sprawę, że w tej odpowiedzi nie zawarłem żadnego kodu SQL. Daj mi znać, czy pomocne byłoby napisanie kodu demonstracyjnego dla któregokolwiek z omawianych tutaj pomysłów.

Joe Obbish
źródło
2
Nie myślałem o podejściu wykluczania takich tabel, podoba mi się to podejście
Tom V - spróbuj topanswers.xyz