Wydajność architektury integracji środowiska CLR
Dotyczy:programu SQL ServerAzure SQL Managed Instance
W tym artykule omówiono niektóre opcje projektowania, które zwiększają wydajność integracji programu SQL Server z środowiskiem uruchomieniowym języka wspólnego programu .NET Framework (CLR).
Proces kompilacji
Podczas kompilacji wyrażeń SQL, gdy napotkano odwołanie do zarządzanej procedury, jest generowany wspólny łącznik języka pośredniego (CIL). Ten wycink zawiera kod umożliwiający przeprowadzanie marshalingu rutynowych parametrów z programu SQL Server do środowiska CLR, wywoływania funkcji i zwracania wyniku. Ten kod jest oparty na typie parametru i kierunku parametru (w, lub odwołania).
Kod kleju umożliwia optymalizację specyficzną dla typu i zapewnia wydajne wymuszanie semantyki programu SQL Server, takich jak dopuszczanie wartości null, ograniczanie aspektów, według wartości i standardowa obsługa wyjątków. Generując kod dla dokładnych typów argumentów, należy unikać kosztów tworzenia obiektów typu przymusu lub otoki (nazywanego "boxing") w granicach wywołania.
Wygenerowany wycink jest następnie kompilowany do kodu natywnego i zoptymalizowany pod kątem konkretnej architektury sprzętowej, na której jest wykonywany program SQL Server, przy użyciu usług kompilacji just in time (JIT) środowiska CLR. Usługi JIT są wywoływane na poziomie metody i umożliwiają środowisku hostingu programu SQL Server utworzenie pojedynczej jednostki kompilacji obejmującej zarówno program SQL Server, jak i środowisko CLR. Po skompilowaniu wycinku wynikowy wskaźnik funkcji staje się implementacją funkcji w czasie wykonywania. Takie podejście generowania kodu gwarantuje, że w czasie wykonywania nie ma dodatkowych kosztów wywołań związanych z odbiciem lub dostępem do metadanych.
Szybkie przejścia między programem SQL Server i clR
Proces kompilacji zwraca wskaźnik funkcji, który może być wywoływany w czasie wykonywania z kodu natywnego. W przypadku funkcji zdefiniowanych przez użytkownika z wartością skalarną to wywołanie funkcji odbywa się dla poszczególnych wierszy. Aby zminimalizować koszt przejścia między programem SQL Server i clR, instrukcje zawierające dowolne zarządzane wywołania mają krok uruchamiania w celu zidentyfikowania domeny aplikacji docelowej. Ten krok identyfikacji zmniejsza koszt przejścia dla każdego wiersza.
Zagadnienia dotyczące wydajności
W poniższej sekcji podsumowano zagadnienia dotyczące wydajności specyficzne dla integracji środowiska CLR w programie SQL Server. Aby uzyskać więcej informacji, zobacz Using CLR Integration in SQL Server 2005. Aby uzyskać informacje na temat wydajności kodu zarządzanego, zobacz Ulepszanie wydajności i skalowalności aplikacji platformy .NET.
Funkcje zdefiniowane przez użytkownika
Funkcje CLR korzystają z szybszej ścieżki wywołania niż Transact-SQL funkcji zdefiniowanych przez użytkownika. Ponadto kod zarządzany ma zdecydowaną przewagę wydajności nad Transact-SQL w zakresie kodu proceduralnego, obliczeń i manipulowania ciągami. Funkcje CLR intensywnie korzystające z obliczeń, które nie wykonują dostępu do danych, są lepiej napisane w kodzie zarządzanym. Transact-SQL funkcje wykonują jednak wydajniejsze uzyskiwanie dostępu do danych niż integracja środowiska CLR.
Agregacje zdefiniowane przez użytkownika
Kod zarządzany może znacznie przewyższać agregację opartą na kursorach. Kod zarządzany zazwyczaj działa nieco wolniej niż wbudowane funkcje agregujące programu SQL Server. Zalecamy, aby jeśli istnieje natywna wbudowana funkcja agregacji, należy jej użyć. W przypadkach, w których wymagana agregacja nie jest natywnie obsługiwana, należy rozważyć agregację zdefiniowaną przez użytkownika clR w implementacji opartej na kursorze ze względu na wydajność.
Przesyłanie strumieniowe funkcji wartości tabeli
Aplikacje często muszą zwracać tabelę w wyniku wywołania funkcji. Przykłady obejmują odczytywanie danych tabelarycznych z pliku w ramach operacji importowania i konwertowanie wartości rozdzielanych przecinkami na reprezentację relacyjną. Zazwyczaj można to osiągnąć, materializując i wypełniając tabelę wyników, zanim będzie można jej użyć przez obiekt wywołujący. Integracja środowiska CLR z programem SQL Server wprowadza nowy mechanizm rozszerzalności nazywany funkcją o wartości tabeli przesyłania strumieniowego (STVF). Zarządzane pliki STVFs działają lepiej niż porównywalne rozszerzone implementacje procedur składowanej.
Pliki STVFs to funkcje zarządzane, które zwracają interfejs IEnumerable
.
IEnumerable
ma metody nawigowania po zestawie wyników zwróconym przez STVF. Po wywołaniu narzędzia STVF zwrócony IEnumerable
jest bezpośrednio połączony z planem zapytania. Plan zapytania wywołuje metody IEnumerable
, gdy trzeba pobrać wiersze. Ten model iteracji umożliwia użycie wyników natychmiast po utworzeniu pierwszego wiersza, zamiast czekać na wypełnienie całej tabeli. Znacznie zmniejsza również ilość pamięci zużywanej przez wywołanie funkcji.
Tablice a kursory
Gdy Transact-SQL kursory muszą przechodzić przez dane, które są łatwiej wyrażone jako tablica, kod zarządzany może być używany ze znacznymi wzrostami wydajności.
Dane ciągu
Dane znaków programu SQL Server, takie jak varchar, mogą być typu SqlString
lub SqlChars
w funkcjach zarządzanych.
SqlString
zmienne tworzą wystąpienie całej wartości w pamięci.
SqlChars
zmienne zapewniają interfejs przesyłania strumieniowego, który może służyć do uzyskania lepszej wydajności i skalowalności, nie tworząc wystąpienia całej wartości w pamięci. Staje się to ważne w przypadku dużych danych obiektów (LOB). Ponadto dostęp do danych XML serwera można uzyskać za pośrednictwem interfejsu przesyłania strumieniowego zwróconego przez SqlXml.CreateReader()
.
CLR a rozszerzone procedury składowane
Interfejsy programowania aplikacji (API) Microsoft.SqlServer.Server
, które umożliwiają zarządzanym procedurom wysyłanie zestawów wyników z powrotem do klienta, lepiej niż interfejsy API usług Open Data Services (ODS) używane przez rozszerzone procedury składowane. Ponadto interfejsy API System.Data.SqlServer
obsługują typy danych, takie jak xml, varchar(max), nvarchar(max)i varbinary(max), podczas gdy interfejsy API ODS nie zostały rozszerzone w celu obsługi tych typów danych.
Za pomocą kodu zarządzanego program SQL Server zarządza użyciem zasobów, takich jak pamięć, wątki i synchronizacja. Jest to spowodowane tym, że zarządzane interfejsy API, które uwidaczniają te zasoby, są implementowane na podstawie menedżera zasobów programu SQL Server. Z drugiej strony program SQL Server nie ma widoku ani kontroli nad użyciem zasobów rozszerzonej procedury składowanej. Jeśli na przykład rozszerzona procedura składowana zużywa zbyt dużo zasobów procesora CPU lub pamięci, nie ma możliwości wykrywania ani kontrolowania tego za pomocą programu SQL Server. W przypadku kodu zarządzanego program SQL Server może jednak wykryć, że dany wątek nie wygenerował przez długi czas, a następnie wymusić wykonanie zadania, aby umożliwić zaplanowanie innej pracy. Dlatego użycie kodu zarządzanego zapewnia lepszą skalowalność i użycie zasobów systemowych.
Kod zarządzany może powodować dodatkowe nakłady pracy niezbędne do utrzymania środowiska wykonywania i przeprowadzania kontroli zabezpieczeń. Dzieje się tak na przykład w przypadku uruchamiania wewnątrz programu SQL Server i licznych przejść z kodu zarządzanego do natywnego (ponieważ program SQL Server musi wykonać dodatkową konserwację ustawień specyficznych dla wątku podczas przechodzenia do kodu natywnego i z powrotem). Dlatego rozszerzone procedury składowane mogą znacznie przewyższać wyniki kodu zarządzanego działającego wewnątrz programu SQL Server w przypadkach, w których często występują przejścia między kodem zarządzanym i natywnym.
Nuta
Nie twórz nowych rozszerzonych procedur składowanych, ponieważ ta funkcja jest przestarzała.
Serializacja natywna dla typów zdefiniowanych przez użytkownika
Typy zdefiniowane przez użytkownika (UDT) są zaprojektowane jako mechanizm rozszerzalności dla systemu typów skalarnych. Program SQL Server implementuje format serializacji dla tras zdefiniowanych przez użytkownika o nazwie Format.Native
. Podczas kompilacji struktura typu jest badana w celu wygenerowania CIL dostosowanego dla danej definicji typu.
Serializacja natywna to domyślna implementacja programu SQL Server. Serializacja zdefiniowana przez użytkownika wywołuje metodę zdefiniowaną przez autora typu w celu wykonania serializacji.
Format.Native
serializacji należy używać, gdy jest to możliwe w celu uzyskania najlepszej wydajności.
Normalizacja porównywalnych tras zdefiniowanych przez użytkownika
Operacje relacyjne, takie jak sortowanie i porównywanie tras zdefiniowanych przez użytkownika, działają bezpośrednio na binarnej reprezentacji wartości. Jest to realizowane przez przechowywanie znormalizowanej (uporządkowanej binarnej) reprezentacji stanu udT na dysku.
Normalizacja ma dwie korzyści:
Dzięki temu operacja porównania jest znacznie mniejsza, unikając budowy wystąpienia typu i obciążenia wywołania metody.
Tworzy domenę binarną dla funkcji UDT, umożliwiając tworzenie histogramów, indeksów i histogramów dla wartości typu.
Znormalizowane trasy zdefiniowane przez użytkownika mają podobny profil wydajności do natywnych wbudowanych typów operacji, które nie obejmują wywołania metody.
Skalowalne użycie pamięci
Aby zarządzać odzyskiwaniem pamięci w celu zapewnienia dobrego działania i skalowania w programie SQL Server, należy unikać dużej, pojedynczej alokacji. Alokacje większe niż 88 kilobajtów (KB) są umieszczane na stercie dużych obiektów, co powoduje, że odzyskiwanie pamięci i skalowanie jest gorsze niż wiele mniejszych alokacji. Jeśli na przykład musisz przydzielić dużą tablicę wielowymiarową, lepiej jest przydzielić tablicę postrzępioną (rozproszoną).