web-dev-qa-db-ja.com

MySQL varcharインデックスの長さ

このようなテーブルがあります:

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;

そしてこのようなもの:

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;

そして、このようなSQLステートメント

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;

説明すると、これがわかります:

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

100万行の場合、これはかなり遅いです。私はproducts.nameにインデックスを追加しようとしました:

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

これはこれを与えます:

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)

このページ で説明されているように、Sub_part列にはインデックス付きのプレフィックスが表示されていると思います(バイト単位)。

クエリを再説明すると、次の結果が得られます。

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

新しいインデックスは使用されていないようです。 このページ で説明されているように、インデックスがプレフィックスインデックスである場合、インデックスはソートに使用されません。実際、次のようにしてデータを切り捨てた場合:

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

Explainは以下を提供します。

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

それを裏付けていると思います。ただし、 このページ では、InnoDBテーブルは最大767バイトのインデックスを持つことができると述べています。長さがバイト単位の場合、なぜ255を超えることを拒否するのですか?文字単位の場合、各UTF-8文字の長さはどのように決定されますか? 3だけを想定していますか?

また、このバージョンのMySQLを使用しています。

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

InnoDBテーブルの制限

警告

MysqlデータベースのMySQLシステムテーブルをMyISAMからInnoDBテーブルに変換しないでください。これはサポートされていない操作です。これを行うと、バックアップから古いシステムテーブルを復元するか、mysql_install_dbプログラムでそれらを再生成するまで、MySQLは再起動しません。

警告

NFSボリューム上のデータファイルまたはログファイルを使用するようにInnoDBを構成することはお勧めできません。そうしないと、ファイルが他のプロセスによってロックされ、MySQLで使用できなくなる可能性があります。

最大値と最小値

  1. テーブルには最大1000列を含めることができます。
  2. テーブルには、最大64個のセカンダリインデックスを含めることができます。
  3. 既定では、単一列インデックスのインデックスキーは最大767バイトです。同じ長さの制限は、すべてのインデックスキープレフィックスに適用されます。たとえば、UTF-8文字セットと各文字の最大3バイトを想定して、TEXTまたはVARCHAR列で255文字を超える列プレフィックスインデックスでこの制限に達する可能性があります。 innodb_large_prefix構成オプションが有効な場合、DYNAMICおよびCOMPRESSED行フォーマットを使用するInnoDBテーブルの場合、この長さ制限は3072バイトに引き上げられます。
  4. 許容される最大値よりも長いインデックスプレフィックス長を指定した場合、長さは暗黙的に最大長まで短縮されます。 MySQL 5.6以降では、最大長より長いインデックスプレフィックス長を指定するとエラーが発生します。

Innodb_large_prefixが有効な場合、REDUNDANTまたはCOMPACTテーブルに対して3072を超えるキー長でインデックスプレフィックスを作成しようとすると、ER_INDEX_COLUMN_TOO_LONGエラーが発生します。

InnoDBの内部最大キー長は3500バイトですが、MySQL自体はこれを3072バイトに制限しています。この制限は、複数列インデックスの結合インデックスキーの長さに適用されます。

可変長列(VARBINARY、VARCHAR、BLOB、TEXT)を除く最大行長は、データベースページの半分よりわずかに短いです。つまり、最大行長は約8000バイトです。 LONGBLOBおよびLONGTEXT列は4GB未満である必要があり、BLOBおよびTEXT列を含む行の合計長は4GB未満である必要があります。

リファレンス: InnoDB Restrictions

1
Rads