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

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

This question is related to mysql byte varchar mysql-error-1071

The answer is


If anyone is having issues with INNODB / Utf-8 trying to put an UNIQUE index on a VARCHAR(256) field, switch it to VARCHAR(255). It seems 255 is the limitation.


If you're creating something like:

CREATE TABLE IF NOT EXISTS your_table (
  id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  name varchar(256) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name (name)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

it should be something like

CREATE TABLE IF NOT EXISTS your_table (
      id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(256) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

but you need to check uniqueness of that column from code or adding a new column as an MD5 or SHA1 of the varchar column


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

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

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


Solution For Laravel Framework

As per Laravel 5.4.* documentation; You have to set the default string length inside the boot method of the app/Providers/AppServiceProvider.php file as follows:

use Illuminate\Support\Facades\Schema;

public function boot() 
{
    Schema::defaultStringLength(191); 
}

Explanation of this fix, given by Laravel 5.4.* documentation:

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.

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.


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;.


you could add an column of the md5 of long columns


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

This solved my issue

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;


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;

We encountered this issue when trying to add a UNIQUE index to a VARCHAR(255) field using utf8mb4. While the problem is outlined well here already, I wanted to add some practical advice for how we figured this out and solved it.

When using utf8mb4, characters count as 4 bytes, whereas under utf8, they could as 3 bytes. InnoDB databases have a limit that indexes can only contain 767 bytes. So when using utf8, you can store 255 characters (767/3 = 255), but using utf8mb4, you can only store 191 characters (767/4 = 191).

You're absolutely able to add regular indexes for VARCHAR(255) fields using utf8mb4, but what happens is the index size is truncated at 191 characters automatically - like unique_key here:

Sequel Pro screenshot showing index truncated at 191 characters

This is fine, because regular indexes are just used to help MySQL search through your data more quickly. The whole field doesn't need to be indexed.

So, why does MySQL truncate the index automatically for regular indexes, but throw an explicit error when trying to do it for unique indexes? Well, for MySQL to be able to figure out if the value being inserted or updated already exists, it needs to actually index the whole value and not just part of it.

At the end of the day, if you want to have a unique index on a field, the entire contents of the field must fit into the index. For utf8mb4, this means reducing your VARCHAR field lengths to 191 characters or less. If you don't need utf8mb4 for that table or field, you can drop it back to utf8 and be able to keep your 255 length fields.


change your collation. You can use utf8_general_ci that supports almost all


Specified key was too long; max key length is 767 bytes

You got that message because 1 byte equals 1 character only if you use the latin-1 character set. If you use utf8, each character will be considered 3 bytes when defining your key column. If you use utf8mb4, each character will be considered to be 4 bytes when defining your key column. Thus, you need to multiply your key field's character limit by, 1, 3, or 4 (in my example) to determine the number of bytes the key field is trying to allow. If you are using uft8mb4, you can only define 191 characters for a native, InnoDB, primary key field. Just don't breach 767 bytes.


Here is my original answer:

I just drop database and recreate like this, and the error is gone:

drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;

However, it doesn't work for all the cases.

It is actually a problem of using indexes on VARCHAR columns with the character set utf8 (or utf8mb4), with VARCHAR columns that have more than a certain length of characters. In the case of utf8mb4, that certain length is 191.

Please refer to the Long Index section in this article for more information how to use long indexes in MySQL database: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4


Replace utf8mb4 with utf8 in your import file.

enter image description here


MySQL assumes worst case for the number of bytes per character in the string. For the MySQL 'utf8' encoding, that's 3 bytes per character since that encoding doesn't allow characters beyond U+FFFF. For the MySQL 'utf8mb4' encoding, it's 4 bytes per character, since that's what MySQL calls actual UTF-8.

So assuming you're using 'utf8', your first column will take 60 bytes of the index, and your second another 1500.


If you have changed innodb_log_file_size recently, try to restore the previous value which worked.


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


Change CHARSET of the complaining index field to "latin1"
i.e. ALTER TABLE tbl CHANGE myfield myfield varchar(600) CHARACTER SET latin1 DEFAULT NULL;
latin1 takes one byte for one character instead of four


Please check if sql_mode is like

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

if it is, change to

sql_mode=NO_ENGINE_SUBSTITUTION

OR

restart your server changing your my.cnf file (putting following)

innodb_large_prefix=on

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


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.


What character encoding are you using? Some character sets (like UTF-16, et cetera) use more than one byte per character.


For me, the issue of "#1071 - Specified key was too long; max key length is 767 bytes" got resolved after changing the primarykey / uniquekey combination by limiting the column size by 200.

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

run this query before your query:

SET @@global.innodb_large_prefix = 1;

this will increase limit to 3072 bytes.


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

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?

UTF8 requires 3 bytes per character to store the string, so in your case 20 + 500 characters = 20*3+500*3 = 1560 bytes which is more than allowed 767 bytes.

The limit for UTF8 is 767/3 = 255 characters, for UTF8mb4 which uses 4 bytes per character it is 767/4 = 191 characters.


There are two solutions to this problem if you need to use longer column than the limit:

  1. Use "cheaper" encoding (the one that requires less bytes per character)
    In my case, I needed to add Unique index on column containing SEO string of article, as I use only [A-z0-9\-] characters for SEO, I used latin1_general_ci which uses only one byte per character and so column can have 767 bytes length.
  2. Create hash from your column and use unique index only on that
    The other option for me was to create another column which would store hash of SEO, this column would have UNIQUE key to ensure SEO values are unique. I would also add KEY index to original SEO column to speed up look up.

The answer about why you get error message was already answered by many users here. My answer is about how to fix and use it as it be.

Refer from this link.

  1. Open MySQL client (or MariaDB client). It is a command line tool.
  2. It will ask your password, enter your correct password.
  3. Select your database by using this command use my_database_name;

Database changed

  1. set global innodb_large_prefix=on;

Query OK, 0 rows affected (0.00 sec)

  1. set global innodb_file_format=Barracuda;

Query OK, 0 rows affected (0.02 sec)

  1. Go to your database on phpMyAdmin or something like that for easy management. > Select database > View table structure > Go to Operations tab. > Change ROW_FORMAT to DYNAMIC and save changes.
  2. Go to table's structure tab > Click on Unique button.
  3. Done. Now it should has no errors.

The problem of this fix is if you export db to another server (for example from localhost to real host) and you cannot use MySQL command line in that server. You cannot make it work there.


For laravel 5.7 to 6.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.


When you hit the limit. Set the following.

  • INNODB utf8 VARCHAR(255)
  • INNODB utf8mb4 VARCHAR(191)

I fixed this issue with :

varchar(200) 

replaced with

varchar(191)

all the unique or primary varchar keys which have more than 200 replace them with 191 or set them as text.


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.


Based on the column given below, those 2 variable string columns are using utf8_general_ci collation (utf8 charset is implied).

In MySQL, utf8 charset uses a maximum of 3 bytes for each character. Thus, it would need to allocate 500*3=1500 bytes, which is much greater than the 767 bytes MySQL allows. That's why you are getting this 1071 error.

In other words, you need to calculate the character count based on the charset's byte representation as not every charset is a single byte representation (as you presumed.) I.E. utf8 in MySQL is uses at most 3-byte per character, 767/3˜255 characters, and for utf8mb4, an at most 4-byte representation, 767/4˜191 characters.

It's also known that MySQL

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

Examples related to mysql

Implement specialization in ER diagram How to post query parameters with Axios? PHP with MySQL 8.0+ error: The server requested authentication method unknown to the client Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver' phpMyAdmin - Error > Incorrect format parameter? Authentication plugin 'caching_sha2_password' is not supported How to resolve Unable to load authentication plugin 'caching_sha2_password' issue Connection Java-MySql : Public Key Retrieval is not allowed How to grant all privileges to root user in MySQL 8.0 MySQL 8.0 - Client does not support authentication protocol requested by server; consider upgrading MySQL client

Examples related to byte

Convert bytes to int? TypeError: a bytes-like object is required, not 'str' when writing to a file in Python3 How many bits is a "word"? How many characters can you store with 1 byte? Save byte array to file Convert dictionary to bytes and back again python? How do I get total physical memory size using PowerShell without WMI? Hashing with SHA1 Algorithm in C# How to create a byte array in C++? Converting string to byte array in C#

Examples related to varchar

SQL Server date format yyyymmdd What does it mean when the size of a VARCHAR2 in Oracle is declared as 1 byte? Difference between VARCHAR and TEXT in MySQL PostgreSQL: ERROR: operator does not exist: integer = character varying Can I use VARCHAR as the PRIMARY KEY? Is the LIKE operator case-sensitive with MSSQL Server? How to convert Varchar to Double in sql? SQL Server : error converting data type varchar to numeric What is the MySQL VARCHAR max size? SQL Server Convert Varchar to Datetime

Examples related to mysql-error-1071

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