Каковы основные различия в производительности между типами данных SQL Server varchar и nvarchar?
Я работаю над базой данных для небольшого веб-приложения в моей школе, используя SQL Server 2005
,
Я вижу пару школ мысли по вопросу varchar
против nvarchar
:
- использование
varchar
если вы не имеете дело с большим количеством интернационализированных данных, то используйтеnvarchar
, - Просто используйте
nvarchar
За все.
Я начинаю видеть достоинства представления 2. Я знаю, что nvarchar занимает вдвое больше места, но это не обязательно огромная сделка, так как она собирается хранить данные только для нескольких сотен студентов. Мне кажется, что было бы проще не беспокоиться об этом и просто позволить всему использовать nvarchar. Или мне чего-то не хватает?
14 ответов
Всегда используйте nvarchar.
Возможно, вам никогда не понадобятся двухбайтовые символы для большинства приложений. Однако, если вам требуется поддержка двухбайтовых языков и в вашей схеме базы данных есть только однобайтовая поддержка, очень дорого вернуться и модифицировать свое приложение.
Стоимость переноса одного приложения с varchar на nvarchar будет намного больше, чем немного дополнительного дискового пространства, которое вы будете использовать в большинстве приложений.
Дисковое пространство не проблема... но память и производительность будут. Двойное чтение страниц, двойной размер индекса, странное LIKE и = постоянное поведение и т. Д.
Вам нужно хранить китайский сценарий и т. Д.? Да или нет...
И от MS BOL " Хранение и производительность Unicode"
Редактировать:
Недавний вопрос о том, насколько плохой может быть производительность nvarchar...
SQL Server использует высокий процессор при поиске внутри строк nvarchar
Быть последовательным! Присоединение VARCHAR к NVARCHAR имеет большой успех.
У nvarchar будут значительные затраты памяти, хранилища, рабочего набора и индексации, поэтому, если спецификации требуют, что это действительно никогда не понадобится, не беспокойтесь.
У меня не было бы жесткого и быстрого правила "всегда nvarchar", потому что оно может быть полной тратой во многих ситуациях - особенно ETL из ASCII/EBCDIC или идентификаторы и столбцы кода, которые часто являются ключами и внешними ключами.
С другой стороны, есть много случаев столбцов, где я обязательно задам этот вопрос рано, и если я не получу точный и быстрый ответ сразу, я сделаю столбец nvarchar.
Я не решаюсь добавить еще один ответ, поскольку их уже немало, но необходимо сделать несколько замечаний, которые либо не были сделаны, либо не были четко сформулированы.
Первое: не всегда используйте 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
так как оба из них могут теперь содержать символы Unicode. - и так далее....
Четвертое: теперь, когда у вас есть NVARCHAR
данные занимают вдвое больше места, чем нужно для данных, которые хорошо вписываются в VARCHAR
("хорошо вписывается" = не превращается в "?") и каким-то образом, как по волшебству, приложение действительно выросло, и теперь есть миллионы записей по крайней мере в одном из этих полей, где большинство строк являются стандартными ASCII, но некоторые содержат Unicode символы, поэтому вы должны сохранить NVARCHAR
Учтите следующее:
Если вы используете SQL Server 2008 - 2016 RTM и используете Enterprise Edition, ИЛИ если используете SQL Server 2016 с пакетом обновления 1 (который сделал сжатие данных доступным во всех выпусках) или новее, то вы можете включить сжатие данных. Сжатие данных может (но не всегда) сжимать данные Unicode в
NCHAR
а такжеNVARCHAR
поля. Определяющими факторами являются:NCHAR(1 - 4000)
а такжеNVARCHAR(1 - 4000)
используйте стандартную схему сжатия для Unicode, но только начиная с SQL Server 2008 R2, и только для данных IN ROW, а не OVERFLOW! Это выглядит лучше, чем обычный алгоритм сжатия ROW / PAGE.NVARCHAR(MAX)
а такжеXML
(и я думаю, такжеVARBINARY(MAX)
,TEXT
, а такжеNTEXT
) данные, которые находятся в строке (не в строке на страницах больших или больших страниц), по крайней мере, могут быть сжаты на странице, но не сжаты в строке. Конечно, сжатие PAGE зависит от размера значения в строке: я проверил с помощью VARCHAR(MAX) и увидел, что 6000 строк символов / байтов не будут сжиматься, но 4000 строк символов / байтов сделали.- Любые данные OFF ROW, LOB или OVERLOW = Нет сжатия для вас!
Если вы используете 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) );
Вы можете GZIP входящие значения в
VARBINARY(MAX)
и затем разархивируйте на выходе:- Для SQL Server 2005 - 2014: вы можете использовать SQLCLR. SQL # (библиотека SQLCLR, которую я написал) поставляется с Util_GZip и Util_GUnzip в бесплатной версии
- Для SQL Server 2016 и новее: вы можете использовать встроенный
COMPRESS
а такжеDECOMPRESS
функции, которые также являются GZip.
Если вы используете SQL Server 2017 или новее, вы можете сделать таблицу Clustered Columnstore Index.
Хотя этот вариант пока не подходит, в SQL Server 2019 появилась встроенная поддержка UTF-8 в
VARCHAR
/CHAR
типы данных. В настоящее время в нем слишком много ошибок, чтобы его можно было использовать, но если они исправлены, то это вариант для некоторых сценариев. Пожалуйста, ознакомьтесь с моим постом " Поддержка UTF-8 в SQL Server 2019: Спаситель или Лжепророк? " Для подробного анализа этой новой функции.
Для вашего приложения подходит nvarchar, потому что размер базы данных невелик. Сказать "всегда используйте nvarchar" - это чрезмерное упрощение. Если вам не нужно хранить такие вещи, как кандзи или другие сумасшедшие персонажи, используйте VARCHAR, это займет намного меньше места. Мой предшественник на моей нынешней работе разработал что-то, используя NVARCHAR, когда это было не нужно. Недавно мы переключили его на VARCHAR и сэкономили 15 ГБ только на этой таблице (это было очень записано). Кроме того, если у вас есть индекс для этой таблицы, и вы хотите включить этот столбец или создать составной индекс, вы просто увеличили размер файла индекса.
Просто будьте внимательны в своем решении; в разработке SQL и определениях данных, похоже, редко встречается "ответ по умолчанию" (конечно, кроме обхода курсоров любой ценой).
Поскольку ваше приложение небольшого размера, использование nvarchar по сравнению с varchar существенно не увеличится, и вы избавите себя от потенциальных головных болей в будущем, если у вас возникнет необходимость в хранении данных Unicode.
Вообще говоря; Начните с самого дорогого типа данных, который имеет наименьшие ограничения. Поместите это в производство. Если производительность начинает создавать проблемы, выясните, что на самом деле хранится в этих nvarchar
колонны. Есть ли там персонажи, которые не вписываются в varchar
? Если нет, переключитесь на varchar. Не пытайтесь предварительно оптимизировать, пока не узнаете, где боль. Я предполагаю, что выбор между nvarchar/varchar - это не то, что замедлит ваше приложение в обозримом будущем. Будут и другие части приложения, где настройка производительности даст вам гораздо больше прибыли.
Я могу говорить по собственному опыту, остерегайтесь nvarchar
, Если это абсолютно не требуется, этот тип поля данных снижает производительность в большой базе данных. Я унаследовал базу данных, которая вредит производительности и пространству. Мы смогли уменьшить размер базы данных 30 ГБ на 70%! Были сделаны некоторые другие модификации, чтобы помочь с производительностью, но я уверен, что varchar
С этим тоже очень помогли. Если в вашей базе данных есть потенциал для увеличения таблиц до миллиона записей, держитесь подальше от nvarchar
любой ценой.
За последние несколько лет все наши проекты использовали NVARCHAR для всего, так как все эти проекты многоязычны. Импортированные данные из внешних источников (например, ASCII-файл и т. Д.) Преобразуются в Unicode перед вставкой в базу данных.
Мне еще не приходилось сталкиваться с проблемами, связанными с производительностью больших индексов и т. Д. Индексы действительно используют больше памяти, но память дешевая.
Независимо от того, используете ли вы хранимые процедуры или создаете SQL на лету, убедитесь, что все строковые константы имеют префикс N (например, SET @foo = N'Hello world.';), Поэтому константа также является Unicode. Это исключает любое преобразование строкового типа во время выполнения.
YMMV.
Я часто занимаюсь этим вопросом на работе:
FTP-фиды инвентаря и цены - описания предметов и другой текст были в nvarchar, когда varchar работал нормально. Преобразование их в varchar уменьшило размер файла почти вдвое и действительно помогло с загрузкой.
Вышеописанный сценарий работал нормально, пока кто-то не вставил специальный символ в описание предмета (возможно, товарный знак, не помню)
Я до сих пор не использую nvarchar каждый раз над varchar. Если есть какие-либо сомнения или потенциал для специальных символов, я использую nvarchar. Я нахожу, что я использую varchar в основном, когда я на 100% контролирую то, что заполняет поле.
Почему во всей этой дискуссии не было упоминания о UTF-8? Возможность хранить полный диапазон символов Юникода не означает, что нужно всегда выделять два байта на символ (или "кодовую точку", чтобы использовать термин UNICODE). Все ASCII - это UTF-8. Проверяет ли SQL Server для полей VARCHAR(), что текст является строгим ASCII (т. Е. Верхний байт равен нулю)? Я надеюсь, что нет.
Если затем вы хотите сохранить Unicode и хотите совместимости со старыми приложениями, поддерживающими только ASCII, я думаю, что использование VARCHAR() и UTF-8 было бы волшебной палочкой: он использует больше места только тогда, когда это необходимо.
Для тех из вас, кто не знаком с UTF-8, могу я порекомендовать учебник для начинающих.
Будут исключительные случаи, когда вы захотите сознательно ограничить тип данных, чтобы убедиться, что он не содержит символов из определенного набора. Например, у меня был сценарий, когда мне нужно было сохранить доменное имя в базе данных. Интернационализация доменных имен не была надежной в то время, поэтому было лучше ограничить ввод на базовом уровне и помочь избежать возможных проблем.
Если вы используете NVARCHAR
просто потому, что этого требует системная хранимая процедура, наиболее частым случаем является необъяснимое sp_executesql
и ваш динамический SQL очень длинный, вам лучше с точки зрения производительности делать все строковые манипуляции (конкатенация, замена и т. д.) в VARCHAR
затем преобразовать конечный результат в NVARCHAR
и вводить его в параметр proc. Так что нет, не всегда использовать NVARCHAR
!