Thiết kế độ dài VARCHAR cơ sở dữ liệu: Thực tiễn tốt nhất cho giới hạn ký tự

9 phút đọc

Chọn độ dài VARCHAR phù hợp cho cột cơ sở dữ liệu là quyết định thiết kế cơ bản ảnh hưởng đến hiệu quả lưu trữ, hiệu suất truy vấn và tính toàn vẹn dữ liệu, giống như thiết kế độ dài phản hồi API ảnh hưởng đến chất lượng tổng thể của hệ thống. Bài viết này trình bày các hướng dẫn thực tế cho các loại trường phổ biến trên các hệ thống cơ sở dữ liệu chính. Để tìm hiểu sâu hơn về thiết kế schema, hãy tham khảo sách thiết kế cơ sở dữ liệu.

Huyền thoại VARCHAR(255) - Tại sao 255 trở thành mặc định

Mặc định VARCHAR(255) phổ biến bắt nguồn từ giới hạn MySQL cũ. Trước MySQL 5.0, tiền tố độ dài VARCHAR được lưu trữ trong một byte duy nhất, giới hạn tối đa ở 255. MySQL 5.0 chuyển sang tiền tố độ dài 2 byte, cho phép lên đến 65.535 byte - nhưng quy ước "255" vẫn tồn tại như một thói quen lâu sau khi ràng buộc kỹ thuật đã biến mất.

Có một lý do khác khiến quy ước này tồn tại. Trong InnoDB của MySQL, các cột VARCHAR có độ dài khai báo 255 trở xuống sử dụng tiền tố độ dài 1 byte, trong khi các cột có độ dài 256 trở lên sử dụng tiền tố 2 byte. Điều này có nghĩa là có sự chênh lệch 1 byte chi phí trên mỗi hàng giữa VARCHAR(255)VARCHAR(256). Với bảng có 1 triệu hàng, điều này chỉ tương đương 1 MB - nhưng nhận thức rằng "255 là hiệu quả" đã trở nên phổ biến.

Triển khai nội bộ VARCHAR trên các RDBMS

Ngay cả với cùng khai báo VARCHAR(100), định dạng lưu trữ nội bộ và hành vi cấp phát bộ nhớ khác nhau đáng kể giữa các hệ thống cơ sở dữ liệu. Không hiểu những khác biệt này có thể dẫn đến vấn đề hiệu suất và lưu trữ không mong đợi.

RDBMSĐộ dài tối đaĐơn vịLưu trữ nội bộCấp phát bộ nhớ
MySQL 8.0 (InnoDB)65.535 byte (mỗi hàng)Ký tựDữ liệu thực + tiền tố 1-2 byte. Dữ liệu vượt quá 768 byte tràn sang trang ngoàiBảng tạm cấp phát độ dài khai báo × byte tối đa mỗi ký tự (×4 cho utf8mb4)
PostgreSQL~1 GBKý tựCấu trúc varlena. VARCHAR và TEXT sử dụng lưu trữ giống hệt nhau. TOAST tự động nén dữ liệu trên 2 KBChỉ độ dài dữ liệu thực. Độ dài khai báo hoạt động như ràng buộc kiểm tra
SQL Server8.000 byteKý tựLưu trữ trong hàng. VARCHAR(MAX) sử dụng lưu trữ LOBThực thi truy vấn dự trữ độ dài khai báo (Memory Grant)
Oracle4.000 byte (tiêu chuẩn) / 32.767 byte (mở rộng)Byte hoặc Ký tự (điều khiển bởi NLS_LENGTH_SEMANTICS)Lưu trữ trong hàng. Chế độ mở rộng sử dụng SecureFile LOBPGA cấp phát độ dài khai báo
SQLiteKhông giới hạnKiểu động. Khai báo VARCHAR bị bỏ qua; chỉ lưu trữ độ dài dữ liệu thựcChỉ độ dài dữ liệu thực

MySQL và SQL Server đáng được chú ý đặc biệt. Trong các hệ thống này, ngay cả khi cột VARCHAR(255) chỉ lưu trữ 10 ký tự, bảng tạm và thao tác sắp xếp vẫn cấp phát 255 × 4 = 1.020 byte bộ nhớ. Với các bảng có nhiều cột, việc cấp phát bộ nhớ quá mức này có thể làm giảm đáng kể hiệu suất truy vấn.

Tác động mã hóa độ dài biến đổi UTF-8 lên VARCHAR(255)

Ngay cả trong RDBMS chỉ định độ dài VARCHAR bằng ký tự, giới hạn byte nội bộ vẫn áp dụng. Hiểu sự khác biệt giữa ký tự và byte là điều cần thiết cho thiết kế schema đúng đắn. UTF-8 là mã hóa có độ dài biến đổi trong đó các loại ký tự khác nhau tiêu thụ số byte khác nhau.

Loại ký tựByte UTF-8Ví dụSố ký tự tối đa trong VARCHAR(255) (tương đương byte)
Chữ cái và số ASCII1 bytea, Z, 0, @255 ký tự (255 byte)
Latin mở rộng / Cyrillic2 byteé, ñ, Д255 ký tự (510 byte)
Ký tự CJK3 byte漢, あ, 한255 ký tự (765 byte)
Emoji / ký hiệu đặc biệt4 byte😀, 🎉, 𠮷255 ký tự (1.020 byte)

Với utf8mb4 của MySQL, cột VARCHAR(255) có thể tiêu thụ tới 1.020 byte trong trường hợp xấu nhất. Giới hạn kích thước hàng của InnoDB là khoảng 8.126 byte (một nửa trang 16 KB trừ header), vì vậy chỉ 8 cột VARCHAR(255) đã có thể vượt quá giới hạn kích thước hàng. Sử dụng Bộ đếm ký tự để xác minh số byte khi thiết kế schema và ngăn chặn việc cắt ngắn dữ liệu không mong muốn.

VARCHAR vs TEXT - Thực tế hiệu suất và lập chỉ mục

Lời khuyên phổ biến "sử dụng TEXT cho chuỗi dài" đơn giản hóa quá mức tình huống. Sự đánh đổi giữa VARCHAR và TEXT khác nhau đáng kể tùy theo RDBMS.

Khía cạnhMySQL (InnoDB)PostgreSQLSQL Server
Khác biệt lưu trữVARCHAR được lưu trong hàng (tối đa 768 byte). TEXT hoạt động tương tự nhưng ở định dạng hàng COMPACT, chỉ 768 byte đầu tiên được giữ trong hàngKhông khác biệt. VARCHAR(n) và TEXT sử dụng cùng cấu trúc varlenaVARCHAR lưu trong hàng. TEXT (VARCHAR(MAX)) sử dụng lưu trữ LOB
Lập chỉ mụcVARCHAR: chỉ mục đầy đủ (tối đa 767 byte). TEXT: chỉ chỉ mục tiền tốCả hai đều có thể lập chỉ mục như nhauVARCHAR: chỉ mục đầy đủ. TEXT: chỉ chỉ mục toàn văn
Sắp xếp / GROUP BYVARCHAR: trong bộ nhớ. TEXT: có thể sử dụng bảng tạm trên đĩaKhông khác biệtVARCHAR: trong bộ nhớ. TEXT: sử dụng tempdb
Giá trị mặc địnhVARCHAR: hỗ trợ. TEXT: không hỗ trợ (hỗ trợ từ MySQL 8.0.13)Cả hai đều hỗ trợCả hai đều hỗ trợ

Trong PostgreSQL, hầu như không có sự khác biệt giữa VARCHAR(n) và TEXT - tài liệu chính thức thậm chí khuyến nghị "sử dụng TEXT hoặc VARCHAR không ràng buộc trừ khi bạn có lý do cụ thể." Tuy nhiên trong MySQL, cột TEXT không thể có chỉ mục đầy đủ, vì vậy nên chọn VARCHAR cho các cột sẽ được tìm kiếm.

Cạm bẫy Emoji (4-Byte UTF-8) với VARCHAR

Ứng dụng hiện đại phải được thiết kế với giả định rằng đầu vào người dùng sẽ chứa emoji. Emoji tiêu thụ 4 byte trong UTF-8, nhưng vấn đề không chỉ dừng lại ở số byte.

VARCHAR vs. CHAR

Trước khi chọn loại chuỗi, hãy hiểu sự khác biệt cơ bản giữa VARCHAR và CHAR.

Thuộc tínhCHAR(n)VARCHAR(n)
Lưu trữĐộ dài cố định (đệm bằng khoảng trắng)Độ dài biến đổi (chỉ dữ liệu thực)
Sử dụng đĩaLuôn n byteDữ liệu thực + 1-2 byte chi phí
Phù hợp nhất choDữ liệu độ dài cố định (mã quốc gia, mã bưu chính)Dữ liệu độ dài biến đổi (tên, email)
Tốc độ tìm kiếmNhanh hơn một chút nhờ độ dài cố địnhChi phí nhỏ từ độ dài biến đổi

VARCHAR là lựa chọn đúng cho đại đa số trường hợp sử dụng. Chỉ dùng CHAR cho dữ liệu thực sự có độ dài cố định như mã quốc gia ISO (CHAR(2)) hoặc mã tiền tệ (CHAR(3)). Lưu ý rằng trong InnoDB của MySQL, cột CHAR cũng được lưu trữ dưới dạng độ dài biến đổi (khoảng trắng cuối bị loại bỏ), nên sự khác biệt lưu trữ là tối thiểu.

Lỗi thiết kế VARCHAR phổ biến và chi phí sửa chữa

Thay đổi độ dài VARCHAR khi migration - Rủi ro và quy trình an toàn

Thao tác ALTER TABLE để thay đổi độ dài VARCHAR trong production hoạt động rất khác nhau giữa các RDBMS. Hiểu cơ chế nội bộ là điều cần thiết để thực thi an toàn.

RDBMSTăng độ dài (vd: 100→200)Giảm độ dài (vd: 200→100)Ghi chú
MySQL (InnoDB)≤255→≤255: chỉ metadata (tức thì). ≤255→≥256: cần xây dựng lại bảngCần xây dựng lại bảng. Lỗi nếu dữ liệu hiện có vượt quá độ dài mớiSử dụng pt-online-schema-change hoặc gh-ost cho bảng lớn
PostgreSQLChỉ metadata (tức thì). Không cần khóa bảngCần xác thực dữ liệu. Lỗi khi vi phạm ràng buộcThay đổi độ dài VARCHAR luôn nhẹ trong PostgreSQL
SQL ServerChỉ metadata (tức thì)Xác thực dữ liệu rồi thay đổi metadataVARCHAR→VARCHAR(MAX) cần xây dựng lại bảng
OracleChỉ metadata (tức thì)Xác thực dữ liệu rồi thay đổi metadataCó thể thay đổi ngữ nghĩa BYTE→CHAR qua ALTER TABLE MODIFY

Quy trình an toàn để thay đổi độ dài VARCHAR trong MySQL:

  1. Kiểm tra độ dài tối đa dữ liệu hiện có: SELECT MAX(CHAR_LENGTH(column_name)) FROM table_name;
  2. Xác định xem thay đổi có vượt qua ranh giới 255 byte hay không (vượt qua sẽ kích hoạt xây dựng lại bảng).
  3. Với bảng lớn (1 triệu+ hàng), sử dụng pt-online-schema-change hoặc gh-ost để thay đổi không ngừng hoạt động.
  4. Sau khi thay đổi, chạy ANALYZE TABLE để cập nhật thống kê trình tối ưu hóa.

Độ dài khuyến nghị theo loại trường

TrườngVARCHAR khuyến nghịLý do
Email254Tối đa theo RFC 5321
Tên đăng nhập50Ràng buộc hiển thị UI
Tên hiển thị100Hỗ trợ đa ngôn ngữ, bao gồm emoji
Tên (quốc tế)100Phù hợp với các nền văn hóa có tên dài và tên đệm
Số điện thoại20Định dạng E.164 tối đa 15 chữ số + tiền tố quốc gia + ký hiệu
URL2048Giới hạn thực tế của trình duyệt
Dòng địa chỉ200Định dạng địa chỉ quốc tế
Tên sản phẩm200Giới hạn trên phổ biến trong thương mại điện tử
Hash mật khẩuVARCHAR(60) / CHAR(60)Hash bcrypt cố định 60 ký tự. CHAR(60) là tối ưu
UUIDCHAR(36) / BINARY(16)36 ký tự có dấu gạch ngang. Lưu trữ nhị phân là 16 byte và hiệu quả hơn
  1. Khi có đặc tả hoặc tiêu chuẩn (RFC, ISO, v.v.) định nghĩa giá trị tối đa, hãy khớp với nó.
  2. Khi không có đặc tả, thêm biên 20-50% vào độ dài dữ liệu tối đa quan sát được.
  3. Lập kế hoạch cho tăng trưởng tương lai nhưng tránh giá trị quá lớn. Trong MySQL, hãy lưu ý ranh giới 255 byte.
  4. Triển khai cùng xác thực giới hạn ký tự ở phía ứng dụng để ngăn chặn sự không khớp với DB.
  5. Với các cột quốc tế hóa, thiết kế dựa trên ngôn ngữ/văn hóa dài nhất, không chỉ ngôn ngữ chính của bạn.

Kết luận

Thiết kế độ dài VARCHAR đòi hỏi xem xét toàn diện về đặc tính dữ liệu, mã hóa và triển khai nội bộ RDBMS. Thay vì mặc định 255, hãy đặt độ dài với lý do rõ ràng để cải thiện hiệu quả lưu trữ, hiệu suất truy vấn và chất lượng dữ liệu. Trong MySQL, hãy đặc biệt chú ý đến ranh giới tiền tố 255 byte, cấp phát bộ nhớ bảng tạm và tác động kích thước chỉ mục. Để tìm hiểu toàn diện về tối ưu hóa SQL, hãy khám phá tài liệu tối ưu hiệu suất SQL. Sử dụng Bộ đếm ký tự để đo độ dài dữ liệu thực tế khi thiết kế schema của bạn.