Есть ли недостатки в использовании универсальной переменной varchar (255) для всех текстовых полей?


100

У меня есть contactsтаблица , которая содержит такие поля, как postcode, first name, last name, town, country, и phone numberт.д., все из которых определены как VARCHAR(255)даже если ни один из этих полей не будет когда - нибудь близко к тому 255 символов. (Если вам интересно, это так, потому что миграции Ruby on Rails сопоставляют поля String VARCHAR(255)по умолчанию, и я никогда не удосужился переопределить это).

Поскольку VARCHAR будет хранить только количество фактических символов поля (вместе с длиной поля), есть ли какое-либо явное преимущество (производительность или иное) в использовании, скажем, VARCHAR(16)сверх VARCHAR(255)?

Кроме того, большинство этих полей имеют индексы. Влияет ли вообще больший размер VARCHAR в поле на размер или производительность индекса?

К вашему сведению, я использую MySQL 5.


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

1
@Gili удалил мой комментарий, поскольку OP, по-видимому, изменил свое согласие. Хорошие моменты, в будущем я укажу, о каком ответе говорю и почему.
ceejayoz

Некоторые другие ответы на этот повторяющийся вопрос, stackoverflow.com/questions/1262174/…
Джеймс МакМахон

Ответы:


129

В хранилище VARCHAR(255)достаточно умен, чтобы хранить только ту длину, которая вам нужна в данной строке, в отличие от CHAR(255)которой всегда хранится 255 символов.

Но поскольку вы пометили этот вопрос с помощью MySQL, я упомяну совет, относящийся к MySQL: по мере того, как строки копируются с уровня механизма хранения на уровень SQL, VARCHARполя преобразуются в, CHARчтобы получить преимущество работы со строками фиксированной ширины. Таким образом, строки в памяти заполняются до максимальной длины объявленного VARCHARстолбца.

Когда ваш запрос неявно создает временную таблицу, например, при сортировке или GROUP BY, это может использовать много памяти. Если вы используете много VARCHAR(255)полей для данных, которые не должны быть такими длинными, временная таблица может стать очень большой.

Вам также может быть интересно узнать, что такое поведение «заполнения» означает, что строка, объявленная с набором символов utf8, дополняется до трех байтов на символ даже для строк, которые вы храните с однобайтовым содержимым (например, символы ascii или latin1). И аналогично набор символов utf8mb4 заставляет строку заполнять до четырех байтов на символ в памяти.

Таким образом, VARCHAR(255)в utf8 для хранения короткой строки, такой как «Нет мнения», требуется 11 байт на диске (десять символов нижнего набора символов плюс один байт для длины), но он занимает 765 байт в памяти и, следовательно, во временных таблицах или отсортированных результатах.

Я помогал пользователям MySQL, которые по незнанию часто создавали временные таблицы объемом 1,5 ГБ и заполняли свое дисковое пространство. У них было много VARCHAR(255)столбцов, в которых на практике хранились очень короткие строки.

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

Конечно, трудно определить самый длинный почтовый адрес, поэтому многие люди выбирают длинный, VARCHARкоторый определенно длиннее любого адреса. А 255 является обычным, потому что это максимальная длина, VARCHARдля которой длина может быть закодирована одним байтом. Это также была максимальная VARCHARдлина в MySQL старше 5.0.


6
Я всегда думал, 255что длина строки может уместиться в один байт
BlueRaja - Дэнни Пфлугофт

3
@BlueRaja: Вероятно, это было верно для баз данных, внутренняя файловая структура которых закодировала длину строки в один байт, или если они закодировали короткие строки в одном байте. Но это уже не так для большинства баз данных.
Билл Карвин,

7
@BlueRaja: InnoDB не хранит длину следующей переменной varchar, он хранит серию смещений полей для всех полей в строке. Эти смещения полей могут составлять 1 байт, если общий размер строки меньше 127 байтов, или 2 байта. См. Forge.mysql.com/wiki/MySQL_Internals_InnoDB
Билл Карвин,

6
@BlueRaja: MyISAM (для тех, кто все еще использует) хранит длины varchar, и они могут храниться в 1 или 2 байтах. Однако: «При отправке ключа обработчику для index_read () или records_in_range мы всегда используем 2-байтовую длину для VARCHAR, чтобы упростить задачу». См. Forge.mysql.com/wiki/MySQL_Internals_MyISAM
Билл Карвин,

1
один вопрос - сортировка и группировка по любому полю или самому полю varchar?
Рохит Банга

24

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

Определение максимальных пределов для строк - это хорошая вещь, которую нужно сделать, чтобы предотвратить попадание в СУБД более длинных, чем ожидалось, строк и последующего переполнения буфера или исключений / ошибок при извлечении и анализе значений из базы данных, которые длиннее (больше байтов), чем ожидалось.

Например, если у вас есть поле, которое принимает двухсимвольные строки для сокращений стран, у вас нет никаких причин ожидать, что ваши пользователи (в данном контексте программисты) будут вводить полные названия стран. Поскольку вы не хотите, чтобы они вводили "Антигуа и Барбуда" (AG) или "Остров Херд и острова Макдоналда" (HM), вы не разрешаете это на уровне базы данных. Кроме того, вполне вероятно, что некоторые программисты еще не ознакомились с проектной документацией ( которая, безусловно, существует ), чтобы знать, что этого не следует делать.

Настройте поле так, чтобы оно принимало два символа, и позвольте СУБД обрабатывать их (либо аккуратно, усекая, либо неуклюже, отклоняя их SQL с ошибкой).

Примеры реальных данных, у которых нет причин превышать определенную длину:

  • Канадские почтовые индексы имеют формат A1A1A1 и всегда имеют длину 6 символов, даже для Санта-Клауса (6 символов не включают пробел, который можно указать для удобочитаемости).
  • адреса электронной почты - до 64 байт до @, до 255 байт после. Никогда больше, чтобы не сломать Интернет.
  • Номера телефонов в Северной Америке никогда не содержат более 10 цифр (за исключением кода страны).
  • Компьютеры под управлением (последние версии) Windows не могут иметь имена компьютеров длиннее 63 байтов , хотя более 15 не рекомендуется, так как это приведет к поломке вашей фермы серверов Windows NT.
  • Аббревиатуры штатов состоят из 2 символов (например, коды стран, приведенные выше).
  • Номера для отслеживания UPS состоят из 18, 12, 11 или 9 символов. 18-значные номера начинаются с «1Z», а 11-значные номера начинаются с «T», что заставляет задуматься, как они доставляют все эти пакеты, если они не знают разницы между буквами и цифрами.

И так далее...

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

Используя varchar (n) вместо varchar (255), вы устраняете проблему, когда пользователи (конечные пользователи, программисты, другие программы) вводят неожиданно длинные данные , которые позже будут преследовать ваш код.

И я не говорил, что вам не следует также применять это ограничение в коде бизнес-логики, используемом вашим приложением.


5
Канадские почтовые индексы на самом деле состоят из 7 цифр, пробел в середине важен, и его следует указывать на почтовых этикетках. В телефонных номерах Северной Америки может быть более 10 цифр, если есть расширение. Если вы не можете хранить добавочные номера телефонов, то 10 цифр - это нормально, но вы, вероятно, пожалеете об этом.
Кибби

3
Определенно есть основания для ограничения целостности данных. Тем не менее, все еще легко быть слишком строгим. Установите ограничения на данные, которые вы контролируете, и наложите разумные ограничения на требования к данным, которые вы не можете контролировать. Ограничения по вашему номеру телефона и электронной почте нормальны (при условии, что вы никогда не выходите на международный уровень). Ваше требование, которое гласит, что усечение двухсимвольного кода страны является «изящным», - безумие. Вы знаете, что произошла ошибка, не усекайте и не принимайте. Если вы усечете, очень высока вероятность того, что вы получите неправильный код страны.
coderjoe

В большинстве приложений перед отправкой в ​​базу данных выполняется проверка данных ...
Кобби

2
Конечно. Наиболее. Но я чувствую, что здесь вы предполагаете, что разработчик, который разрабатывает новое приложение для существующей базы данных, знает об ограничениях на данные (мы не все эксперты по каждому типу данных и тому, как это реализовано в каждой базе данных. ). То, что вы можете проверять данные в своем приложении, не означает, что вы это сделали.
shufler

3
the design documentation (which surely exists)Ха. : D
Камило Мартин

14

Я с тобой. Суетливое внимание к деталям - боль в шее, и ценность ее ограничена.

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

Если вы используете только поля типа CHAR, вы можете получить строки фиксированной длины. Это может сэкономить немного времени на перезагрузке диска, если вы выбрали точные размеры полей. Вы можете получить более плотно упакованные данные (меньше операций ввода-вывода для сканирования таблиц) и более быстрые обновления (легче найти открытые места в блоке для обновлений и вставок).

Однако, если вы переоцениваете свои размеры или ваши фактические размеры данных изменчивы, вы будете тратить пространство впустую на поля CHAR. Данные будут упакованы менее плотно (что приведет к большему количеству операций ввода-вывода для больших объемов извлечения).

Как правило, выигрыш в производительности от попытки установить размер для переменных полей незначителен. Вы можете легко выполнить сравнительный анализ, используя VARCHAR (255) по сравнению с CHAR (x), чтобы увидеть, можете ли вы измерить разницу.

Однако иногда мне нужно дать «маленький», «средний», «большой» намек. Поэтому я использую 16, 64 и 255 для размеров.


13

В настоящее время я не могу представить, что это действительно имеет значение.

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

Теперь сложность полей varchar заключается в том, что вы не можете легко найти запись по ее номеру записи. Когда у вас есть размер строки фиксированной длины (с полями фиксированной длины), тривиально вычислить дисковый блок, на который указывает идентификатор строки. Благодаря размеру строк переменной длины такое просто невозможно.

Итак, теперь вам нужно поддерживать какой-то индекс номера записи, как и любой другой первичный ключ, ИЛИ вам нужно создать надежный идентификатор строки, который кодирует детали (например, блок и т. Д.) В идентификаторе. Однако, если вы это сделаете, идентификатор придется пересчитывать, если когда-либо строка перемещается в постоянное хранилище. Ничего страшного, просто нужно переписать все записи индекса и убедиться, что вы либо а) никогда не открываете его потребителю, либо б) никогда не утверждаете, что число является надежным.

Но поскольку сегодня у нас есть поля varchar, единственное значение varchar (16) по сравнению с varchar (255) состоит в том, что БД будет применять ограничение в 16 символов для varchar (16). Если предполагается, что модель БД действительно представляет физическую модель данных, то длина полей может иметь значение. Если, однако, это просто «хранилище», а не «модель И хранилище», в этом нет никакой необходимости.

Затем вам просто нужно различать текстовое поле, которое является индексируемым (например, varchar), и тем, что нет (например, текстовое поле или поле CLOB). Индексируемые поля, как правило, имеют ограничение по размеру для облегчения индексации, тогда как поля CLOB - нет (в разумных пределах).


5

По моему опыту, если вы разрешите тип данных из 255 символов, какой-нибудь глупый пользователь (или какой-нибудь опытный тестировщик) фактически заполнит его.

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

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


0

Хорошая практика - выделять немного больше того, что вам нужно. Телефонные номера никогда бы не стали такими большими.

Одна из причин заключается в том, что, если вы не проверяете большие записи, несомненно, кто-то будет использовать все, что есть. Тогда в вашем ряду может закончиться место. Я не уверен в ограничении MySQL, но 8060 - это максимальный размер строки в MS SQL.

Более нормальным значением по умолчанию будет 50 imho, а затем увеличьте там, где это необходимо.


Спасибо. Я определенно согласен с тем, что это хорошая практика. Это аспект производительности, который мне бы очень хотелось прояснить
Олли

0

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

Это означает, что при добавлении индекса для нескольких столбцов varchar 255 вы можете довольно быстро / даже быстрее достичь этого предела для столбцов utf8 или utf8mb4, как указано в ответах выше

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.