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

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

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

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

14 ответов


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

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

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


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

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

и от MS BOL"эффекты хранения и производительности Unicode"

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

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

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


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


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

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

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


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

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


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

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

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

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

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

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

третий: в то время как некоторые ответы неправильно фокусируются на аспекте "это небольшое приложение", а некоторые правильно предлагают "использовать то, что подходит", ни один из ответов не предоставил реального руководства O. P. важная деталь, упомянутая в вопросе, заключается в том, что это веб-страница для их школы. Здорово! Поэтому мы можем предложить следующее:

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

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

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

    1. NCHAR(1 - 4000) и NVARCHAR(1 - 4000) использовать стандартная схема сжатия для Unicode, но только начиная с SQL Server 2008 R2, и только для данных в строке, а не переполнения! Это кажется лучше, чем обычное сжатие строк / страниц алгоритм.
    2. NVARCHAR(MAX) и XML (и я думаю также VARBINARY(MAX), TEXT и NTEXT) данные, которые находятся в строке (не в строке в LOB или переполнении страниц), могут быть сжаты, но не строки сжаты. Конечно, сжатие страницы зависит от размера значения в строке: я тестировал с помощью VARCHAR(MAX) и увидел, что 6000 символьных/байтовых строк не сжимаются, но 4000 символьных/байтовых строк.
    3. любые данные строки, LOB или OVERLOW = нет сжатия для Ты!
  2. при использовании SQL Server 2005 или 2008-2016 RTM и не в 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 или новее, вы можете посмотреть, как сделать таблицу кластеризованным индексом Columnstore.

  5. хотя это еще не жизнеспособный вариант, SQL Server 2019 вводит встроенную поддержку UTF-8 в VARCHAR / CHAR типы данных. Там в настоящее время слишком много ошибок с ним для его использования, но если они исправлены, то это опция для некоторые сценарии. Пожалуйста, посмотрите мой пост".--172-->собственная поддержка UTF-8 в SQL Server 2019: спаситель или лжепророк?", для детального анализа этой новой функции.


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


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

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

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

YMMV.


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


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


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

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

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

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


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

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

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


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


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