O seu VARCHAR tem o comprimento certo?
Autor: Guan Yongqiang, membro da equipe Aikesheng DBA, tem boas habilidades de operação e manutenção de MySQL. Ele adora aprender novos conhecimentos e também é um otaku que adora jogar.
Autor: Li Fuqiang, membro da equipe Aikesheng DBA, familiarizado com MySQL, TiDB, OceanBase e outros bancos de dados. Acredito que se você continuar fazendo bem as coisas certas, receberá recompensas diferentes.
Produzido pela comunidade de código aberto Aikeson O conteúdo original não pode ser usado sem autorização. Entre em contato com o editor e indique a fonte para reimpressão.
Este artigo tem cerca de 2.200 palavras e espera-se que leve 8 minutos para ser lido.
Descrição do plano de fundo
Alguns clientes relataram que expandiram o comprimento de um campo do tipo VARCHAR . Na primeira vez você pode modificá-lo rapidamente, mas na segunda vez leva muito tempo para ser executado. Estou confuso porque a quantidade de dados na tabela é quase a mesma. Por que é mais rápido VARCHAR(20)
ajustar de para , mas leva muito tempo para ajustar de para ?VARCHAR(50)
VARCHAR(50)
VARCHAR(100)
Então reproduzimos a situação e conduzimos a análise do problema.
informação ambiental
As informações sobre produtos e versões envolvidas nesta verificação são as seguintes:
produtos | Versão |
---|---|
MySQL | 5.7.25-log MySQL Community Server (GPL) |
Sysbench | sysbench 1.0.17 |
Recorrência de cena
3.1 Preparação de dados
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 Verificação de problemas
Para simular a descrição do cliente, c
modificamos o campo, VARCHAR(20)
modificamos para VARCHAR(50)
e depois modificamos para VARCHAR(100)
, e observamos o tempo necessário para sua execução.
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)
Através da verificação, descobriu-se que o problema reapareceria de forma estável, por isso continuamos tentando modificá-lo. Finalmente, descobrimos que demorava muito para modificar VARCHAR(63)
para VARCHAR(64)
, mas depois de 64, continuamos a expandir o comprimento e descobrimos que sim. poderia ser concluído rapidamente.
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 Análise do problema
Analise a situação em que leva muito tempo para VARCHAR(63)
modificar . VARCHAR(64)
Consultando a documentação oficial , descobrimos que VARCHAR
o tipo de caractere que pode armazenar caracteres quando o comprimento de byte é 1 é 0~255. O tipo de conjunto de caracteres atual é UTF8MB4, como UTF8MB4 é um conjunto de caracteres codificados de quatro bytes, ou seja, um comprimento de byte pode armazenar 63,75 (255/4) caracteres, portanto, quando o modificamos paraVARCHAR(63)
, precisamos adicionar um byte para dados. processamento. VARCHAR(64)
Para armazenamento, é necessário completar essa expansão de comprimento estabelecendo uma tabela temporária, por isso leva muito tempo.
Verificação estendida
4.1 Preparação de dados
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 Verificação de cena UTF8
Como UTF8 é um conjunto de caracteres de codificação de três bytes, um byte pode armazenar 85 (255/3=85) caracteres.
A sequência desta modificação é: VARCHAR(20)→VARCHAR(50)→VARCHAR(85), e observe o tempo necessário para sua execução A seguir estão os comandos de operação relevantes e resultados de execução:
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)
Sequência de modificação: VARCHAR(85)→VARCHAR(86)→VARCHAR(100). Neste momento, observaremos que a instrução SQL executada retorna diretamente um erro. Portanto, excluímos algorithm=inplace ,lock=none
esses dois parâmetros, o que permite que este SQL crie uma tabela temporária e bloqueie a tabela de destino e, em seguida, execute novamente o SQL. A seguir estão os comandos de operação relevantes e os resultados da execução:
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 Verificação do cenário UTF8MB4
Como UTF8MB4 é um conjunto de caracteres de codificação de quatro bytes, um byte pode armazenar 63 (255/4=63,75) caracteres.
A sequência desta modificação é: VARCHAR(20)→VARCHAR(50)→VARCHAR(63), e observe o tempo necessário para sua execução A seguir estão os comandos de operação relevantes e resultados de execução:
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)
A sequência desta modificação é: VARCHAR(63)→VARCHAR(64)→VARCHAR(100). Neste momento, observaremos que a instrução SQL executada retorna diretamente um erro. Portanto, excluímos algorithm=inplace, lock=none
esses dois parâmetros, o que permite que este SQL crie uma tabela temporária e bloqueie a tabela de destino e, em seguida, execute novamente o SQL. A seguir estão os comandos de operação relevantes e os resultados da execução:
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 Análise comparativa
Modificação do comprimento dos caracteres | UTF8(MB3) | UTF8MB4 |
---|---|---|
20->50 | ddl online (no local) | ddl online (no local) |
50->100 | ddl on-line (cópia) | ddl on-line (cópia) |
X->Y | Quando Y*3<256, inserir <br> Quando X*3>=256, inserir | Quando Y*4<256, inserir <br> Quando X*4>=256, inserir |
Observação | Um caractere ocupa no máximo 3 bytes | Um caractere ocupa no máximo 4 bytes |
para concluir
Quando o comprimento máximo de bytes de um campo é >= 256 caracteres, são necessários 2 bytes para representar o comprimento do campo.
Exemplo usando UTF8MB4:
- Para o comprimento máximo de bytes do campo variando entre 256 caracteres (ou seja, x*4<256 e Y*4<256), o ddl online usa o modo inplace, que é altamente eficiente.
- Para campos cujo comprimento máximo de bytes varia além de 256 caracteres (ou seja, x*4>=256 e Y*4>=256), o ddl online usa o modo inplace, que é altamente eficiente.
- Caso contrário, o ddl online usa o modo de cópia, que é ineficiente.
- O mesmo vale para UTF8(MB3).
sugestão
Para evitar expansão posterior do comprimento do campo, o ddl online adota o modo de cópia ineficiente.
- Para tipo de caractere UTF8(MB3):
- O número de caracteres é inferior a 50. Recomenda-se definir o comprimento dos caracteres como VARCHAR(50) ou menos.
- O número de caracteres é próximo de 84 (256/3=83,33). É recomendado defini-lo como varchar(84) ou um comprimento de caracteres maior.
- Para tipo de caractere UTF8MB4:
- Se o número de caracteres for inferior a 50, é recomendável defini-lo como VARCHAR(50) ou um comprimento de caracteres menor.
- O número de caracteres é próximo de 64 (256/4=64). Recomenda-se defini-lo como VARCHAR(64) ou um comprimento de caracteres maior.
Os resultados desta verificação são apenas para referência. Se você precisar operar em um ambiente de produção, defina razoavelmente a duração do VARCHAR com base na situação real para evitar perdas econômicas.
Para mais artigos técnicos, visite: https://opensource.actionsky.com/
Sobre SQLE
SQLE é uma plataforma abrangente de gerenciamento de qualidade de SQL que cobre auditoria e gerenciamento de SQL desde o desenvolvimento até ambientes de produção. Ele oferece suporte aos principais bancos de dados de código aberto, comerciais e domésticos, fornece recursos de automação de processos para desenvolvimento, operação e manutenção, melhora a eficiência on-line e melhora a qualidade dos dados.
Obter SQLE
tipo | endereço |
---|---|
Repositório | https://github.com/actiontech/sqle |
documento | https://actiontech.github.io/sqle-docs/ |
lançar notícias | https://github.com/actiontech/sqle/releases |
Documentação de desenvolvimento de plug-in de auditoria de dados | https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse |