MySQL Error #1071 - Specified key was too long; max key length is 767 bytes

Viewed 842410

When I executed the following command:

ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);

I got this error message:

#1071 - Specified key was too long; max key length is 767 bytes

Information about column1 and column2:

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

I think varchar(20) only requires 21 bytes while varchar(500) only requires 501 bytes. So the total bytes are 522, less than 767. So why did I get the error message?

#1071 - Specified key was too long; max key length is 767 bytes
37 Answers

Replace utf8mb4 with utf8 in your import file.

enter image description here

5 workarounds:

The limit was raised in 5.7.7 (MariaDB 10.2.2?). And it can be increased with some work in 5.6 (10.1).

If you are hitting the limit because of trying to use CHARACTER SET utf8mb4. Then do one of the following (each has a drawback) to avoid the error:

⚈  Upgrade to 5.7.7 for 3072 byte limit -- your cloud may not provide this;
⚈  Change 255 to 191 on the VARCHAR -- you lose any values longer than 191 characters (unlikely?);
⚈  ALTER .. CONVERT TO utf8 -- you lose Emoji and some of Chinese;
⚈  Use a "prefix" index -- you lose some of the performance benefits.
⚈  Or... Stay with older version but perform 4 steps to raise the limit to 3072 bytes:

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
logout & login (to get the global values);
ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

-- http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes

For laravel 5.7 to 9.0

Steps to followed

  1. Go to App\Providers\AppServiceProvider.php.
  2. Add this to provider use Illuminate\Support\Facades\Schema; in top.
  3. Inside the Boot function Add this Schema::defaultStringLength(191);

that all, Enjoy.

To fix that, this works for me like a charm.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

I did some search on this topic finally got some custom change

For MySQL workbench 6.3.7 Version Graphical inter phase is available

  1. Start Workbench and select the connection.
  2. Go to management or Instance and select Options File.
  3. If Workbench ask you permission to read configuration file and then allow it by pressing OK two times.
  4. At center place Administrator options file window comes.
  5. Go To InnoDB tab and check the innodb_large_prefix if it not checked in the General section.
  6. set innodb_default_row_format option value to DYNAMIC.

For Versions below 6.3.7 direct options are not available so need to go with command prompt

  1. Start CMD as administrator.
  2. Go To director where mysql server is install Most of cases its at "C:\Program Files\MySQL\MySQL Server 5.7\bin" so command is "cd \" "cd Program Files\MySQL\MySQL Server 5.7\bin".
  3. Now Run command mysql -u userName -p databasescheema Now it asked for password of respective user. Provide password and enter into mysql prompt.
  4. We have to set some global settings enter the below commands one by one set global innodb_large_prefix=on; set global innodb_file_format=barracuda; set global innodb_file_per_table=true;
  5. Now at the last we have to alter the ROW_FORMAT of required table by default its COMPACT we have to set it to DYNAMIC.
  6. use following command alter table table_name ROW_FORMAT=DYNAMIC;
  7. Done

Index Lengths & MySQL / MariaDB


Laravel uses the utf8mb4 character set by default, which includes support for storing "emojis" in the database. If you are running a version of MySQL older than the 5.7.7 release or MariaDB older than the 10.2.2 release, you may need to manually configure the default string length generated by migrations in order for MySQL to create indexes for them. You may configure this by calling the Schema::defaultStringLength method within your AppServiceProvider:

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

Alternatively, you may enable the innodb_large_prefix option for your database. Refer to your database's documentation for instructions on how to properly enable this option.

Reference from blog : https://www.scratchcode.io/specified-key-too-long-error-in-laravel/

Reference from Official laravel documentation : https://laravel.com/docs/5.7/migrations

Just changing utf8mb4 to utf8 when creating tables solved my problem. For example: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; to CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;.

This solved my issue

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

In my case, I had this problem when I was backing up a database using the linux redirection output/input characters. Therefore, I change the syntax as described below. PS: using a linux or mac terminal.

Backup (without the > redirect)

# mysqldump -u root -p databasename -r bkp.sql

Restore (without the < redirect )

# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql

The error "Specified key was too long; max key length is 767 bytes" simple disappeared.

I found this query useful in detecting which columns had an index violating the max length:

SELECT
  c.TABLE_NAME As TableName,
  c.COLUMN_NAME AS ColumnName,
  c.DATA_TYPE AS DataType,
  c.CHARACTER_MAXIMUM_LENGTH AS ColumnLength,
  s.INDEX_NAME AS IndexName
FROM information_schema.COLUMNS AS c
INNER JOIN information_schema.statistics AS s
  ON s.table_name = c.TABLE_NAME
 AND s.COLUMN_NAME = c.COLUMN_NAME 
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.CHARACTER_MAXIMUM_LENGTH > 191 
  AND c.DATA_TYPE IN ('char', 'varchar', 'text')

Due to prefix limitations this error will occur. 767 bytes is the stated prefix limitation for InnoDB tables in MySQL versions before 5.7 . It's 1,000 bytes long for MyISAM tables. In MySQL version 5.7 and upwards this limit has been increased to 3072 bytes.

Running the following on the service giving you the error should resolve your issue. This has to be run in the MYSQL CLI.

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;

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;

I have changes from varchar to nvarchar, works for me.

My own solution for this problem was a bit more simple and less dangerous than lowering the VARCHAR size of tables.

Situation: A CentOS 7 server running Plesk Obsidian 18.0.37 with MariaDB 5.5. I was trying to import a MySQL dump from a server running MariaDB 10.1.

Solution: Upgrading from MariaDB 5.5 to 10.6.

The steps were roughly based on this guide and this one:

  1. mysqldump -u admin -p`cat /etc/psa/.psa.shadow` --all-databases --routines --triggers > /root/all-databases.sql
  2. systemctl stop mariadb
  3. cp -a /var/lib/mysql/ /var/lib/mysql_backup
  4. Configure MariaDB repositories according to the official guide
    Make sure you meet Plesk's minimum version requirements detailed here
  5. yum install MariaDB-client MariaDB-server MariaDB-compat MariaDB-shared
  6. systemctl start mariadb
    In my case, the server failed to start here with an error: "Can't start server: Bind on TCP/IP port. Got error: 22: Invalid argument".
    The fix was to replace bind-address as follows in /etc/my.cnf and re-run the command:
    [mysqld]
    # OLD (broken)
    #bind-address = ::ffff:127.0.0.1
    # NEW
    bind-address = 127.0.0.1
    
  7. MYSQL_PWD=`cat /etc/psa/.psa.shadow` mysql_upgrade -uadmin
  8. plesk sbin packagemng -sdf
  9. rm -f /etc/init.d/mysql
  10. systemctl daemon-reload

OK , in my situation , I have to restore a database file from mySQL 5.7 to mySQL 5.6, and I met this problem.

the root cause is the version incompatible, and and some column which is indexed is longer than 191 (default is 255)

so the solution is quite simple: make all the "indexed columns' length" to be a number less than 191 (e.g 180)

enter image description here

My fix to this very same issue was to add an option as a 3rd argument: charset

queryInterface.createTable(
  tableName,
  { /*... columns*/ },
  { charset: 'utf8' } 
)

Otherwise sequelize would create tables as utf8mb4.

Related