Каковы основные различия в производительности между типами данных SQL Server varchar и nvarchar?

236

Я работаю над базой данных для небольшого веб-приложения в моей школе, используя SQL Server 2005.
Я вижу несколько школ мысли по вопросу varcharпротив nvarchar:

  1. Используйте, varcharесли вы не имеете дело с большим количеством интернационализированных данных, затем используйте nvarchar.
  2. Просто используйте nvarcharдля всего.

Я начинаю видеть достоинства представления 2. Я знаю, что nvarchar занимает вдвое больше места, но это не обязательно огромная сделка, поскольку она будет хранить данные только для нескольких сотен студентов. Мне кажется, что было бы проще не беспокоиться об этом и просто позволить всему использовать nvarchar. Или мне чего-то не хватает?

Джейсон Бейкер
источник
похожий вопрос здесь: stackoverflow.com/questions/312170/… EDIT от le dorfier: который интересно пришел к совершенно противоположному выводу.
Booji Boy
6
Ссылка на гораздо более обширную тему пришла к противоположному выводу. stackoverflow.com/questions/312170/…
dkretz
2
Джейсон: Я надеюсь, что это не несоответствующий запрос, но не могли бы вы рассмотреть вопрос об изменении принятого ответа на gbn . Ответ ДжоБароне ужасно неверен по многим причинам. Его «принятие» вводит новичков в заблуждение, делая неправильный выбор. «Всегда использовать NVARCHAR» ненужно и расточительно , и это может оказать очень негативное влияние на производительность и затраты / бюджет на оборудование. Несколько рядов, даже несколько тысяч, не будут иметь значения. Но системы растут быстрее, чем ожидают люди, поэтому принятый в настоящее время ответ является плохой услугой для сообщества. Спасибо.
Соломон Руцкий

Ответы:

140

Всегда используйте nvarchar.

Возможно, вам никогда не понадобятся двухбайтовые символы для большинства приложений. Однако, если вам требуется поддержка двухбайтовых языков и у вас есть только однобайтовая поддержка в схеме базы данных, очень дорого вернуться и модифицировать свое приложение.

Стоимость переноса одного приложения с varchar на nvarchar будет намного больше, чем немного дополнительного дискового пространства, которое вы будете использовать в большинстве приложений.

Джо Бароне
источник
4
гораздо труднее вернуться назад и добавить поддержку многоязычных текстов / сообщений, часовых поясов, единиц измерения и валюты, поэтому каждый ДОЛЖЕН всегда кодировать их в своем приложении с первого дня, ВСЕГДА (даже если это только на вашей домашней странице в Интернете). приложение)!
КМ.
82
Как насчет размера индекса, использования памяти и т. Д.? Я предполагаю, что вы всегда используете int, когда вы можете использовать tinyint тоже "на всякий случай"?
2010 года
99
Всегда кодирование / планирование для многоязычного сайта (когда у вас нет никаких намеков на то, что оно вам когда-нибудь понадобится) - это все равно, что сказать всем молодым взрослым, что они должны купить большой 8-местный, газопоглощающий внедорожник для своего первого автомобиля ... в конце концов они могут когда-нибудь пожениться и иметь 6 детей. Я предпочел бы наслаждаться производительностью и эффективностью, пока могу и заплатить цену за обновление, когда / если мне это нужно.
Э.Дж. Бреннан
4
@cbmeeks: я не пишу то, что я не знаю. Но если вы можете использовать его без заметного
снижения
60
Обычно, когда люди начинают свой ответ со слова «Всегда», тогда вы должны игнорировать все, что происходит после этого. (Обратите внимание, что я начал это утверждение со слова «обычно» :)
Брэндон Мур
226

Дисковое пространство не проблема ... но память и производительность будут. Двойное чтение страниц, двойной размер индекса, странное LIKE и = постоянное поведение и т. Д.

Вам нужно хранить китайский сценарий и т. Д.? Да или нет...

И от MS BOL " Хранение и производительность Unicode "

Редактировать :

Недавний вопрос о том, насколько плохой может быть производительность nvarchar ...

SQL Server использует высокий процессор при поиске внутри строк nvarchar

ГБН
источник
19
+1, если ваше приложение выходит на международный уровень, у вас будет много других проблем, связанных с поиском / заменой nvarchar: многоязычный текст / сообщения, часовые пояса, единицы измерения и валюта
км.
2
Но что, если вам нужно иногда хранить иностранное имя, например, Хосе или Бьёрн?
Qwertie
7
@Qwertie: тогда вы используете nvarchar. То, что вы не делаете, используйте это без необходимости. Эти два имени вписываются в varchar в любом случае IIRC
gbn
6
Сказать, что дисковое пространство - это не проблема, не для всех. Мы наивно использовали nvarchar без необходимости в большом банковском приложении с миллиардами записей, хранящихся в течение многих лет. С дорогим хранилищем на основе SAN с репликацией, резервным копированием и аварийным восстановлением это может фактически превратиться в миллионы долларов для nvarchar vs varchar. Не говоря уже о существенном (100%) влиянии на производительность, когда при каждом чтении приходится читать вдвое больше байтов с диска.
codemonkey
2
@codemonkey и др. Я сделал все возможное, чтобы решить проблему неиспользуемого пространства в следующей статье: « Диск дешев! ORLY? (требуется бесплатная регистрация). Эта статья призвана помочь предотвратить возникновение ситуации, с которой столкнулся codemonkey в отношении дорогого хранилища корпоративного уровня.
Соломон Руцки
59

Быть последовательным! Присоединение VARCHAR к NVARCHAR имеет большой успех.

Томас Харлан
источник
115
Если вы выполняете объединения в символьных полях, то ваша база данных, вероятно, имеет худшие проблемы, чем использовать nvarchar или varchar, вообще говоря.
Брэндон Мур
@Thomas Харлан Простого тест показывает мне , что нет никакой ощутимой разницы между присоединением nvarcharк varcharпротив преобразования nvarcharк varcharи присоединению к varchar. Если, конечно, вы не имели в виду согласованность типов данных столбцов, а не присоединение.
17
1
@ajeh and Thomas: 1) «простые» тесты часто вводят в заблуждение, поскольку они не охватывают изменения, которые вызывают различия в поведении. 2) Если человек видит резкое падение производительности при смешивании VARCHARи NVARCHAR, что должно быть связанно с индексацией VARCHARколонны вместе с типом сортировки , используемой для этого столбца (а следовательно , и индекса). Я подробно рассмотрю эту тему в следующем сообщении в блоге: Влияние на индексы при смешивании типов VARCHAR и NVARCHAR .
Соломон Руцкий,
44

У nvarchar будут значительные накладные расходы на память, хранилище, рабочий набор и индексацию, поэтому, если спецификации требуют, что это действительно никогда не понадобится, не беспокойтесь.

У меня не было бы жесткого и быстрого правила «всегда nvarchar», потому что оно может быть полной тратой во многих ситуациях - особенно ETL из ASCII / EBCDIC или идентификаторы и столбцы кода, которые часто являются ключами и внешними ключами.

С другой стороны, есть много случаев столбцов, где я обязательно задал бы этот вопрос рано, и если бы я не получил точный и быстрый ответ сразу, я бы сделал столбец nvarchar.

Кейд Ру
источник
26

Я не решаюсь добавить еще один ответ, поскольку их уже немало, но необходимо сделать несколько замечаний, которые либо не были сделаны, либо не были четко сформулированы.

Во- первых: Do не всегда использовать NVARCHAR. Это очень опасный и часто дорогостоящий подход / подход. И не лучше сказать « Никогда не используйте курсоры», так как они иногда являются наиболее эффективным средством решения конкретной проблемы, и общий обходной WHILEцикл выполнения цикла почти всегда будет медленнее, чем правильно сделанный Курсор.

Единственный раз, когда вы должны использовать термин «всегда», это когда вы советуете «всегда делать то, что лучше для ситуации». Конечно, это часто трудно определить, особенно когда мы пытаемся сбалансировать краткосрочные выгоды во времени разработки (менеджер: «нам нужна эта функция, о которой вы не знали до сих пор - неделю назад!») С давно расходы на техническое обслуживание (менеджер, который первоначально заставил команду завершить трехмесячный проект в трехнедельном спринте: «почему у нас возникают такие проблемы с производительностью? как мы могли бы сделать X без гибкости? один-два спринта, чтобы это исправить. Что мы можем сделать за неделю, чтобы вернуться к приоритетным задачам? И нам определенно нужно больше времени уделять дизайну, чтобы этого не происходило! »).

Второе: ответ @ gbn затрагивает некоторые очень важные моменты, которые следует учитывать при принятии определенных решений по моделированию данных, когда путь не ясен на 100%. Но есть еще что рассмотреть:

  • размер файлов журнала транзакций
  • время, необходимое для репликации (при использовании репликации)
  • время, необходимое для ETL (если ETLing)
  • время доставки журналов в удаленную систему и восстановления (если используется доставка журналов)
  • размер резервных копий
  • время, необходимое для завершения резервного копирования
  • время, необходимое для восстановления (это может быть важно когда-нибудь ;-)
  • размер, необходимый для tempdb
  • выполнение триггеров (для вставленных и удаленных таблиц, которые хранятся в базе данных tempdb)
  • производительность управления версиями строк (если используется SNAPSHOT ISOLATION, поскольку хранилище версий находится в базе данных tempdb)
  • возможность получить новое дисковое пространство, когда финансовый директор говорит, что в прошлом году он только что потратил 1 миллион долларов на сеть хранения данных, и поэтому они не будут выделять еще 250 тысяч долларов на дополнительное хранилище.
  • продолжительность выполнения операций INSERT и UPDATE
  • время, необходимое для ведения индекса
  • и т. д. и т. д.

Потеря пространства имеет огромный каскадный эффект на всю систему. Я написал статью, подробно описав эту тему: « Диск дешев! ORLY? (требуется бесплатная регистрация; извините, я не контролирую эту политику).

Третье: хотя некоторые ответы неправильно фокусируются на аспекте «это маленькое приложение», а некоторые правильно предлагают «использовать то, что уместно», ни один из ответов не дал реального руководства ОП. Важная деталь, упомянутая в Вопросе является то, что это веб-страница для их школы. Большой! Таким образом, мы можем предположить, что:

  • Поля для имен студентов и / или преподавателей, вероятно, должны быть такими, NVARCHARпоскольку со временем становится все более вероятным, что имена из других культур будут появляться в этих местах.
  • Но для адреса и названия города? Цель приложения не была указана (это было бы полезно), но предполагая, что адресные записи, если таковые имеются, относятся только к определенному географическому региону (т. Е. К одному языку / культуре), затем используйте их VARCHARс соответствующей кодовой страницей (которая определяется из сопоставления поля).
  • При хранении кодов ISO штата и / или страны (не нужно хранить INT/, TINYINTпоскольку коды ISO имеют фиксированную длину, удобочитаемы и, как правило, стандартны :), используйте CHAR(2)двухбуквенные коды и CHAR(3)3-буквенные коды. И рассмотрите возможность использования бинарной сортировки, такой как Latin1_General_100_BIN2.
  • При хранении почтовых индексов (т.е. почтовых индексов) используйте, VARCHARпоскольку это международный стандарт, никогда не используйте никакие буквы за пределами AZ. И да, по-прежнему используйте, VARCHARдаже если хранятся только почтовые индексы США, а не INT, поскольку почтовые индексы не являются числами, они являются строками, и некоторые из них имеют начальный «0». И рассмотрите возможность использования бинарной сортировки, такой как Latin1_General_100_BIN2.
  • Если вы храните адреса электронной почты и / или URL-адреса, используйте их, NVARCHARпоскольку теперь они могут содержать символы Юникода.
  • и так далее....

Четвертое: теперь, когда у вас есть NVARCHARданные, занимающие вдвое больше места, чем нужно для данных, которые хорошо вписываются VARCHAR(«хорошо вписывается» = не превращаются в «?») И каким-то образом, как по волшебству, приложение росло и теперь есть миллионы записей, по крайней мере, в одном из этих полей, где большинство строк являются стандартными ASCII, но некоторые содержат символы Юникода, поэтому вам следует сохранить NVARCHARследующее:

  1. Если вы используете SQL Server 2008 - 2016 RTM и используете Enterprise Edition, или, если используете SQL Server 2016 с пакетом обновления 1 (который сделал сжатие данных доступным во всех выпусках) или новее, вы можете включить сжатие данных . Сжатие данных может (но не будет «всегда») сжатие данные Unicode в NCHARи NVARCHARполе. Определяющими факторами являются:

    1. NCHAR(1 - 4000)и NVARCHAR(1 - 4000)использовать стандартную схему сжатия для Unicode , но только начиная с SQL Server 2008 R2, и только для данных IN ROW, а не OVERFLOW! Это выглядит лучше, чем обычный алгоритм сжатия ROW / PAGE.
    2. NVARCHAR(MAX)и XML(и я думаю, также VARBINARY(MAX), TEXTи NTEXT) данные, которые находятся в строке (не в строке на страницах LOB или OVERFLOW), по крайней мере, могут быть сжаты на PAGE, но не сжаты на ROW. Конечно, сжатие PAGE зависит от размера значения в строке: я проверил с помощью VARCHAR (MAX) и увидел, что 6000 строк символов / байтов не будут сжиматься, но 4000 строк символов / байтов сделали.
    3. Любые данные OFF ROW, LOB или OVERLOW = Нет сжатия для вас!
  2. Если вы используете SQL Server 2005 или RTM 2008 - 2016, а не Enterprise Edition, у вас может быть два поля: одно VARCHARи одно NVARCHAR. Например, предположим, что вы храните URL-адреса, которые в основном все являются базовыми символами ASCII (значения 0–127) и, следовательно, вписываются VARCHAR, но иногда содержат символы Unicode. Ваша схема может включать следующие 3 поля:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );

    В этой модели вы только выбираете из [URL]вычисляемого столбца. Для вставки и обновления вы определяете, какое поле использовать, видя, изменяет ли преобразование входящее значение, которое должно иметь NVARCHARтип:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
  3. Вы можете GZIP входящие значения в, VARBINARY(MAX)а затем распаковать на выходе:

    • Для SQL Server 2005 - 2014: вы можете использовать SQLCLR. SQL # (библиотека SQLCLR, которую я написал) поставляется с Util_GZip и Util_GUnzip в бесплатной версии
    • Для SQL Server 2016 и новее: вы можете использовать встроенные функции COMPRESSи DECOMPRESSфункции, которые также являются GZip.
  4. Если вы используете SQL Server 2017 или новее, вы можете сделать таблицу Clustered Columnstore Index.

  5. Хотя этот вариант пока не подходит, в SQL Server 2019 появилась встроенная поддержка UTF-8 в VARCHAR/ CHARdatatypes. В настоящее время в нем слишком много ошибок, чтобы его можно было использовать, но если они исправлены, то это вариант для некоторых сценариев. Пожалуйста, ознакомьтесь с моим постом « Поддержка UTF-8 в SQL Server 2019: Спаситель или Лжепророк? », Где подробно рассматривается новая функция.

Соломон Руцкий
источник
7
Медленный Хлоп Просто поражаюсь, что «всегда используй nvarchar» набрал 140 голосов, а этого нет. Отличная работа на этом посту.
schizoid04
1
@ schizoid04 Спасибо. Честно говоря, принятый ответ был опубликован за 7 лет до моего, поэтому за него проголосовал большой (и / или другие) трафик, который никогда не возвращался для переоценки. Тем не менее, это обеспечивает очень твердый контрапункт теории «мудрости толпы», которая ведет форумы, основанные на голосовании. Там слишком много дезинформации. Например, это на DBA.SE. Другой ответ, принятый до того, как я опубликовал свой, является «правильным» по самым узким определениям, вводящим в заблуждение, и содержит информацию, которую я опровергаю в моем, но все же он опережает мой.
Соломон Руцкий
22

Для вашего приложения подходит nvarchar, потому что размер базы данных невелик. Сказать «всегда используйте nvarchar» - это чрезмерное упрощение. Если вам не нужно хранить такие вещи, как кандзи или другие сумасшедшие персонажи, используйте VARCHAR, это займет гораздо меньше места. Мой предшественник на моей нынешней работе разработал что-то, используя NVARCHAR, когда это было не нужно. Недавно мы переключили его на VARCHAR и сэкономили 15 ГБ только на этой таблице (это было очень записано). Кроме того, если у вас есть индекс для этой таблицы, и вы хотите включить этот столбец или создать составной индекс, вы просто увеличили размер файла индекса.

Просто будьте внимательны в своем решении; в разработке SQL и определениях данных, похоже, редко встречается «ответ по умолчанию» (конечно, кроме обхода курсоров любой ценой).

WebMasterP
источник
10

Поскольку ваше приложение небольшого размера, использование nvarchar по сравнению с varchar существенно не увеличится, и вы избавите себя от возможных головных болей в будущем, если у вас возникнет необходимость в хранении данных Unicode.

tbreffni
источник
8

Вообще говоря; Начните с самого дорогого типа данных, который имеет наименьшие ограничения. Поместите это в производство . Если производительность начинает вызывать проблемы, выясните, что на самом деле хранится в этих nvarcharстолбцах. Есть ли там персонажи, которые не подходят varchar? Если нет, переключитесь на varchar. Не пытайтесь предварительно оптимизировать, пока не узнаете, где боль. Я предполагаю, что выбор между nvarchar / varchar - это не то, что замедлит ваше приложение в обозримом будущем. Будут и другие части приложения, где настройка производительности даст вам гораздо больше прибыли .

Къетил Клауссен
источник
7

За последние несколько лет все наши проекты использовали NVARCHAR для всего, так как все эти проекты многоязычны. Импортированные данные из внешних источников (например, файл ASCII и т. Д.) Преобразуются в Unicode перед вставкой в ​​базу данных.

Мне еще не приходилось сталкиваться с проблемами, связанными с производительностью больших индексов и т. Д. Индексы используют больше памяти, но память дешевая.

Независимо от того, используете ли вы хранимые процедуры или создаете SQL на лету, убедитесь, что все строковые константы имеют префикс N (например, SET @foo = N'Hello world. ';), Поэтому константа также является Unicode. Это исключает любое преобразование строкового типа во время выполнения.

YMMV.

devstuff
источник
4
Вероятно, у вас нет нескольких сотен миллионов записей в таблицах, с которыми вы работаете. Я согласен, что для большинства приложений по умолчанию nvarchar это хорошо, но не все.
Брэндон Мур
7

Я могу говорить по своему опыту, остерегайтесь nvarchar. Если это абсолютно не требуется, этот тип поля данных снижает производительность в большой базе данных. Я унаследовал базу данных, которая вредит производительности и пространству. Мы смогли уменьшить размер базы данных 30 ГБ на 70%! Были сделаны некоторые другие модификации, чтобы помочь с производительностью, но я уверен, что они varcharзначительно помогли с этим. Если ваша база данных имеет потенциал для увеличения таблиц до миллиона записей, держитесь подальше nvarcharлюбой ценой.

JA
источник
4

Я часто занимаюсь этим вопросом на работе:

  • FTP-фиды инвентаря и цены - описания предметов и другой текст были в nvarchar, когда varchar работал нормально. Преобразование их в varchar уменьшило размер файла почти вдвое и действительно помогло с загрузкой.

  • Вышеописанный сценарий работал нормально, пока кто-то не вставил специальный символ в описание предмета (возможно, товарный знак, не помню)

Я до сих пор не использую nvarchar каждый раз над varchar. Если есть какие-либо сомнения или потенциал для специальных символов, я использую nvarchar. Я нахожу, что я использую varchar в основном, когда я на 100% контролирую то, что заполняет поле.

К Ричард
источник
3

Почему во всей этой дискуссии не упоминалось о UTF-8? Возможность хранить полный диапазон символов Юникода не означает, что нужно всегда выделять два байта на символ (или «кодовую точку», чтобы использовать термин UNICODE). Все ASCII - это UTF-8. Проверяет ли SQL Server для полей VARCHAR (), что текст является строгим ASCII (т. Е. Бит нулевого старшего байта)? Я надеюсь, что нет.

Если затем вы хотите хранить Unicode и хотите совместимости со старыми приложениями, поддерживающими только ASCII, я думаю, что использование VARCHAR () и UTF-8 было бы волшебной палочкой: он использует больше места только тогда, когда это необходимо.

Для тех из вас, кто не знаком с UTF-8, могу я порекомендовать учебник для начинающих .

Tevya
источник
2
То, что вы предлагаете, может работать для некоторых приложений, но следует также учитывать влияние дополнительного уровня кодирования на способ обработки текста SQL. В частности, будут производиться сопоставления, поиск и сопоставление с образцом. И если отчеты запускаются для базы данных, стандартные инструменты отчетов не будут корректно взаимодействовать с многобайтовыми символами. И оптовый импорт и экспорт могут быть осуществлены. Я думаю, что в долгосрочной перспективе эта схема может принести больше хлопот, чем она того стоит.
Джеффри Л Уитледж
1
Нельзя хранить UTF-8 в столбцах VARCHAR. MSSQL всегда преобразует ваши данные UTF-8 в параметры сортировки столбцов. Если вы испортите параметры сортировки (например, попытаетесь сохранить CP1252 в Latin_1), преобразование не будет работать, и в результате вы получите дополнительные байты в ваших данных. Может показаться, что он работает нормально, когда вы конвертируете latin_1 в UTF-8 (на стороне приложения) и обратно обратно в latin_1 (на стороне db), но это всего лишь иллюзия. Вы можете подкрасться к БД, автоматически преобразовав ее в сопоставление столбцов, используя freetds и установив для протокола значение меньше 7, но вы потеряете возможность запрашивать nvarchar.
Chugadie
1
@chugadie и Tevya: этот ответ немного бессмысленный. SQL Server использует только UCS-2 / UTF-16 для хранения данных Unicode (то Nесть типов XML и префиксов). У вас нет выбора использования UTF-8. Кроме того, кодировки Unicode (UTF-8, UCS-2 / UTF-16 и UTF-32) не могут быть применены к полям VARCHAR.
Соломон Руцкий
2

Будут исключительные случаи, когда вы захотите сознательно ограничить тип данных, чтобы убедиться, что он не содержит символов из определенного набора. Например, у меня был сценарий, когда мне нужно было сохранить доменное имя в базе данных. Интернационализация доменных имен не была надежной в то время, поэтому было лучше ограничить ввод на базовом уровне и помочь избежать возможных проблем.

Крис Хэлкроу
источник
1

Если вы используете NVARCHARтолько потому, что этого требует системная хранимая процедура, наиболее часто встречающееся явление необъяснимо sp_executesql, а ваш динамический SQL очень длинный, вам лучше с точки зрения производительности выполнять все строковые манипуляции (конкатенация, замена и т. Д.) При VARCHARпоследующем преобразовании конечный результат NVARCHARи ввод его в параметр proc. Так что нет, не всегда пользуюсь NVARCHAR!

ajeh
источник