Який вплив на ефективність використання CHAR проти VARCHAR на полі фіксованого розміру?


58

У мене індексований стовпчик, який зберігає хеш MD5. Таким чином, стовпець завжди буде зберігати значення 32 символів. З будь-якої причини це було створено як варчар, а не чарівник. Чи варто клопотати з переміщенням бази даних, щоб перетворити її на таблицю? Це в MySQL 5.0 з InnoDB.


6
ПОПЕРЕДЖЕННЯ Це питання та його відповіді були написані перед InnoDB, а за умовчанням - utf8.
Рік Джеймс

Відповіді:


56

Подібне питання задавали і раніше

Наслідки для продуктивності розмірів MySQL VARCHAR

Ось уривок моєї відповіді

Ви повинні усвідомити компроміси використання CHAR проти VARCHAR

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

З полями VARCHAR ви отримуєте зовсім іншу історію. Наприклад, VARCHAR (15) насправді динамічно виділяє до 16 байт, до 15 для даних і, щонайменше, 1 додатковий байт для збереження довжини даних. Якщо у вас є рядок "привіт", який займе 6 байтів, а не 5. Маніпуляція з рядками завжди повинна виконувати певну форму перевірки довжини у всіх випадках.

Компроміс є більш очевидним, коли ви виконуєте дві речі: 1. Зберігання мільйонів чи мільярдів рядків 2. Індексація стовпців, що є або CHAR або VARCHAR

TRADEOFF №1 Очевидно, VARCHAR має перевагу, оскільки дані змінної довжини створюватимуть менші рядки і, таким чином, менші фізичні файли.

TRADEOFF # 2 Оскільки поля CHAR потребують меншої маніпуляції з рядками через фіксовану ширину поля, пошук індексів щодо поля CHAR в середньому на 20% швидший, ніж для полів VARCHAR. Це не будь-яка здогадка з мого боку. Книга Дизайн та настройка баз даних MySQL виконала щось чудове на таблиці MyISAM, щоб довести це. Приклад у книзі зробив щось подібне:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Ця директива змушує всіх VARCHAR поводитися як CHAR. Я робив це на своїй попередній роботі ще в 2007 році, і взяв таблицю на 300 ГБ і збільшив пошукові показники на 20%, не змінюючи нічого іншого. Це працювало як опубліковано. Однак вона створила таблицю майже вдвічі більше, але це просто повертається до компромісу №1.

Ви можете проаналізувати дані, що зберігаються, щоб побачити, що рекомендує MySQL для визначення стовпців. Просто запустіть наступне проти будь-якої таблиці:

SELECT * FROM tblname PROCEDURE ANALYSE();

Це дозволить пройти всю таблицю та рекомендувати визначення стовпців для кожного стовпця на основі даних, які він містить, мінімальних значень поля, максимальних значень поля тощо. Іноді просто потрібно використовувати здоровий глузд при плануванні CHAR vs VARCHAR. Ось хороший приклад:

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

Проблеми CHAR проти VARCHAR можна вирішити лише за допомогою належного планування. З великою силою приходить велика відповідальність (кліше, але правда).

ОНОВЛЕННЯ

Що стосується MD5, то обчислення strlenвнутрішньо слід виключити при переключенні всього формату рядків. Не потрібно було б змінювати визначення поля.

Якщо ключ MD5 є єдиним присутнім VARCHAR, я б пішов на нього і перетворив формат рядка таблиці у фіксований . Якщо є значна кількість інших полів VARCHAR, вони також отримають користь. В обмін на це таблиця розшириться приблизно вдвічі більше її розміру. Але запити повинні прискорюватися приблизно на 20% більше без додаткової настройки.


1
Думаю, я використовував би знак char (4) або щось на кшталт непідписаного цілого числа для IP-адреси
Джек Дуглас

@JackPDouglas Ви в цьому правильні.
RolandoMySQLDBA

Чи індекси не зберігаються з фіксованою довжиною? Я не розумію, як змінити формат зберігання на покращену кількість покажчиків індексу. Ви маєте на увазі покращення сканування таблиці?
Маркус Адамс

1
@JackDouglas, чому б bitі ні binary?
Печер'є

@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 |

db <> скрипка тут


Хороший дзвінок про перехід на бінарний.
RThomas

Я планую перетворити це на бінарне. Тепер, коли я замислююся над цим, розмір не повинен відрізнятись лише від того, чи використовую я байт чи знак, оскільки наше кодування - utf-8. Або я помиляюся?
Джейсон Бейкер

@Jason - кодування не поширюється на 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 "


Це "прискорення" стосується MyISAM, а не InnoDB.
Рік Джеймс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.