一、背景
今天把服务器的数据库导出了一份sql文件,准备导入到本地,但是在导入的时候,报了个错:
Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
这就很奇怪了,明明服务器上都可以,凭什么我这边就报错呢。
二、错误分析
1、错误部分的sql文件
CREATE TABLE `model_has_permissions` (
`permission_id` int(10) unsigned NOT NULL,
`model_id` int(10) unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
就是这个primary key部分报错的。错误信息的意思是,设置的键长超过了767 bytes。
2、这个767 bytes是什么玩意?
答:
(1)
在mysql 5.5.3之前,mysql的InnoDB引擎,要求设置的主键长度不得超过767bytes。
mysql的MyIsam引擎的主键长度不得超过1000 bytes。
(2)
在mysql中,gbk字符集会占用2个字节。utf8字符会占用3个字节。
而且从mysql5.5.3之后的版本,mysql 开始支持utf8m4字符,代表着一个字符占用4个字节。
也就是说:
(255+10+10)*3 = 825 //在用utf8作为字符集的时候,超过了规定的767 bytes
(255+10+10)*2 = 550 //当该用gbk作为字符集的时候
(255+10+10)*4 = 1100 //当用utf8m4作为字符集的时候,也超标了
3、大致原因知道之后,查看sql文件
(1)、数据库使用的InnoDB引擎
(2)、数据库使用utf8m4作为字符集
三、解决办法
1、修改字符长度
//根据上面的分析可以进行计算,我的主键长度不能超过192
768/4 = 192
但这样很明显是不符合的需求的,不能随便改动数据库的字段!
2、升级mysql
这个方案是在查询服务器数据库版本的时候,发现服务器数据库采用的是mysql5.7版本。。也就是说在升级数据库之后,是完全可以达到的。。
后面再查询中发现一个国外的帖子:
链接:
https://stackoverflow.com/questions/1814532/1071-specified-key-was-too-long-max-key-length-is-767-bytes
原文:
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.
原文的意思是说,在mysql的5.5.3版本之前,InnoDB引擎的主键对应的最大字节数是767字节,MyISAM对应的主键最大字节是1000字节。但是在mysql5.7版本之后,最大主键字节增大为3072字节。
OK,这样就很明显了,升级mysql是最佳的选择。用集成环境的小伙伴可以关闭集成环境中的mysql,然后下载最新的mysql版本即可。
四、在解决问题时,学到的东西
1、查看数据库的存储引擎
//进入数据库,执行这个命令
show variables like '%storage_engine%';
2、查看当前数据库的字符编码
show variables like '%character_set%';
3、查看数据库的版本号
//进入数据库之后,执行status即可
mysql>status
4、关于varchar
MySQL5.0.3之前varchar(n)这里的n表示字节数
MySQL5.0.3之后varchar(n)这里的n表示字符数,比如varchar(200),不管是英文还是中文都可以存放200个
5、关于用varchar作为主键,不好的地方
varchar相对于int来说占用磁盘空间多,磁盘io也会多,然后内存带宽也会多。这点上尤其在innodb更为明显,innodb表的Secondary index的 leaf page中都要保存primary key的值,主键如果是varchar,会导致secondary index的体积会比较大。而且varchar主键在比较上也会慢一些,插入时容易发送数据的非顺序插入,导致碎片,index tree效率比int低
end