Ваш 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 |