Długość indeksu MySQL varchar

Mam taką tabelę:

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;

I taki:

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;

I takie polecenie 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;

Co jeśli wyjaśnię daje mi to:

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

Jak na milion rzędów, to jest dość powolne. Próbowałem dodać indeks na products.name z:

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

Co daje:

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)

Myślę, że kolumna Sub_part pokazuje przedrostek, który został w indeksowane (w bajtach), jak opisano na tej stronie .

Kiedy ponownie wyjaśnij zapytanie, otrzymuję:

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

Co wygląda na to, że nowy indeks nie jest używany. Zgodnie z opisem na ta strona , indeksy nie będą używane do sortowania, jeśli są indeksy przedrostkowe. W rzeczywistości, jeśli obcinam dane za pomocą:

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

Objaśnienie daje:

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

Co myślę, że to potwierdza. Jednak na tej stronie jest napisane, że Tabele InnoDB mogą mieć do 767 bajtów indeksu. Jeśli długość jest w bajtów, dlaczego nie ma więcej niż 255? Jeśli jest w znaki, jak decyduje długość każdego znaku UTF-8? Na zakładając tylko 3?

Również używam tej wersji MySQL:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.27-log |
+------------+
1 row in set (0.00 sec)
Author: Gras Double, 2013-03-01

2 answers

Muszę poprawić swoją odpowiedź ze względu na moje badania. Oryginalnie to zamieściłem (cytując siebie):

Wierzę, że odpowiedź jest taka, że nie wiesz, ile znaków będzie być w indeksie, bo nie wiadomo ile bajtów ma twoje znaki będzie (chyba, że zrobisz coś, aby wykluczyć wielobajtowe znaki).

I nie jestem pewien, ale to może być poprawne, ale nie w taki sposób, jak myślałem.

Oto prawidłowa odpowiedź:

MySQL przyjmuje 3 bajty na znak utf8. 255 znaków to maksymalny rozmiar indeksu, który można określić dla kolumny, ponieważ 256x3=768, co łamie limit 767 bajtów.

Jeśli nie podasz rozmiaru indeksu, MySQL wybierze maksymalny rozmiar (tj. 255 na kolumnę). Unikalne ograniczenie nie może być zastosowane do kolumny utf8, której długość jest większa niż 255, ponieważ unikalny indeks musi zawierać całą wartość komórki. Ale można użyć zwykłego indeksu - indeksuje tylko pierwsze 255 znaków (lub pierwsze 767 bajtów?). I tam jest jeszcze jakaś tajemnica dla mnie.

Tajemnica: Rozumiem, dlaczego MySQL zakłada 3 bajty na znak, dla bezpieczeństwa, ponieważ w przeciwnym razie unikalne ograniczenie może zostać złamane. Ale dokumenty wydają się sugerować, że indeks jest rzeczywiście wielkości w bajtach, a nie znaków. Więc załóżmy, że umieścisz 255 indeks char (765 bajtów) na warcharze(256) kolumna. Jeśli przechowywane znaki są znakami ASCII, 1-bajtowymi, takimi jak A-Z, a-Z, 0-9, wtedy możesz dopasować cała kolumna w indeksie 767 bajtów. I wygląda na to, że tak by się stało.

Poniżej kilka informacji z mojej oryginalnej odpowiedzi na temat znaków, bajtów itp.


Według Wikipedii, znak UTF-8 może mieć długość 1,2, 3 lub 4 bajtów. Ale, zgodnie z ta dokumentacja mysql , maksymalny rozmiar znaków wynosi 3 bajty, a więc indeks kolumny indeks ponad 255 znaków może trafić ten limit bajtów. Ale jak to rozumiem, to może nie. Jeśli większość znaków znajduje się w zakresie ASCII, średni rozmiar znaków będzie zbliżony do 1 bajtu. Jeśli średni rozmiar znaków wynosi na przykład 1,3 bajtów (głównie 1 bajt, ale znaczna liczba znaków 2-3 bajtowych), możesz podać indeks 767/1.3

Więc, jeśli przechowujesz głównie 1-bajtowe znaki, Twój rzeczywisty limit znaków będzie bardziej podobny: 767 / 1.3 = 590. Ale okazuje się, że to nie tak działa. 255 znaków to limit.

Jak wspomniano w tej dokumentacji MySQL ,

Granice przedrostka są mierzone w bajtach, natomiast długość przedrostka w Polecenia CREATE INDEX są interpretowane jako liczba znaków dla nonbinary typy danych (CHAR, varchar, TEXT). Weź to pod uwagę przy określaniu długości przedrostka dla kolumny, która używa wielobajtu zestaw znaków.

Wygląda na to, że MySQL radzi ludziom zrobić obliczenia/guestimation tak jak ja właśnie zrobiłem w celu aby określić rozmiar klucza dla kolumny varchar. Ale w rzeczywistości nie można określić indeksu większego niż 255 dla kolumn utf8.

Na koniec, jeśli jeszcze raz odwołasz się do mojego drugiego linku, jest też to:

Gdy opcja konfiguracji innodb_large_prefix jest włączona, To limit długości jest podniesiony do 3072 bajtów, dla tabel InnoDB, które używają Dynamiczne i skompresowane formaty wierszy.

Więc wygląda na to, że możesz uzyskać znacznie większe indeksy, jeśli chcesz, z odrobiną poprawek. Po prostu upewnij się, że formaty wierszy są dynamiczne lub skompresowane. Możesz prawdopodobnie podać indeks 1023 lub 1024 znaków w tym przypadku.


Przy okazji, okazuje się, że można przechowywać 4-bajtowe znaki za pomocą zestawu znaków utf8mb4. Zestaw znaków utf8 przechowuje jedynie znaki "płaszczyzna 0" .

EDIT:

Właśnie próbowałem utworzyć indeks złożony na kolumnie varchar (511) z kolumną tinyint(1) i dostałem komunikat o błędzie mówiący, że maksymalny rozmiar indeksu wynosił 767 bajtów. To sprawia, że myślę, że MySQL zakłada, że kolumny zestawu znaków utf8 będą zawierały 3 bajty na znak (maksimum) i pozwalają na użycie maksymalnie 255 znaków. Ale być może jest to tylko z indeksów kompozytowych. Zaktualizuję swoją odpowiedź, gdy dowiem się więcej. Ale na razie zostawiam to jako edycję.

 47
Author: Buttle Butkus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-02 04:56:02

Limity tabel InnoDB

Warning

Nie konwertuje tabel systemowych MySQL w bazie danych mysql z tabel MyISAM na InnoDB. To nieobsługiwana operacja. Jeśli to zrobisz, MySQL nie uruchamia się ponownie, dopóki nie przywrócisz starych tabel systemowych z kopii zapasowej lub ponownie wygenerujesz je za pomocą programu mysql_install_db.

Warning

Nie jest dobrym pomysłem skonfigurowanie InnoDB do używania plików danych lub plików dziennika na woluminach NFS. W przeciwnym razie pliki mogą być zablokowane przez inne procesy i stają się niedostępne do użycia przez MySQL.

Maksimum i minimum

  1. tabela może zawierać maksymalnie 1000 kolumn.
  2. tabela może zawierać maksymalnie 64 indeksy wtórne.
  3. domyślnie klucz indeksu dla indeksu jednokolumnowego może wynosić do 767 bajtów. Ten sam limit długości odnosi się do każdego prefiksu klucza indeksu. Na przykład, możesz osiągnąć ten limit z indeksem prefiksu kolumny ponad 255 znaków w tekście lub WARCZARZE kolumny, zakładając zestaw znaków UTF-8 i maksymalnie 3 bajty dla każdego znaku. Gdy opcja konfiguracyjna innodb_large_prefix jest włączona, limit długości jest zwiększany do 3072 bajtów dla tabel InnoDB, które używają dynamicznych i skompresowanych formatów wierszy.
  4. jeśli określisz przedrostek indeksu długości, który jest większy niż dozwolona maksymalna wartość, Długość zostanie po cichu zmniejszona do maksymalnej długości. W MySQL 5.6 i nowszych, określanie długości przedrostka indeksu większej niż maksymalna długość powoduje błąd.

Gdy opcja innodb_large_prefix jest włączona, próba utworzenia prefiksu indeksu o długości klucza większej niż 3072 dla nadmiarowej lub zwartej tabeli powoduje błąd ER_INDEX_COLUMN_TOO_LONG.

Wewnętrzna Maksymalna długość klucza InnoDB wynosi 3500 bajtów, ale MySQL ogranicza to do 3072 bajtów. Limit ten odnosi się do długości połączonego klucza indeksu w indeksie wielokolumnowym.

Maksymalna długość wiersza, z wyjątkiem kolumn o zmiennej długości (VARBINARY, VARCHAR, BLOB i TEXT), to nieco mniej niż połowa strony bazy danych. Oznacza to, że maksymalna długość wiersza wynosi około 8000 bajtów. Kolumny LONGBLOB i LONGTEXT muszą być mniejsze niż 4 GB, a całkowita długość wierszy, w tym kolumn BLOB i TEXT, musi być mniejsza niż 4 GB.

Odniesienie: Innodbiór

 0
Author: Rads,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-15 07:39:26