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

StackOverflow https://stackoverflow.com/questions/35366

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

Проблема не в дисковом пространстве...но память и производительность будут.Удвоение количества прочитанных страниц, удвоение размера индекса, странное поведение ТИПА и = constant и т. Д

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

И от госпожи БОЛ " .Влияние Unicode на память и производительность"

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

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

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

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

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

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

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

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

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

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

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

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

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

  • размер файлов журнала транзакций
  • время, необходимое для репликации (при использовании репликации)
  • время, необходимое для ETL (если ETLing)
  • время, необходимое для отправки журналов в удаленную систему и восстановления (при использовании доставки журналов)
  • размер резервных копий
  • время, необходимое для завершения резервного копирования
  • время, необходимое для восстановления (когда-нибудь это может оказаться важным ;-)
  • размер, необходимый для базы данных tempdb
  • выполнение триггеров (для вставленных и удаленных таблиц, которые хранятся в базе данных tempdb)
  • производительность управления версиями строк (при использовании ИЗОЛЯЦИИ МОМЕНТАЛЬНЫХ СНИМКОВ, поскольку хранилище версий находится в базе данных tempdb)
  • возможность получить новое дисковое пространство, когда финансовый директор говорит, что они только что потратили 1 миллион долларов на SAN в прошлом году и поэтому не будут выделять еще 250 тысяч долларов на дополнительное хранилище
  • продолжительность времени, необходимого для выполнения операций ВСТАВКИ и ОБНОВЛЕНИЯ
  • продолжительность времени, необходимого для обслуживания индекса
  • и т.д., и т.п., и т.п.

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

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

  • Поля для имен студентов и / или преподавателей должны содержать вероятно быть 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, или если используется SQL Server 2016 с пакетом обновления 1 (который сделал сжатие данных доступным во всех выпусках) или новее, то вы можете включить Сжатие данных.Сжатие данных может (но не "всегда") сжимать данные в формате 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 или RTM 2008-2016 и нет в версии Enterprise у вас может быть два поля:один VARCHAR и один NVARCHAR.Например, допустим, вы храните URL-адреса, которые в основном состоят из базовых символов ASCII (значения 0 - 127) и, следовательно, вписываются в VARCHAR, но иногда содержат символы Юникода.Ваша схема может включать следующие 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. Вы можете архивировать входящие значения в 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 типы данных.В настоящее время в нем слишком много ошибок, чтобы его можно было использовать, но если они исправлены, то это вариант для некоторые сценарии.Пожалуйста, посмотрите мой пост ".Встроенная поддержка UTF-8 в SQL Server 2019:Спаситель или Лжепророк?", для подробного анализа этой новой функции.

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

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

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

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

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

ИММВ.

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

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

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

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

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

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

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

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

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

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

scroll top