Longueur de l'indice de varchar MySQL

j'ai un tableau comme ceci:

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;

et l'un comme ceci:

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;

et une instruction SQL comme ceci

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;

qui, si je l'explique me donne ceci:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| 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)

pour un million de rangs, c'est assez lent. J'ai essayé d'ajouter un index sur produits.nom avec:

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

ce qui donne ceci:

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)

je pense que la colonne Sub_part montre le préfixe qui a été indexé (en octets), décrit sur cette page.

Quand j'ai re-expliquer la requête, j'obtiens:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| 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)

qui semble ne pas être utilisé. Comme décrit sur cette page, les index ne seront pas utilisés pour le tri s'ils sont préfixe index. En fait, si je tronque les données avec:

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

L'expliquer donne:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+
| 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 |       |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+

je pense que le dos. Cependant, il est dit sur cette page que Les tables InnoDB peut avoir jusqu'à 767 octets d'index. Si la longueur est en octets, pourquoi faut-il refuser d'avoir plus de 255? Si c'est dans caractères, comment est-ce de décider la longueur de chaque caractère UTF-8? Être juste en supposant que 3?

Aussi, je suis en utilisant cette version de MySQL:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.27-log |
+------------+
1 row in set (0.00 sec)
23
demandé sur Gras Double 2013-03-01 15:58:40

2 réponses

je dois réviser ma réponse à cause de mes recherches. J'ai posté ce (me citer):

je crois que la réponse est que vous ne pouvez pas savoir combien de caractères être dans l'index parce que vous ne pouvez pas savoir combien d'octets de vos personnages sera (sauf si vous faites quelque chose pour exclure des caractères multi-octets).

Et je ne suis pas sûr, mais ça peut être correct, mais pas tout à fait la façon dont je pensais.

Ici est la bonne réponse:

MySQL suppose 3 octets par caractère utf8. 255 caractères est la taille maximale de l'index que vous pouvez spécifier par colonne, parce que 256x3=768, qui dépasse la limite de 767 octets.

si vous ne spécifiez pas la taille de l'index, MySQL choisit la taille maximale (i.e. 255 par colonne). Une contrainte UNIQUE ne peut pas être placée sur une colonne utf8 dont la longueur est supérieure à 255, parce qu'un index unique doit contenir la valeur entière de la cellule. Mais un indice régulier peut être utilisé - il sera il suffit d'indexer les premiers 255 caractères (ou les premiers 767 octets?). Et c'est là qu'il y a encore un mystère pour moi.

Le Mystère: Je peux voir pourquoi MySQL suppose 3 octets par caractère, pour la sécurité, parce que sinon la contrainte UNIQUE pourrait être cassée. Mais les docs semblent suggérer que l'index est en fait dimensionné en octets, pas en caractères. Donc, supposons que vous mettiez un 25 5 indice de char (765 octets) sur un varchar (25 6) de la colonne. Si les caractères que vous stockez sont tous ASCII, caractères de 1 octet, comme A-Z, a-z, 0-9, puis vous pouvez ajuster la colonne entière dans l'index de 767 octets. Et il semble que c'est ce qui serait effectivement se produire.

ci-dessous est un peu plus d'informations de ma réponse originale sur les caractères, octets, etc.


Selon wikipédia, UTF-8 caractère peut être 1,2, 3, ou 4 octets de long. Mais, selon cette documentation mysql, la taille maximale des caractères est de 3 octets, et ainsi, tout index d'index de colonne de plus de 255 caractères pourrait atteindre cette limite de byte. Mais si je comprends bien, ça pourrait ne pas l'être. Si la plupart de vos caractères sont dans la gamme ASCII, alors la taille moyenne de vos caractères sera plus proche de 1 octet. Si la taille moyenne de vos caractères est, par exemple, de 1,3 Byte (généralement 1 byte, mais un nombre significatif de 2-3 Byte caractères), alors vous pouvez spécifier un index de 767/1.3

Donc, si vous stockez surtout des personnages 1 octet, votre limite de caractères serait plus comme: 767 / 1,3 = 590. Mais il s'avère que ce n'est pas la façon dont il fonctionne. 255 caractères est la limite.

Comme indiqué dans le cette documentation MySQL,

Préfixe limite est mesurée en octets, alors que la longueur de préfixe dans Créer des instructions D'INDEX est interprété comme le nombre de caractères pour types de données non ordinaires (CHAR, VARCHAR, TEXT). Prendre cela en compte lorsqu'on spécifie une longueur de préfixe pour une colonne qui utilise un multi-octets caractère définir.

il semble que MySQL conseille aux gens de faire un calcul/estimation comme je viens de le faire afin de déterminer la taille de votre clé pour une colonne varchar. Mais en fait, vous ne peut pas spécifier un indice supérieur à 255 pour les colonnes utf8.

Enfin, si vous référer à mon deuxième lien, il y a aussi ceci:

lorsque l'option de configuration innodb_large_prefix est activée, ceci la limite de longueur est augmentée à 3072 octets, pour Les tables InnoDB qui utilisent Formats de lignes dynamiques et compressées.

il semble donc que vous pouvez obtenir des index beaucoup plus grands si vous le voulez, avec un peu de retouche. Assurez-vous juste que les formats de ligne sont dynamiques ou compressés. Vous pouvez probablement spécifier un index de 1023 ou 1024 caractères dans ce cas.


Au fait, il s'avère que vous pouvez stocker des caractères de 4 octets en utilisant le jeu de caractères utf8mb4. Le jeu de caractères utf8 apparemment les magasins seulement plan"0" caractères"<"!--28-->.

EDIT:

je viens d'essayer de créer un index composite sur une colonne de varchar(511) avec une colonne de tinyint(1) et j'ai reçu le message d'erreur disant que la taille de l'index max était de 767 octets. Cela me fait croire que MySQL suppose que les colonnes de jeu de caractères utf8 contiendront 3 octets par caractère (le maximum), et vous permet d'utiliser 255 caractères max. Mais peut-être que c'est seulement avec les indices composites. Je vais mettre à jour ma réponse comme je le découvre plus. Mais pour l'instant, je laisse ça comme un montage.

47
répondu Buttle Butkus 2017-06-02 04:56:02

Limites sur les Tables InnoDB

Avertissement

ne convertissez pas les tables système MySQL dans la base de données mysql de MyISAM à InnoDB tables. C'est une opération non prise en charge. Si vous faites cela, MySQL ne redémarre pas jusqu'à ce que vous restauriez les anciennes tables système à partir d'une sauvegarde ou les recréiez avec le programme mysql_install_db.

Avertissement

ce n'est pas une bonne idée de configurer InnoDB pour utiliser des fichiers de données ou des fichiers log sur NFS volume. Dans le cas contraire, les fichiers pourraient être verrouillés par D'autres processus et devenir indisponibles pour utilisation par MySQL.

Maximums et minima

  1. Un tableau peut contenir un maximum de 1000 colonnes.
  2. Un tableau peut contenir un maximum de 64 index secondaires.
  3. par défaut, une clé d'index pour un index à une seule colonne peut atteindre 767 octets. La même limite de longueur s'applique à tout préfixe de clé index. Par exemple, vous pouvez atteindre cette limite avec un préfixe de colonne de plus de 255 caractères sur un texte ou une colonne VARCHAR, en supposant un jeu de caractères UTF-8 et le maximum de 3 octets pour chaque caractère. Lorsque l'option de configuration innodb_large_prefix est activée, cette limite de longueur est augmentée à 3072 octets, pour les tables InnoDB qui utilisent les formats de lignes dynamique et compressée.
  4. si vous spécifiez une longueur de préfixe d'index qui est supérieure à la valeur maximale permise, la longueur est réduite silencieusement à la longueur maximale. Dans MySQL 5.6 et plus tard, le fait de spécifier une longueur de préfixe d'index supérieure à la longueur maximale produit une erreur.

lorsque innodb_large_prefix est activé, tenter de créer un préfixe d'index d'une longueur de clé supérieure à 3072 pour une table redondante ou compacte provoque une erreur ER_INDEX_COLUMN_TOO_LONG.

la longueur maximale de la clé interne InnoDB est de 3500 octets, mais MySQL elle-même la restreint à 3072 octets. Cette limite s'applique à la longueur de la clé d'index combinée dans un index multi-colonne.

la longueur maximale des lignes, sauf pour les colonnes de longueur variable (VARBINARY, VARCHAR, BLOB et TEXT), est légèrement inférieure à la moitié d'une page de base de données. Autrement dit, la longueur de ligne maximale est d'environ 8000 octets. Les colonnes LONGBLOB et LONGTEXT doivent être de moins de 4 Go, et la longueur totale des lignes, y compris les colonnes BLOB et texte, doit être inférieure à 4 Go.

Référence: InnoDB Restrictions

0
répondu Rads 2015-06-15 07:39:26