Tác động hiệu suất của việc sử dụng CHAR so với VARCHAR trên trường có kích thước cố định là gì?


58

Tôi có một cột được lập chỉ mục lưu trữ hàm băm MD5. Do đó, cột sẽ luôn lưu trữ giá trị 32 ký tự. Vì lý do nào, điều này đã được tạo ra như một varchar chứ không phải là char. Có đáng để di chuyển cơ sở dữ liệu để chuyển đổi nó thành một char không? Đây là trong MySQL 5.0 với InnoDB.


6
CẢNH BÁO Câu hỏi này và câu trả lời của nó đã được viết trước khi InnoDB và utf8 là mặc định.
Rick James

Câu trả lời:


56

Một câu hỏi tương tự đã được hỏi trước đó

Ý nghĩa về hiệu suất của các kích thước VARCHAR của MySQL

Đây là đoạn trích câu trả lời của tôi

Bạn phải nhận ra sự đánh đổi bằng cách sử dụng CHAR vs VARCHAR

Với các trường CHAR, những gì bạn phân bổ chính xác là những gì bạn nhận được. Ví dụ: CHAR (15) phân bổ và lưu trữ 15 byte, bất kể bạn đặt ký tự trong trường như thế nào. Thao tác chuỗi rất đơn giản và dễ hiểu vì kích thước của trường dữ liệu là hoàn toàn có thể dự đoán được.

Với các trường VARCHAR, bạn có được một câu chuyện hoàn toàn khác. Ví dụ, VARCHAR (15) thực sự phân bổ động tối đa 16 byte, tối đa 15 cho dữ liệu và, ít nhất, thêm 1 byte để lưu trữ độ dài của dữ liệu. Nếu bạn có chuỗi 'hello' để lưu trữ sẽ mất 6 byte, chứ không phải 5. Thao tác chuỗi phải luôn thực hiện một số hình thức kiểm tra độ dài trong mọi trường hợp.

Sự cân bằng thể hiện rõ hơn khi bạn thực hiện hai điều: 1. Lưu trữ hàng triệu hoặc hàng tỷ hàng 2. Các cột lập chỉ mục là CHAR hoặc VARCHAR

TRADINGOFF # 1 Rõ ràng, VARCHAR nắm giữ lợi thế vì dữ liệu có độ dài thay đổi sẽ tạo ra các hàng nhỏ hơn và do đó, các tệp vật lý nhỏ hơn.

TRADINGOFF # 2 Vì các trường CHAR yêu cầu thao tác chuỗi ít hơn do độ rộng trường cố định, tra cứu chỉ mục so với trường CHAR trung bình nhanh hơn 20% so với trường VARCHAR. Đây không phải là bất kỳ phỏng đoán nào về phía tôi. Cuốn sách Thiết kế và điều chỉnh cơ sở dữ liệu MySQL đã thực hiện một cái gì đó tuyệt vời trên bảng MyISAM để chứng minh điều này. Ví dụ trong cuốn sách đã làm một cái gì đó như sau:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Lệnh này buộc tất cả các VARCHAR hoạt động như CHARs. Tôi đã làm điều này trong công việc trước đây vào năm 2007 và lấy một bảng 300 GB và tăng tốc độ tra cứu chỉ số thêm 20% mà không thay đổi bất cứ điều gì khác. Nó làm việc như đã xuất bản. Tuy nhiên, nó đã tạo ra một bảng có kích thước gần gấp đôi, nhưng điều đó chỉ đơn giản là quay trở lại sự đánh đổi # 1.

Bạn có thể phân tích dữ liệu đang được lưu trữ để xem những gì MySQL đề xuất cho định nghĩa cột. Chỉ cần chạy như sau với bất kỳ bảng nào:

SELECT * FROM tblname PROCEDURE ANALYSE();

Điều này sẽ đi qua toàn bộ bảng và đề xuất các định nghĩa cột cho mỗi cột dựa trên dữ liệu chứa, các giá trị trường tối thiểu, giá trị trường tối đa, v.v. Đôi khi, bạn chỉ cần sử dụng thông thường với việc lập kế hoạch CHAR vs VARCHAR. Đây là một ví dụ tốt:

Nếu bạn đang lưu trữ địa chỉ IP, mặt nạ cho một cột như vậy nhiều nhất là 15 ký tự (xxx.xxx.xxx.xxx). Tôi sẽ nhảy ngay vào CHAR(15)một nhịp tim vì độ dài của địa chỉ IP sẽ không thay đổi nhiều và độ phức tạp thêm của thao tác chuỗi được điều khiển bởi một byte bổ sung. Bạn vẫn có thể làm PROCEDURE ANALYSE()một cột chống lại một cột như vậy. Nó thậm chí có thể đề nghị VARCHAR. Tiền của tôi sẽ vẫn còn trên CHAR trên VARCHAR trong trường hợp này.

Các vấn đề CHAR vs VARCHAR chỉ có thể được giải quyết thông qua kế hoạch thích hợp. Với sức mạnh lớn đi kèm với trách nhiệm lớn (sáo rỗng nhưng đúng).

CẬP NHẬT

Khi nói đến MD5, strlencần loại bỏ tính toán nội bộ khi chuyển đổi toàn bộ định dạng hàng. Sẽ không cần phải thay đổi định nghĩa trường.

Nếu khóa MD5 là VARCHAR duy nhất hiện tại, tôi sẽ tìm nó và chuyển đổi định dạng hàng của bảng thành cố định . Nếu có một số lượng đáng kể các trường VARCHAR khác, họ cũng sẽ được hưởng lợi. Đổi lại, bảng sẽ mở rộng gấp đôi kích thước của nó. Nhưng các truy vấn sẽ tăng tốc thêm khoảng 20% ​​mà không cần điều chỉnh bổ sung.


1
Tôi nghĩ rằng tôi sẽ sử dụng một char (4) hoặc một cái gì đó giống như một số nguyên không dấu cho một địa chỉ IP
Jack Douglas

@JackPDoureb Bạn đã đúng về điểm đó.
RolandoMySQLDBA

Không có chỉ mục được lưu trữ với một chiều dài cố định nào? Tôi không hiểu cách thay đổi định dạng lưu trữ thành tra cứu chỉ mục được cải thiện độ dài cố định. Bạn có nghĩa là nó được cải thiện quét bảng?
Marcus Adams

1
@JackDoumund, Tại sao không bitbinary?
Pacerier

@Pacerier sẽ tốt hơn, tôi đồng ý :)
Jack Douglas

19

Có vẻ như bạn sẽ tiết kiệm 1 byte cho mỗi giá trị hoặc khoảng 3% bằng cách chuyển đổi sang a char. Có thể không có giá trị nếu bạn đang lưu trữ MD5 ở dạng hex - bạn có thể tiết kiệm 50% bằng cách sử dụng binarythay thế.

Cảm ơn Ovais (xem bình luận) vì đã chỉ ra rằng char(32)có thể sử dụng nhiều hơn 32 byte nếu bạn đang sử dụng một bộ ký tự đa nhân.

Cảm ơn Rick James đã chỉ ra rằng bạn nên sử dụng unhexhàm để chuyển đổi chuỗi hex thành nhị phân:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| chiều dài (thanh) |
| ----------: |
| 32 |
| 16 |

db <> fiddle ở đây


Cuộc gọi tốt về việc thay đổi thành nhị phân.
RThomas

Tôi đang dự định chuyển đổi nó thành nhị phân. Bây giờ tôi nghĩ về nó, kích thước không nên khác nhau chỉ dựa trên việc tôi đang sử dụng byte hay char vì mã hóa của chúng tôi là utf-8. Hoặc là tôi sai?
Jason Baker

@Jason - mã hóa không áp dụng cho binary- hoặc tôi đã hiểu nhầm?
Jack Douglas

3
đối với cột char (32) với bộ ký tự utf-8, mọi giá trị sẽ cần 32x3 byte để lưu trữ. Tại sao bạn cần đặt giá trị băm MD5 là utf-8. Chuyển đổi thành nhị phân (32) sẽ cần 32 byte cho mỗi giá trị.
ovais.tariq

1
Thay đổi thành BINARYrất ít trừ khi bạn cũng sử dụng UNHEX(). Đó là, bạn có thể lưu trữ UNHEX(MD5(x))vào 16 byte BINARY(16)để tiết kiệm không gian đáng kể khi lưu trữ MD5(x)vào CHAR(32) CHARACTER SET ascii.
Rick James

15

Nó không đáng để thay đổi theo ý kiến ​​của tôi. Nếu bạn xem qua các tài liệu ở đây, nó sẽ minh họa sự khác biệt giữa hai. Trong kịch bản sử dụng của bạn, cái này không thực sự mang lại bất kỳ lợi ích đáng kể nào so với cái kia trừ khi bạn thực sự lo lắng về phần chi phí phụ liên quan đến kích thước hàng.

http://dev.mysql.com/doc/refman/5.0/en/char.html

Cũng lưu ý nhận xét đầu tiên về tài liệu tôi liên kết ở trên ... "CHAR sẽ chỉ tăng tốc truy cập của bạn nếu toàn bộ bản ghi có kích thước cố định. Đó là, nếu bạn sử dụng bất kỳ đối tượng kích thước thay đổi nào, bạn cũng có thể tạo ra tất cả chúng kích thước thay đổi. Bạn không đạt được tốc độ bằng cách sử dụng CHAR trong bảng cũng chứa VARCHAR "


"Tăng tốc" đó áp dụng cho MyISAM, không phải InnoDB.
Rick James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.