web-dev-qa-db-fra.com

Longueur de l'index varchar MySQL

J'ai une table comme celle-ci:

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 un comme ça:

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 celle-ci

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 lignes, c'est assez lent. J'ai essayé d'ajouter un index sur products.name 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), comme décrit sur cette page .

Lorsque je ré-explique 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 que le nouvel index n'est pas utilisé. Comme décrit sur cette page , les index ne seront pas utilisés pour le tri s'ils sont des index préfixés. En fait, si je tronque les données avec:

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

L'explication 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 |       |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+

qui, je pense, le confirme. Cependant, il est indiqué sur cette page que les tables InnoDB peuvent avoir jusqu'à 767 octets d'index. Si la longueur est en octets, pourquoi refuse-t-elle d'en avoir plus de 255? Si c'est en caractères, comment décide-t-il de la longueur de chaque caractère UTF-8? Est-ce juste en supposant 3?

J'utilise également cette version de MySQL:

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

Limites des tables InnoDB

Avertissement

Ne convertissez pas les tables système MySQL de la base de données mysql de MyISAM en tables InnoDB. Il s'agit d'une opération non prise en charge. Dans ce cas, MySQL ne redémarre pas tant que vous n'avez pas restauré les anciennes tables système à partir d'une sauvegarde ou que vous ne les régénérez pas 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 journaux sur des volumes NFS. Sinon, les fichiers pourraient être verrouillés par d'autres processus et devenir indisponibles pour MySQL.

Maximums et minimums

  1. Une table peut contenir au maximum 1 000 colonnes.
  2. Une table peut contenir un maximum de 64 index secondaires.
  3. Par défaut, une clé d'index pour un index à colonne unique peut aller jusqu'à 767 octets. La même limite de longueur s'applique à tout préfixe de clé d'index. Par exemple, vous pouvez atteindre cette limite avec un index de préfixe de colonne de plus de 255 caractères sur une colonne TEXT ou VARCHAR, en supposant un jeu de caractères UTF-8 et un 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 ligne DYNAMIC et COMPRESSED.
  4. Si vous spécifiez une longueur de préfixe d'index supérieure à la valeur maximale autorisée, la longueur est réduite silencieusement à la longueur maximale. Dans MySQL 5.6 et versions ultérieures, la spécification d'une longueur de préfixe d'index supérieure à la longueur maximale produit une erreur.

Lorsque innodb_large_prefix est activé, la tentative de création d'un préfixe d'index avec une longueur de clé supérieure à 3072 pour une table REDUNDANT ou COMPACT provoque une erreur ER_INDEX_COLUMN_TOO_LONG.

La longueur de clé maximale interne InnoDB est de 3500 octets, mais MySQL lui-même le limite à 3072 octets. Cette limite s'applique à la longueur de la clé d'index combinée dans un index à plusieurs colonnes.

La longueur maximale des lignes, à l'exception des 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 8 000 octets. Les colonnes LONGBLOB et LONGTEXT doivent être inférieures à 4 Go et la longueur totale des lignes, y compris les colonnes BLOB et TEXT, doit être inférieure à 4 Go.

Référence: Restrictions InnoDB

1
Rads