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
What character encoding are you using? Some character sets (like UTF-16, et cetera) use more than one byte per character.
767 bytes is the stated prefix limitation for InnoDB tables in MySQL version 5.6 (and prior versions). It's 1,000 bytes long for MyISAM tables. In MySQL version 5.7 and upwards this limit has been increased to 3072 bytes.
You also have to be aware that if you set an index on a big char or varchar field which is utf8mb4 encoded, you have to divide the max index prefix length of 767 bytes (or 3072 bytes) by 4 resulting in 191. This is because the maximum length of a utf8mb4 character is four bytes. For a utf8 character it would be three bytes resulting in max index prefix length of 254.
One option you have is to just place lower limit on your VARCHAR fields.
Another option (according to the response to this issue) is to get the subset of the column rather than the entire amount, i.e.:
Tweak as you need to get the key to apply, but I wonder if it would be worth it to review your data model regarding this entity to see if there's improvements that would allow you to implement the intended business rules without hitting the MySQL limitation.
I fixed this issue with :
replaced with
all the varchar which have more than 200 replace them with 191 or set them text.
When you hit the limit. Set the following.
utf8
VARCHAR(255)
utf8mb4
VARCHAR(191)
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:
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 usedlatin1_general_ci
which uses only one byte per character and so column can have 767 bytes length.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 addKEY
index to original SEO column to speed up look up.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 - likeunique_key
here: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.