Как влияет на производительность использование CHAR против VARCHAR в поле фиксированного размера?

58

У меня есть индексированный столбец, в котором хранится хэш MD5. Таким образом, столбец всегда будет хранить 32-символьное значение. По какой-то причине, он был создан как varchar, а не как char. Стоит ли переносить базу данных, чтобы преобразовать ее в символ? Это в MySQL 5.0 с InnoDB.

Джейсон Бейкер
источник
6
ВНИМАНИЕ! Этот вопрос и его ответы были написаны до того, как InnoDB и utf8 стали значениями по умолчанию.
Рик Джеймс

Ответы:

56

Подобный вопрос был задан ранее

Влияние на производительность размеров MySQL VARCHAR

Вот выдержка из моего ответа

Вы должны понять компромиссы использования CHAR против VARCHAR

С полями CHAR вы выделяете именно то, что получаете. Например, CHAR (15) выделяет и хранит 15 байтов, независимо от того, какие символы вы размещаете в поле. Работа со строками проста и понятна, поскольку размер поля данных полностью предсказуем.

С полями VARCHAR вы получите совершенно другую историю. Например, VARCHAR (15) фактически выделяет динамически до 16 байтов, до 15 для данных и, по крайней мере, 1 дополнительный байт для хранения длины данных. Если у вас есть строка 'hello' для хранения, которая будет занимать 6 байтов, а не 5. Во всех случаях при обработке строк всегда должна выполняться проверка длины.

Компромисс становится более очевидным, когда вы делаете две вещи: 1. Хранение миллионов или миллиардов строк. 2. Индексирование столбцов, которые являются CHAR или VARCHAR.

TRADEOFF # 1 Очевидно, что VARCHAR обладает преимуществом, поскольку данные переменной длины будут создавать меньшие строки и, следовательно, меньшие физические файлы.

TRADEOFF # 2 Поскольку поля CHAR требуют меньших манипуляций со строками из-за фиксированной ширины поля, поиск индекса по полю CHAR в среднем на 20% быстрее, чем поиск полей VARCHAR. Это не какая-то догадка с моей стороны. Книга MySQL Database Design and Tuning провела нечто удивительное на столе MyISAM, чтобы доказать это. Пример в книге сделал что-то вроде следующего:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Эта директива заставляет все VARCHAR вести себя как CHAR. Я сделал это на своей предыдущей работе в 2007 году, взял таблицу на 300 ГБ и ускорил поиск индекса на 20%, не меняя ничего другого. Он работал как опубликовано. Тем не менее, он дал таблицу почти в два раза больше, но это просто возвращает к компромиссу № 1.

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

SELECT * FROM tblname PROCEDURE ANALYSE();

Это будет проходить по всей таблице и рекомендовать определения столбцов для каждого столбца на основе содержащихся в нем данных, минимальных значений полей, максимальных значений полей и т. Д. Иногда вам просто нужно руководствоваться здравым смыслом при планировании CHAR против VARCHAR. Вот хороший пример:

Если вы храните IP-адреса, маска для такого столбца должна содержать не более 15 символов (xxx.xxx.xxx.xxx). Я бы сразу CHAR(15)понял, потому что длины IP-адресов не будут сильно меняться, а сложность манипулирования строками будет контролироваться дополнительным байтом. Вы все еще можете сделать PROCEDURE ANALYSE()против такой колонны. Может даже порекомендовать VARCHAR. В этом случае мои деньги все еще будут на CHAR, а не на VARCHAR.

Проблемы CHAR и VARCHAR могут быть решены только путем правильного планирования. С большой силой приходит большая ответственность (клише, но это правда).

ОБНОВИТЬ

Когда дело доходит до MD5, strlenвнутреннее вычисление должно быть исключено при переключении всего формата строки. Там не будет необходимости изменять определение поля.

Если ключ MD5 - единственный присутствующий VARCHAR, я бы пошел на него и преобразовал формат строки таблицы в фиксированный . Если имеется большое количество других полей VARCHAR, они также выиграют. В обмен на это таблица расширилась бы примерно вдвое. Но запросы должны ускоряться примерно на 20% больше без дополнительной настройки.

RolandoMySQLDBA
источник
1
Я думаю, что я использовал бы char (4) или что-то вроде целого числа без знака для IP-адреса
Джек Дуглас
@JackPDouglas Вы правы в этом вопросе.
RolandoMySQLDBA
Разве индексы не хранятся с фиксированной длиной? Я не понимаю, как изменение формата хранения на фиксированную длину улучшило поиск по индексу. Вы имеете в виду, что это улучшило сканирование таблицы?
Маркус Адамс
1
@JackDouglas, а почему бы bitи нет binary?
Pacerier
@Pacerier, что было бы лучше, я согласен :)
Джек Дуглас
19

Похоже, вы сэкономите 1 байт на значение или около 3%, преобразовав в a char. Вероятно, это того не стоит, если вы все равно храните MD5 в гексе - вы можете сэкономить 50%, используя binaryвместо этого.

Спасибо Ovais (см. Комментарии) за то, что он указал, что char(32)может использовать намного больше, чем 32 байта, если вы используете многобайтовый набор символов.

Спасибо Рику Джеймсу за указание на то, что вы должны использовать unhexфункцию для преобразования шестнадцатеричной строки в двоичную:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| длина (бар) |
| ----------: |
| 32 |
| 16 |

дБ <> скрипка здесь

Джек Дуглас
источник
Хороший призыв к переходу на бинарный.
RThomas
Я планирую преобразовать это в двоичный файл. Теперь, когда я думаю об этом, размер не должен быть разным, только в зависимости от того, использую ли я байт или символ, поскольку наша кодировка - utf-8. Или я не прав?
Джейсон Бейкер
@ Джейсон - кодировка не применяется binary- или я неправильно понял?
Джек Дуглас
3
для столбца char (32) с набором символов utf-8 каждому значению потребуется 32x3 байта для хранения. Зачем вам нужно установить значение хеша MD5 равным utf-8. Преобразование в двоичный код (32) потребует 32 байта на значение.
ovais.tariq
1
Переход на BINARYочень мало, если вы также не используете UNHEX(). То есть, вы можете хранить UNHEX(MD5(x))в 16 байт BINARY(16)значительно сэкономить пространство над хранением MD5(x)в CHAR(32) CHARACTER SET ascii.
Рик Джеймс
15

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

http://dev.mysql.com/doc/refman/5.0/en/char.html

Также обратите внимание на первый комментарий к документации, на которую я ссылаюсь выше ... «CHAR только ускорит ваш доступ, если вся запись имеет фиксированный размер. То есть, если вы используете какой-либо объект переменного размера, вы можете также сделать все из них». переменный размер. Вы не получаете скорости, используя CHAR в таблице, которая также содержит VARCHAR "

RThomas
источник
Это «ускорение» относится к MyISAM, а не к InnoDB.
Рик Джеймс