2013-03-01 45 views
21

Tôi có một bảng như thế này:MySQL index varchar dài

CREATE TABLE `products` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `name` varchar(512) NOT NULL, 
    `description` text, 
    PRIMARY KEY (`id`), 
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8; 

và một như thế này:

CREATE TABLE `product_variants` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `product_id` int(11) unsigned NOT NULL, 
    `product_code` varchar(255) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `product_code` (`product_code`), 
    KEY `product_variant_product_fk` (`product_id`), 
    CONSTRAINT `product_variant_product_fk` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1037 DEFAULT CHARSET=utf8; 

và một câu lệnh SQL như thế này

SELECT p.id AS id, p.name AS name, p.description AS description, pv.id AS product_variant_id, pv.product_code AS product_code 
FROM products p 
INNER JOIN product_variants pv ON pv.product_id = p.id 
ORDER BY p.name ASC 
LIMIT 300 OFFSET 0; 

mà nếu tôi giải thích cung cấp cho tôi điều này:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref  | rows | Extra   | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| 1 | SIMPLE  | p  | ALL | PRIMARY     | NULL      | NULL | NULL | 993658 | Using filesort | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | db.p.id |  1 |    | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
2 rows in set (0.00 sec) 

Đối với hàng triệu hàng, điều này khá chậm. Tôi đã cố gắng thêm một chỉ mục trên products.name với:

ALTER TABLE products ADD INDEX `product_name_idx` (name(512)); 

mang đến cho này:

mysql> show indexes from products; 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| products |   0 | PRIMARY   |   1 | id    | A   |  993658 |  NULL | NULL |  | BTREE  |   |    | 
| products |   1 | product_manf_fk |   1 | manufacturer_id | A   |   18 |  NULL | NULL | YES | BTREE  |   |    | 
| products |   1 | product_name_idx |   1 | name   | A   |   201 |  255 | NULL |  | BTREE  |   |    | 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
3 rows in set (0.00 sec) 

Tôi nghĩ rằng cột Sub_part cho thấy tiền tố đó đã được trong lập chỉ mục (theo byte), như được mô tả trên this page.

Khi tôi lại giải thích các truy vấn, tôi nhận được:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref  | rows | Extra   | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| 1 | SIMPLE  | p  | ALL | PRIMARY     | NULL      | NULL | NULL | 993658 | Using filesort | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | db.p.id |  1 |    | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
2 rows in set (0.00 sec) 

trông giống như chỉ số mới không được sử dụng. Như được mô tả trên this page, các chỉ mục sẽ không được sử dụng để sắp xếp nếu chúng là các chỉ mục tiền tố . Trong thực tế nếu tôi cắt ngắn dữ liệu với:

alter table products modify `name` varchar(255) not null; 

Các giải thích cho:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref           | rows | Extra | 
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 
| 1 | SIMPLE  | p  | index | PRIMARY     | product_name_idx   | 767  | NULL           | 300 |  | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | oh_2c98c233_69fe_4f06_ad0d_fe6f85a5beac.p.id | 1 |  | 
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 

mà tôi nghĩ rằng lưng lên. Tuy nhiên, nó nói trên this page rằng bảng InnoDB có thể có tối đa 767 byte chỉ mục. Nếu độ dài là byte, tại sao nó từ chối có nhiều hơn 255? Nếu nó ở trong các ký tự , làm cách nào để quyết định độ dài của mỗi ký tự UTF-8? Là nó chỉ giả định 3?

Ngoài ra, đang sử dụng phiên bản này của MySQL:

mysql> select version(); 
+------------+ 
| version() | 
+------------+ 
| 5.5.27-log | 
+------------+ 
1 row in set (0.00 sec) 
+0

Trước mysql 5.0.3 chiều dài tối đa của một lĩnh vực varchar là 255, và 65535 trong mysql 5.0.3 và các phiên bản sau. – Cyclonecode

+0

xin lỗi - đáng lẽ phải nói, tôi đang sử dụng 5.5.27-log – l0st3d

Trả lời

43

tôi phải rà soát lại câu trả lời của tôi do nghiên cứu của tôi. Tôi ban đầu được đăng tải này (trích dẫn bản thân mình):

Tôi tin rằng câu trả lời là bạn không thể biết có bao nhiêu nhân vật sẽ hiện trong chỉ mục bởi vì bạn không thể biết có bao nhiêu byte ký tự bạn sẽ được (trừ khi bạn làm điều gì đó để loại trừ các ký tự nhiều byte).

Và tôi không chắc chắn, nhưng nó vẫn có thể đúng, nhưng không hoàn toàn theo cách tôi nghĩ.

Dưới đây là câu trả lời đúng:

MySQL giả 3 byte cho mỗi nhân vật utf8. 255 ký tự là kích thước chỉ mục tối đa bạn có thể chỉ định cho mỗi cột, bởi vì 256x3 = 768, phá vỡ giới hạn 767 byte.

Nếu bạn không chỉ định kích thước chỉ mục, MySQL chọn kích thước tối đa (tức là 255 mỗi cột). Không thể đặt ràng buộc UNIQUE trên cột utf8 có chiều dài lớn hơn 255, vì chỉ mục duy nhất phải chứa toàn bộ giá trị ô. Nhưng một chỉ mục thông thường có thể được sử dụng - nó sẽ chỉ lập chỉ mục 255 ký tự đầu tiên (hoặc 767 byte đầu tiên?). Và đó là nơi vẫn còn một số bí ẩn đối với tôi.

MySTERY: Tôi có thể thấy lý do tại sao MySQL giả sử 3 byte cho mỗi ký tự, vì sự an toàn, bởi vì nếu không thì ràng buộc UNIQUE có thể bị hỏng. Nhưng các tài liệu dường như gợi ý rằng chỉ mục thực sự có kích thước bằng byte chứ không phải ký tự. Vì vậy, giả sử bạn đặt một chỉ mục 25 char (765 byte) vào cột vARCHAR (25). Nếu các ký tự bạn lưu trữ là tất cả ASCII, các ký tự 1 byte, như A-Z, a-z, 0-9, thì bạn có thể vừa với toàn bộ cột vào chỉ mục byte 767. Và có vẻ như đó là điều thực sự xảy ra.

Dưới đây là một số thông tin từ câu trả lời ban đầu của tôi về nhân vật, byte vv


Theo wikipedia, UTF-8 nhân vật có thể dài 1,2, 3, hoặc 4 byte. Nhưng, theo this mysql documentation, kích thước ký tự tối đa là 3 byte và vì vậy, bất kỳ chỉ mục cột nào trên 255 ký tự đều có thể đạt đến giới hạn byte đó. Nhưng khi tôi hiểu nó, nó có thể không. Nếu hầu hết các ký tự của bạn nằm trong phạm vi ASCII, thì kích thước ký tự trung bình của bạn sẽ gần bằng 1 byte. Nếu kích thước ký tự trung bình của bạn là, ví dụ, 1,3 byte (chủ yếu là 1 byte, nhưng một số lượng đáng kể 2-3 ký tự byte), thì bạn có thể chỉ định chỉ mục 767/1.3

Vì vậy, nếu bạn đang lưu trữ chủ yếu Ký tự 1 byte, giới hạn ký tự thực của bạn sẽ giống như: 767/1.3 = 590. Nhưng hóa ra đó không phải là cách hoạt động của nó. 255 ký tự là giới hạn.

Như đã đề cập trong this MySQL documentation,

giới hạn tiền tố được đo bằng byte, trong khi chiều dài tiền tố trong câu lệnh CREATE INDEX được hiểu là số lượng ký tự cho kiểu dữ liệu nonbinary (CHAR, VARCHAR, TEXT). Xem xét điều này khi chỉ định độ dài tiền tố cho cột sử dụng bộ ký tự nhiều byte .

Có vẻ như MySQL đang tư vấn cho mọi người thực hiện phép tính/đánh giá như tôi vừa làm để xác định kích thước khóa của bạn cho cột vARCHAR. Nhưng trên thực tế, bạn không thể chỉ định một chỉ mục lớn hơn 255 cho các cột utf8.

Cuối cùng, nếu bạn xem lại liên kết thứ hai của tôi một lần nữa, đó cũng là điều này:

Khi tùy chọn cấu hình innodb_large_prefix được kích hoạt, hạn chiều dài này được nâng lên 3072 byte, cho các bảng InnoDB sử dụng định dạng hàng NĂNG ĐỘNG và được nén.

Vì vậy, có vẻ như bạn có thể nhận được nhiều chỉ mục lớn hơn nếu bạn muốn, với một chút tinh chỉnh. Chỉ cần chắc chắn rằng các định dạng hàng là NĂNG ĐỘNG hoặc NỔI BẬT. Bạn có thể chỉ định một chỉ mục gồm 1023 hoặc 1024 ký tự trong trường hợp đó.


Nhân tiện, hóa ra bạn có thể lưu trữ các ký tự 4 byte bằng cách sử dụng the utf8mb4 character set. Bộ ký tự utf8 dường như chỉ lưu trữ "plane 0" characters.

EDIT:

Tôi chỉ cố gắng để tạo ra một chỉ số tổng hợp trên (511) cột VARCHAR với một tinyint (1) cột và nhận được thông báo lỗi nói rằng kích thước chỉ số tối đa là 767 byte. Điều này làm cho tôi tin rằng MySQL giả định các cột thiết lập ký tự utf8 sẽ chứa 3 byte cho mỗi ký tự (tối đa), và cho phép bạn sử dụng tối đa 255 ký tự. Nhưng có lẽ đó chỉ là với các chỉ mục tổng hợp. Tôi sẽ cập nhật câu trả lời của mình khi tìm hiểu thêm. Nhưng bây giờ tôi để lại điều này như một chỉnh sửa.

0

Giới hạn trên InnoDB Bàn

Warning

Không chuyển đổi bảng hệ thống MySQL trong cơ sở dữ liệu mysql từ MyISAM các bảng InnoDB. Đây là một hoạt động không được hỗ trợ. Nếu bạn làm điều này, MySQL không khởi động lại cho đến khi bạn khôi phục lại các bảng hệ thống cũ từ một bản sao lưu hoặc tái tạo chúng với chương trình mysql_install_db.

Warning

Nó không phải là một ý tưởng tốt để cấu hình InnoDB để sử dụng tập tin dữ liệu hoặc file log trên khối lượng NFS. Nếu không, các tập tin có thể bị khóa bởi các quy trình khác và trở nên không có sẵn để sử dụng bởi MySQL.

mức tối đa và tối thiểu

  1. Một bảng có thể chứa tối đa 1.000 cột.
  2. Bảng có thể chứa tối đa 64 chỉ mục phụ.
  3. Theo mặc định, khóa chỉ mục cho chỉ mục một cột có thể lên tới 767 byte. Giới hạn độ dài tương tự cũng áp dụng cho bất kỳ tiền tố khóa chỉ mục nào. Ví dụ: bạn có thể đạt đến giới hạn này với chỉ mục tiền tố cột có hơn 255 ký tự trên cột TEXT hoặc VARCHAR, giả sử bộ ký tự UTF-8 và tối đa 3 byte cho mỗi ký tự. Khi tùy chọn cấu hình innodb_large_prefix được bật, giới hạn độ dài này được tăng lên 3072 byte, đối với các bảng InnoDB sử dụng định dạng hàng DYNAMIC và COMPRESSED.
  4. Nếu bạn chỉ định độ dài tiền tố chỉ mục lớn hơn giá trị tối đa cho phép, độ dài được giảm âm thầm đến độ dài tối đa. Trong MySQL 5.6 trở lên, chỉ định độ dài tiền tố chỉ mục lớn hơn độ dài tối đa sẽ tạo ra lỗi.

Khi innodb_large_prefix được bật, cố tạo tiền tố chỉ mục có độ dài khóa lớn hơn 3072 cho bảng REDUNDANT hoặc COMPACT gây ra lỗi ER_INDEX_COLUMN_TOO_LONG.

Độ dài khóa tối đa nội bộ InnoDB là 3500 byte, nhưng bản thân MySQL hạn chế điều này thành 3072 byte. Giới hạn này áp dụng cho độ dài của khóa chỉ mục kết hợp trong chỉ mục nhiều cột.

Độ dài hàng tối đa, ngoại trừ cột có độ dài thay đổi (VARBINARY, VARCHAR, BLOB và TEXT), nhỏ hơn một nửa trang cơ sở dữ liệu. Nghĩa là, chiều dài hàng tối đa là khoảng 8000 byte. Các cột LONGBLOB và LONGTEXT phải nhỏ hơn 4GB và tổng chiều dài hàng, bao gồm các cột BLOB và TEXT, phải nhỏ hơn 4GB.

tham khảo: InnoDB Restrictions

Các vấn đề liên quan