Melhores práticas de avaliação de comprimento do MySQL VARCHAR

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, cmodificamos 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 VARCHARo 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=noneesses 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=noneesses 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
Um programador nascido na década de 1990 desenvolveu um software de portabilidade de vídeo e faturou mais de 7 milhões em menos de um ano. O final foi muito punitivo! Alunos do ensino médio criam sua própria linguagem de programação de código aberto como uma cerimônia de maioridade - comentários contundentes de internautas: Contando com RustDesk devido a fraude desenfreada, serviço doméstico Taobao (taobao.com) suspendeu serviços domésticos e reiniciou o trabalho de otimização de versão web Java 17 é a versão Java LTS mais comumente usada no mercado do Windows 10 Atingindo 70%, o Windows 11 continua a diminuir Open Source Daily | Google apoia Hongmeng para assumir o controle de telefones Android de código aberto apoiados pela ansiedade e ambição da Microsoft; Electric desliga a plataforma aberta Apple lança chip M4 Google exclui kernel universal do Android (ACK) Suporte para arquitetura RISC-V Yunfeng renunciou ao Alibaba e planeja produzir jogos independentes na plataforma Windows no futuro
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/actiontechoss/blog/11094958
Recomendado
Clasificación