The Problem
There are max key length limits in MySQL.
- InnoDB — max key length is 1,536 bytes (for 8kb page size) and 768 (for 4kb page size) (Source: Dev.MySQL.com).
- MyISAM — max key length is 1,000 bytes (Source Dev.MySQL.com).
These are counted in bytes! So, a UTF-8 character may take more than one byte to be stored into the key.
Therefore, you have only two immediate solutions:
- Index only the first n’th characters of the text type.
- Create a
FULL TEXT
search — Everything will be Searchable within the Text, in a fashion similar to ElasticSearch
Indexing the First N’th Characters of a Text Type
If you are creating a table, use the following syntax to index some field’s first 255 characters: KEY
sometextkey (
SomeText(255))
. Like so:
CREATE TABLE `MyTable` (
`id` int(11) NOT NULL auto_increment,
`SomeText` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `sometextkey` (`SomeText`(255))
);
If you already have the table, then you can add a unique key to a field with: ADD UNIQUE(
ConfigValue(20));
. Like so:
ALTER TABLE
MyTable
ADD UNIQUE(`ConfigValue`(20));
If the name of the field is not a reserved MySQL keyword, then the backticks («`) are not necessary around the fieldname.
Creating a FULL TEXT Search
A Full Text search will allow you to search the entirety of the value of your TEXT
field. It will do whole-word matching if you use NATURAL LANGUAGE MODE
, or partial word matching if you use one of the other modes. See more on the options for FullText here: Dev.MySQL.com
Create your table with the text, and add the Full text index…
ALTER TABLE
MyTable
ADD FULLTEXT INDEX
`SomeTextKey` (`SomeTextField` DESC);
Then search your table like so…
SELECT
MyTable.id, MyTable.Title,
MATCH
(MyTable.Text)
AGAINST
('foobar' IN NATURAL LANGUAGE MODE) AS score
FROM
MyTable
HAVING
score > 0
ORDER BY
score DESC;
Я думаю многие из вас сталкивались с ошибкой при попытке запуска миграций в Laravel. Дело в том, что начиная с версии 5.4, Laravel использует другой набор символов для базы данных, а именно utf8m4
который поддерживает emoji (ну куда же без них). Однако, если вы используете в качестве движка базы данных MySQL v5.7.7 и выше, то все у вас будет работать из коробки, ничего делать не нужно.
А вот если версия MySQL ниже или же вы используете MariaDB (Привет пользователи XAMPP), при попытке запустить миграцию, вы получите примерно такую ошибку:
Migration table created successfully. In Connection.php line 664: SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`)) In Connection.php line 458: SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
Исправить такую ошибку на удивление очень легко. Открываем в редакторе файл app/Providers/AppServiceProvider.php
. Внутри метода boot
задаем длинну строки по умолчанию, вот так:
use IlluminateSupportFacadesSchema; public function boot() { Schema::defaultStringLength(191); }
Пробуем опять запустить миграции и всё должно работать.
You can increase the maximum InnoDB index prefix size in MySQL 5.6 to 3072 bytes by setting innodb_large_prefix
to ON
along with other settings that you’ll also need in order to enable that one, discussed here:
http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_large_prefix
These changes should allow these indexes to be valid for InnoDB tables.
With a character set of (I assume) utf8
, a VARCHAR(1024)
would need 1024 x 3 = 3072 bytes for its index.
Updates:
I incorrectly showed the value of the variable as Yes
when it should have been ON
.
But also:
I think it needs 3072+2 – ypercube
When I thought about this, it sounded correct, because 1 byte is needed to store the size of the value when the value is 255 bytes or less, and 2 bytes are needed otherwise.
However, testing reveals that this isn’t the case, in this case — InnoDB using the COMPRESSED
row format from Barracuda can actually index the full size of a VARCHAR(1024)
… so either they’ve documented it strangely or the COMPRESSED
row format stores the length out-of-band along with another block of metadata, so it doesn’t count in the total bytes in this format.
COMPRESSED
doesn’t truncate the index into a prefix index and throw a warning until you go to VARCHAR(1025)
.
+-------+------+----------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------+
| Error | 1071 | Specified key was too long; max key length is 3072 bytes |
+-------+------+----------------------------------------------------------+
It’s nice the way it throws a warning instead of throwing an error, but that doesn’t help us here because this still requires the explicit ROW_FORMAT
declaration to trigger this behavior.
So, my initial answer is still wrong, because you have to explicitly add ROW_FORMAT=COMPRESSED
to the end of the table definition. Otherwise you still get the same old error.
Actually, you get two different errors in 5.6.10. If you try to create a table with a fully-indexed VARCHAR(1024)
you get this:
ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
…but if you try with a fully-indexed VARCHAR(1025)
you get this:
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes
That’s sloppy code but the bottom line is that my answer doesn’t actually fix this problem.
I don’t see a way to use ROW_FORMAT=COMPRESSED
by default, nor does it seem like a good idea if it were possible, and I’m inclined to suggest that the answer is…
…there’s not a readily available workaround here. My other thought was «character sets» but the difference between latin1
and utf8
still isn’t sufficient to explain 1,024 vs 1000 or 767. I’ll happily get behind a better idea but at the moment, I can’t think of a version of MySQL Server that this code would work properly on.
After adding Schema::defaultStringLength(191);
to ApServiceProvider I run into this error when migrating
Migrating: 2021_02_23_202702_create_permission_tables
IlluminateDatabaseQueryException
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes (SQL: alter table `permissions` add unique `permissions_name_guard_name_unique`(`name`, `guard_name`))
at D:PROJECTvendorlaravelframeworksrcIlluminateDatabaseConnection.php:678
674▕ // If an exception occurs when attempting to run a query, we'll format the error
675▕ // message to include the bindings with SQL, which will make this exception a
676▕ // lot more helpful to the developer instead of just the database's errors.
677▕ catch (Exception $e) {
➜ 678▕ throw new QueryException(
679▕ $query, $this->prepareBindings($bindings), $e
680▕ );
681▕ }
682▕
1 D:PROJECTvendorlaravelframeworksrcIlluminateDatabaseConnection.php:471
PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes")
key length is 1000 bytes")
2 D:PROJECTvendorlaravelframeworksrcIlluminateDatabaseConnection.php:471
PDOStatement::execute()
PHP version:
PHP 7.3.21 (cli) (built: Aug 4 2020 11:21:19) ( ZTS MSVC15 (Visual C++ 2017) x64 )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.21, Copyright (c) 1998-2018 Zend Technologies
To Reproduce
If you have encountered the «Specified Key Was Too Long» error while working with MySQL, you are not alone. This error message can be frustrating and confusing, especially if you are not familiar with the underlying causes. In this guide, we will explain what this error means and provide a step-by-step solution to resolve it.
What is the ‘Specified Key Was Too Long’ Error?
The «Specified Key Was Too Long» error is a common error message that occurs when you try to add an index to a MySQL table. The error message usually reads as follows:
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes
This error message indicates that the length of the index key exceeds the maximum length of 767 bytes allowed in MySQL. This limitation is due to the fact that MySQL uses a three-byte prefix for each index key. Therefore, the maximum length of the index key is 255×3 = 765 bytes.
Why Does the ‘Specified Key Was Too Long’ Error Occur?
The ‘Specified Key Was Too Long’ error occurs when you try to create an index on a column that exceeds the maximum length of 767 bytes. This usually occurs when you use a character set that requires more than one byte per character, such as UTF-8.
How to Fix the ‘Specified Key Was Too Long’ Error?
To fix the ‘Specified Key Was Too Long’ error, you need to reduce the length of the index key. You can do this by either shortening the length of the column or by changing the character set to one that requires fewer bytes per character.
Here are the steps to fix the error:
Identify the column that is causing the error.
Determine the current length of the column by running the following command:
SHOW CREATE TABLE table_name;
Modify the column to reduce its length. For example, if the column is currently defined as follows:
varchar(500) CHARACTER SET utf8mb4
You can reduce the length to 255 characters as follows:
varchar(255) CHARACTER SET utf8mb4
Re-run the command to create the index.
FAQ
Q1. What is the maximum length of an index key in MySQL?
The maximum length of an index key in MySQL is 767 bytes.
Q2. What character sets require more than one byte per character in MySQL?
Character sets such as UTF-8, UTF-16, and UTF-32 require more than one byte per character in MySQL.
Q3. Can I increase the maximum length of an index key in MySQL?
No, you cannot increase the maximum length of an index key in MySQL. It is a hard limit set by MySQL.
Q4. What is the default character set in MySQL?
The default character set in MySQL is utf8mb4.
Q5. What is the difference between utf8 and utf8mb4 in MySQL?
utf8mb4 is a superset of utf8 and supports the full range of Unicode characters, while utf8 only supports a subset of Unicode characters.
- MySQL Documentation: Limits on InnoDB Tables
- MySQL Documentation: Specifying Character Sets and Collations