Kiedy `nvarchar / nchar` będzie używany z SQL Server 2019?

11

Z Microsoft SQL Server 2019 wprowadza UTF-8 za CHARi VARCHARtypów danych i mówi:

Ta funkcja może zapewnić znaczne oszczędności pamięci, w zależności od używanego zestawu znaków. Na przykład zmiana istniejącego typu danych kolumny za pomocą ciągów ASCII z NCHAR (10) na CHAR (10) przy użyciu sortowania z włączoną funkcją UTF-8 przekłada się na prawie 50% zmniejszenie wymagań dotyczących pamięci. Zmniejszenie to wynika z tego, że NCHAR (10) wymaga 22 bajtów do przechowywania, podczas gdy CHAR (10) wymaga 12 bajtów dla tego samego ciągu Unicode.

Wydaje się, że UTF-8 obsługuje każdy skrypt, więc w zasadzie możemy zacząć przechowywać dane varchari charkolumny Unicode . I jak powiedziano w dokumentacji, może to zmniejszyć rozmiar tabel i indeksów, a stamtąd możemy uzyskać jeszcze lepszą wydajność, ponieważ odczytywana jest mniejsza ilość danych.

Zastanawiam się, czy to oznacza, że ​​możemy przestać używać nvarchari ncharkolumn, które implementują UTF-16?

Czy ktoś może wskazać scenariusz i powód, aby nie używać typów danych char z UTFkodowaniem i nadal używać n-znaków?

gotqn
źródło
Dlaczego tego nie przetestujesz i nie zgłosisz? Daj nam również znać, ile wysiłku poświęciłeś na konwersję z nvarchar na varchar - ile czasu zajęły tabele zmian, ile czasu spędziłeś na testowaniu i jakie napotkałeś problemy.
Colin 't Hart,
@ Colin'tHart Jeśli nie są znane żadne problemy ani względy, planuję migrację danych, ponieważ uważam, że odczyt mniejszej liczby danych w ogóle poprawi wydajność systemu. O konwersji - oczywiście zajmie to trochę czasu, zwłaszcza jeśli masz indeksy z podaną kolumną - trzeba je odbudować, ale wierzę, że dobrze się opłaci. Oczywiście zamierzam wkrótce przetestować wpływ na wydajność, szukając tylko problemów, które spowodują, że migracja będzie niepotrzebna.
gotqn
Należy pamiętać, że SQL Server obsługuje kompresję Unicode dla kolumn NVarchar podczas korzystania z kompresji PAGE lub ROW. docs.microsoft.com/en-us/sql/relational-databases/…
David Browne - Microsoft
1
Warto zauważyć, że chociaż UTF-8 może zaoszczędzić miejsce, jeśli przechowujesz „dane podobne do ASCII”, to nie jest to kompresja sama w sobie i nie powinna być mylona jako taka. Na przykład, jeśli przechowujesz głównie chińskie nazwy w bazie danych, gorsze byłoby używanie CHARtypów UTF-8 niż typów Unicode (z kompresją lub bez, ponieważ ostatecznie dane muszą zostać rozpakowane do przetworzenia). Weź również pod uwagę, że rodzimym typem ciągów Windows jest Unicode, więc ciągi UTF-8 często wymagają dekodowania. Związane z tym kompromisy oznaczają, że Ntypy nie zostaną wkrótce wycofane.
Jeroen Mostert
1
„Zabójczą aplikacją” nr 1 dla UTF-8 CHARjest prawdopodobnie SQL Server w systemie Linux, jeśli silnik otrzymuje natywną obsługę przetwarzania ciągów znaków bezpośrednio jako UTF-8 - tutaj UTF-8 jest „rodzimym” zestawem znaków (mniej więcej) i utrzymywanie ciągów w pobliżu, ponieważ UTF-16 jest mniej wydajną alternatywą. Oczywiście nie zaszkodzi również używać go w systemie Windows w miejscach, w których już używasz CHAR, ponieważ układanie ograniczające znaki, które można przechowywać, nigdy nie było atrakcyjne.
Jeroen Mostert

Odpowiedzi:

6

może to zmniejszyć rozmiar tabel i indeksów (wyróżnienie dodane)

Zmniejszenie rozmiaru to tylko możliwe, jeśli większość z bohaterów są w istocie [space], 0 - 9, A - Z, a - z, a niektóre podstawowe znaki interpunkcyjne. Poza tym konkretnym zestawem znaków (w praktyce, standardowe wartości ASCII 32–126) będziesz w najlepszym razie równy rozmiarowi NVARCHAR/ UTF-16, lub w wielu przypadkach większy.

Planuję migrację danych, ponieważ uważam, że odczyt mniej danych w ogóle doprowadzi do lepszej wydajności systemu.

Bądź ostrożny. UTF-8 nie jest magicznym przełącznikiem „napraw wszystko”. Wszystkie inne rzeczy są równe, tak, czytanie mniej poprawia wydajność. Ale tutaj „wszystkie inne rzeczy” nie są równe. Nawet przy przechowywaniu tylko standardowych znaków ASCII (co oznacza, że ​​wszystkie znaki mają 1 bajt, a zatem wymagają połowy miejsca w porównaniu do przechowywania w NVARCHAR), istnieje niewielka utrata wydajności za użycie UTF-8. Uważam, że problem wynika z faktu, że UTF-8 jest kodowaniem o zmiennej długości, co oznacza, że ​​każdy bajt musi być interpretowany podczas odczytu, aby wiedzieć, czy jest to pełny znak, czy też następny bajt jest jego częścią. Oznacza to, że wszystkie operacje na łańcuchach muszą zaczynać się od początku i następować bajt po bajcie. Z drugiej strony,NVARCHAR / UTF-16 ma zawsze 2 bajty (nawet znaki uzupełniające składają się z dwóch 2-bajtowych punktów kodowych), więc wszystko można odczytać w 2-bajtowych porcjach.

W moich testów, nawet z tylko standardowych znaków ASCII, przechowującego dane jako UTF-8 Nie umieszczono oszczędności upływającego czasu, ale był zdecydowanie gorszy dla czasu procesora. I to bez kompresji danych, więc przynajmniej było mniej miejsca na dysku. Ale podczas korzystania z kompresji przestrzeń wymagana dla UTF-8 była tylko 1% - 1,5% mniejsza. Tak więc efektywnie brak oszczędności miejsca i jeszcze dłuższy czas procesora dla UTF-8.

Sprawa się komplikuje, gdy używasz, NVARCHAR(MAX)ponieważ kompresja Unicode nie działa z tym typem danych, nawet jeśli wartość jest na tyle mała, że ​​można ją przechowywać w wierszu. Ale jeśli dane są wystarczająco małe, nadal powinny korzystać z kompresji wierszy lub stron (w takim przypadku faktycznie stają się one szybsze niż UTF-8). Jednak dane poza wierszem nie mogą korzystać z żadnej kompresji. Nadal jednak uczynienie z tabeli Indeks klastrowego magazynu kolumn znacznie zmniejsza rozmiar NVARCHAR(MAX)(nawet jeśli nadal jest on nieco większy niż UTF-8 przy użyciu Indeks klastrowanego magazynu kolumn).

Czy ktoś może wskazać scenariusz i powód, aby nie używać typów danych char z kodowaniem UTF

Zdecydowanie. W rzeczywistości nie znajduję przekonującego powodu, aby z niego korzystać w większości przypadków. Jedyny scenariusz, który naprawdę korzysta z UTF-8, to:

  1. Dane są w większości standardowe ASCII (wartości 0–127)
  2. Musi to być Unicode, ponieważ może być konieczne przechowywanie szerszego zakresu znaków niż jest to możliwe na pojedynczej 8-bitowej stronie kodowej (tj. VARCHAR)
  3. Większość danych jest przechowywana poza wierszem (więc kompresja strony nawet nie działa)
  4. Masz wystarczającą ilość danych, które potrzebujesz / chcesz zmniejszyć rozmiar z powodów niezwiązanych z wydajnością zapytań (np. Zmniejsz rozmiar kopii zapasowej, skróć czas wymagany do utworzenia kopii zapasowej / przywrócenia itp.)
  5. Nie możesz użyć Clustered Columnstore Index (być może użycie tabeli obniża wydajność w tym przypadku?)

Moje testy pokazują, że w prawie wszystkich przypadkach NVARCHAR był szybszy, szczególnie gdy było więcej danych. W rzeczywistości 21 tys. Wierszy ze średnio 5 tys. Znaków na wiersz wymagało 165 MB dla UTF-8 i 236 MB dla NVARCHARnieskompresowanych. A jednak NVARCHARbył dwa razy szybszy w czasie, który upłynął, i co najmniej 2x szybszy (czasem więcej) w czasie procesora. Mimo to zajęło 71 MB więcej na dysku.

Poza tym nadal nie zalecałbym używania UTF-8, przynajmniej od CTP 2, z powodu różnych błędów, które znalazłem w tej funkcji.

Aby uzyskać szczegółową analizę tej nowej funkcji, w tym wyjaśnienie różnic między UTF-16 i UTF-8, oraz listę tych błędów, zobacz mój post:

Natywne wsparcie UTF-8 w SQL Server 2019: Zbawiciel czy fałszywy prorok?

Solomon Rutzky
źródło
12

Obsługa UTF-8 daje nowy zestaw opcji. Potencjalne oszczędności miejsca (bez kompresji wierszy lub strony ) to jedna kwestia, ale wybór rodzaju i kodowania powinien być prawdopodobnie dokonywany przede wszystkim na podstawie rzeczywistych wymagań dotyczących porównania, sortowania, importu i eksportu danych .

Być może będziesz musiał zmienić więcej, niż myślisz, ponieważ np. nchar(1)Typ zapewnia dwa bajty pamięci. To wystarczy, aby zapisać dowolny znak w BMP (punkty kodowe od 000000 do 00FFFF). Niektóre znaki z tego zakresu byłyby kodowane za pomocą 1 bajtu w UTF-8, podczas gdy inne wymagałyby 2 lub nawet 3 bajtów ( więcej szczegółów zawiera ta tabela porównawcza ). Dlatego wymagałoby to pokrycia tego samego zestawu znaków w UTF-8 char(3).

Na przykład:

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 char(1) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

daje znany błąd:

Msg 8152, poziom 16, stan 30, wiersz xxx
Ciąg lub dane binarne zostałyby obcięte.

Lub jeśli flaga śledzenia 460 jest aktywna:

Msg 2628, poziom 16, stan 1, wiersz xxx
Ciąg lub dane binarne zostałyby obcięte w tabeli „@T”, kolumna „UTF8”. Wartość obcięta: „”.

Rozszerzenie kolumny UTF8 do char(2)lub varchar(2)rozwiązuje błąd dla NCHAR(911):

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 varchar(2) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

Jednak gdyby tak było np. NCHAR(8364), Konieczne byłoby dalsze rozwinięcie kolumny, do char(3)lub varchar(3).

Zauważ również, że wszystkie sortowania UTF-8 używają znaków dodatkowych, więc nie będą działać z replikacją.

Oprócz czegokolwiek innego, obsługa UTF-8 jest obecnie tylko w wersji zapoznawczej, więc nie jest dostępna do użytku produkcyjnego.

Paul White 9
źródło