MySQL VARCHAR 최상의 길이 평가 방법

VARCHAR의 길이가 올바른가요?

저자: Aikesheng DBA 팀의 일원인 Guan Yongqiang은 MySQL 운영 및 유지 관리 기술에 능숙합니다. 새로운 지식을 배우는 것을 좋아하고, 게임을 좋아하는 오타쿠이기도 하다.

작성자: Li Fuqiang, Aikesheng DBA 팀 구성원, MySQL, TiDB, OceanBase 및 기타 데이터베이스에 익숙함. 계속해서 옳은 일을 잘하면 다른 보상을 받게 될 것이라고 믿습니다.

Aikeson 오픈 소스 커뮤니티에서 제작되었습니다. 원본 콘텐츠는 승인 없이 사용할 수 없습니다. 편집자에게 연락하여 재인쇄할 출처를 명시해 주세요.

이 글은 약 2,200 단어로, 읽는 데 8분 정도 소요될 것으로 예상됩니다.

배경 설명

일부 고객은 VARCHAR 유형 필드 의 길이를 확장했다고 보고했습니다. 처음에는 빠르게 수정할 수 있지만 두 번째에는 실행하는 데 시간이 오래 걸립니다. 테이블에 있는 데이터의 양이 거의 같아서 헷갈립니다. 에서 를 조정하는 것이 더 빠른데 , 에서 을 조정하는 것은 시간이 오래 걸리는 이유는 무엇입니까 ?VARCHAR(20)VARCHAR(50)VARCHAR(50)VARCHAR(100) 그래서 상황을 재현하고 문제 분석을 진행했습니다.

환경정보

이번 검증에 참여한 제품 및 버전 정보는 다음과 같습니다.

제품 버전
MySQL 5.7.25-로그 MySQL 커뮤니티 서버(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)검증을 통해 문제가 안정적으로 다시 나타날 것으로 확인되어 지속적으로 수정을 시도하였고, 결국 으로 수정하는데 시간이 오래 걸리는 것을 확인하였으나, 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는 4바이트로 인코딩된 문자 집합입니다. 즉, 1바이트 길이는 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은 3바이트 인코딩 문자 집합이므로 1바이트는 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는 4바이트 인코딩 문자 집합이므로 1바이트는 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(MB3) UTF8MB4
20->50 온라인 ddl(inplace) 온라인 ddl(inplace)
50->100 온라인 ddl(복사본) 온라인 ddl(복사본)
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)의 경우 온라인 ddl은 매우 효율적인 내부 모드를 사용합니다.
  • 최대 바이트 길이가 256자를 초과하는 필드(즉, x*4>=256 및 Y*4>=256)의 경우 온라인 ddl은 매우 효율적인 내부 모드를 사용합니다.
  • 그렇지 않으면 온라인 ddl은 복사 모드를 사용하므로 비효율적입니다.
  • UTF8(MB3)도 마찬가지입니다.

제안

나중에 필드 길이 확장을 방지하기 위해 온라인 ddl은 비효율적인 복사 모드를 채택하는 것이 좋습니다.

  • 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년대에 태어난 프로그래머가 비디오 포팅 소프트웨어를 개발하여 1년도 안 되어 700만 개 이상의 수익을 올렸습니다. 결말은 매우 처참했습니다! 고등학생들이 성인식으로 자신만의 오픈소스 프로그래밍 언어 만든다 - 네티즌 날카로운 지적: 만연한 사기로 러스트데스크 의존, 가사 서비스 타오바오(taobao.com)가 가사 서비스를 중단하고 웹 버전 최적화 작업 재개 자바 17은 가장 일반적으로 사용되는 Java LTS 버전입니다. Windows 10 시장 점유율 70%에 도달, Windows 11은 계속해서 Open Source Daily를 지원합니다. Google은 Docker가 지원하는 오픈 소스 Rabbit R1을 지원합니다. Electric, 개방형 플랫폼 종료 Apple, M4 칩 출시 Google, Android 범용 커널(ACK) 삭제 RISC-V 아키텍처 지원 Yunfeng은 Alibaba에서 사임하고 향후 Windows 플랫폼에서 독립 게임을 제작할 계획
{{o.이름}}
{{이름}}

추천

출처my.oschina.net/actiontechoss/blog/11094958