Лучшие практики оценки длины MySQL VARCHAR

Ваш VARCHAR правильной длины?

Автор: Гуань Юнцян, член команды администраторов баз данных Aikesheng, хорошо владеет навыками эксплуатации и обслуживания MySQL. Он любит изучать новые знания, а также является отаку, который любит играть в игры.

Автор: Ли Фуцян, член команды администраторов баз данных Aikesheng, знакомый с MySQL, TiDB, OceanBase и другими базами данных. Я верю, что если вы продолжите хорошо делать правильные вещи, вы получите разные награды.

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

Эта статья содержит около 2200 слов, и ее чтение займет 8 минут.

Описание фона

Некоторые клиенты сообщили, что увеличили длину поля типа VARCHAR . В первый раз вы можете изменить его быстро, но во второй раз выполнение займет много времени. Я в замешательстве, потому что объем данных в таблице практически одинаковый. Почему VARCHAR(20)корректировка от до VARCHAR(50)происходит быстрее , а VARCHAR(50)от до — долго VARCHAR(100)? Поэтому мы воспроизвели ситуацию и провели анализ проблемы.

экологическая информация

В этой проверке участвуют следующие продукты и информация о версиях:

продукт Версия
MySQL Сервер сообщества MySQL 5.7.25-log (GPL)
Сисбенч сисбенч 1.0.17

Повторение сцены

3.1 Подготовка данных

mysql> show create table test.sbtest1;
+---------+----------------------------------------+
| Table   | Create Table                           |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select count(*) from test.sbtest1;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.10 sec)

3.2 Проверка проблемы

Чтобы смоделировать описание клиента, мы cизменяем поле, VARCHAR(20)изменяем его на VARCHAR(50), а затем изменяем на VARCHAR(100)и наблюдаем за временем, необходимым для его выполнения. Ниже приведены соответствующие команды операций и результаты выполнения:

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+--------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100);
Query OK, 1000000 rows affected (4.80 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+---------------------------+
| Table   | Create Table              |
+---------+---------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

В ходе проверки было обнаружено, что проблема стабильно появляется снова, поэтому мы продолжили попытки изменить ее. Наконец, мы обнаружили, что модификация VARCHAR(63)на 64 занимает много времени VARCHAR(64), но после 64 года мы продолжили расширять длину и обнаружили, что это так. можно было завершить быстро.

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64);
Query OK, 1000000 rows affected (4.87 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+---------------+
| Table   | Create Table  |
+---------+---------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(65);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(66);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

3.3 Анализ проблемы

Проанализируйте ситуацию, когда на VARCHAR(63)изменение требуется много времени. VARCHAR(64)Обратившись к официальной документации , мы обнаружили, что VARCHARтип символов, который может хранить символы, длина которых в байтах равна 1, составляет 0–255. Текущий тип набора символов — UTF8MB4. Поскольку UTF8MB4 — это набор символов с четырехбайтовой кодировкой, то есть длина одного байта может хранить 63,75 (255/4) символов, поэтому, когда мы изменяем его наVARCHAR(63) , нам нужно добавить байт для данных. VARCHAR(64)Для обработки необходимо завершить расширение длины путем создания временной таблицы, поэтому это занимает много времени.

Расширенная проверка

4.1 Подготовка данных

mysql> show create table test_utf8.sbtest1;
+---------+----------------------------------------+
| Table   | Create Table                           |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(20) NOT NULL DEFAULT '',
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+------------------+
1 row in set (0.00 sec)

mysql> select count(*) from test_utf8.sbtest1;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.10 sec)

4.2 Проверка сцены UTF8

Поскольку UTF8 представляет собой трехбайтовый набор символов кодировки, один байт может хранить 85 (255/3=85) символов.

Последовательность этой модификации следующая: VARCHAR(20)→VARCHAR(50)→VARCHAR(85) и обратите внимание на время, необходимое для ее выполнения. Ниже приведены соответствующие команды операций и результаты выполнения:

mysql>  ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(85) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(85) DEFAULT NULL,
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)

Последовательность модификации: VARCHAR(85)→VARCHAR(86)→VARCHAR(100). На этом этапе мы увидим, что выполненный оператор SQL напрямую возвращает ошибку. Поэтому мы удаляем algorithm=inplace ,lock=noneэти два параметра, что позволяет этому SQL создать временную таблицу и заблокировать целевую таблицу, а затем повторно выполнить SQL. Ниже приведены соответствующие команды операций и результаты выполнения:

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86);
Query OK, 1000000 rows affected (4.94 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(86) DEFAULT NULL,
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.3 Проверка сценария UTF8MB4

Поскольку UTF8MB4 представляет собой четырехбайтовый набор символов кодировки, один байт может хранить 63 (255/4=63,75) символа.

Последовательность этой модификации: VARCHAR(20)→VARCHAR(50)→VARCHAR(63) и обратите внимание на время, необходимое для ее выполнения. Ниже приведены соответствующие команды операций и результаты выполнения:

mysql>  ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+-------------------------+
| Table   | Create Table           |
+---------+-------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(63) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)

Последовательность этой модификации: VARCHAR(63)→VARCHAR(64)→VARCHAR(100). На данный момент мы увидим, что выполненный оператор SQL напрямую возвращает ошибку. Поэтому мы удаляем algorithm=inplace, lock=noneэти два параметра, что позволяет этому SQL создать временную таблицу и заблокировать целевую таблицу, а затем повторно выполнить SQL. Ниже приведены соответствующие команды операций и результаты выполнения:

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ;
Query OK, 1000000 rows affected (4.93 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+--------------------------+
| Table   | Create Table             |
+---------+--------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.4 Сравнительный анализ

Изменение длины символов UTF8(МБ3) UTF8MB4
20->50 онлайн-ддл (в месте) онлайн-ддл (в месте)
50->100 онлайн-ддл (копия) онлайн-ддл (копия)
X->Y Если Y*3<256, заменить <br> Если X*3>=256, заменить Если Y*4<256, заменить <br> Когда X*4>=256, заменить
Примечание Один символ занимает максимум 3 байта. Один символ занимает максимум 4 байта.

в заключение

Если максимальная длина поля в байтах >= 256 символов, для представления длины поля необходимы 2 байта.

Пример использования UTF8MB4:

  • Для максимальной длины поля в пределах 256 символов (т. е. x*4<256 и Y*4<256) онлайн-ддл использует режим inplace, который очень эффективен.
  • Для полей, максимальная длина которых превышает 256 символов (то есть x*4>=256 и Y*4>=256), онлайн-ддл использует режим Inplace, который очень эффективен.
  • В противном случае онлайн-ддл использует режим копирования, что неэффективно.
  • То же самое касается UTF8 (MB3).

предположение

Чтобы избежать последующего увеличения длины поля, онлайн-ддл использует неэффективный режим копирования. Рекомендуется:

  • Для типа символов UTF8(MB3):
    • Количество символов меньше 50. Рекомендуется установить длину символа VARCHAR(50) или меньше.
    • Количество символов близко к 84 (256/3=83,33). Рекомендуется установить значение varchar(84) или большую длину символов.
  • Для типа символов UTF8MB4:
    • Если количество символов меньше 50, рекомендуется установить значение VARCHAR(50) или меньшую длину символов.
    • Количество символов близко к 64 (256/4=64). Рекомендуется установить значение VARCHAR(64) или большую длину символов.

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

Дополнительные технические статьи можно найти на странице https://opensource.actionsky.com/ .

О SQLE

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

SQLE получить

тип адрес
Репозиторий https://github.com/actiontech/sqle
документ https://actiontech.github.io/sqle-docs/
выпускать новости https://github.com/actiontech/sqle/releases
Документация по разработке плагина аудита данных https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
Программист, родившийся в 1990-х годах, разработал программу для переноса видео и заработал более 7 миллионов менее чем за год. Концовка была очень суровой! Старшеклассники создают свой собственный язык программирования с открытым исходным кодом в качестве церемонии совершеннолетия – резкие комментарии пользователей сети: Полагаясь на RustDesk из-за повального мошенничества, отечественный сервис Taobao (taobao.com) приостановил внутренние сервисы и возобновил работу по оптимизации веб-версии Java 17 является наиболее часто используемой версией Java LTS. Доля рынка Windows 10 Достигнув 70%, Windows 11 продолжает снижаться. Open Source Daily | Google поддерживает Hongmeng, чтобы взять на себя управление телефонами Rabbit R1 с открытым исходным кодом, поддерживаемыми Docker Microsoft; Electric закрывает открытую платформу Apple выпускает чип M4 Google удаляет универсальное ядро ​​Android (ACK) Поддержка архитектуры RISC-V Юньфэн ушел из Alibaba и планирует в будущем выпускать независимые игры на платформе Windows
{{o.name}}
{{м.имя}}

рекомендация

отmy.oschina.net/actiontechoss/blog/11094958