执行以下命令时:
ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);
我得到了这个错误信息:
#1071 - Specified key was too long; max key length is 767 bytes
columnn1和column2的信息:
column1 varchar(20) utf8_general_ci
column2 varchar(500) utf8_general_ci
我认为varchar(20)只需要21个字节,而varchar(500)只需要501个字节。所以总字节数是522,小于767。为什么我得到了错误消息?
#1071 - Specified key was too long; max key length is 767 bytes
对于这个问题,我自己的解决方案比降低表的VARCHAR大小更简单,也更安全。
情况:CentOS 7服务器运行Plesk Obsidian 18.0.37和MariaDB 5.5。我试图从运行MariaDB 10.1的服务器导入MySQL转储。
解决方案:从MariaDB 5.5升级到10.6。
这些步骤大致基于以下指南和以下指南:
mysqldump -u admin -p`cat /etc/psa/.psa.shadow` --all-databases --routines --triggers > /root/all-databases.sql
systemctl stop mariadb
cp -a /var/lib/mysql/ /var/lib/mysql_backup
Configure MariaDB repositories according to the official guide
Make sure you meet Plesk's minimum version requirements detailed here
yum install MariaDB-client MariaDB-server MariaDB-compat MariaDB-shared
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
MYSQL_PWD=`cat /etc/psa/.psa.shadow` mysql_upgrade -uadmin
plesk sbin packagemng -sdf
rm -f /etc/init.d/mysql
systemctl daemon-reload
在MySQL 5.6(以及之前的版本)中,767字节是InnoDB表的前缀限制。MyISAM表有1000字节长。在MySQL版本5.7(及以上)中,此限制已增加到3072字节。
您还必须注意,如果在utf8mb4编码的大char或varchar字段上设置索引,则必须将最大索引前缀长度767字节(或3072字节)除以4,结果为191。这是因为utf8mb4字符的最大长度是4个字节。对于utf8字符,它将是三个字节,导致最大索引前缀长度为255(或减去空结束符,254个字符)。
一个选择是在VARCHAR字段上设置下限。
另一种选择(根据对这个问题的响应)是获取列的子集,而不是整个数量,即:
ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );
根据需要进行调整以获得要应用的键,但我想知道是否值得检查关于这个实体的数据模型,看看是否有可能进行改进,从而允许您在不触及MySQL限制的情况下实现预期的业务规则。
根据下面给出的列,这两个变量字符串列使用utf8_general_ci排序规则(隐含utf8字符集)。
在MySQL中,utf8字符集每个字符最多使用3个字节。因此,它需要分配500*3=1500字节,这比MySQL允许的767字节要大得多。这就是为什么您会得到1071错误。
换句话说,您需要基于字符集的字节表示来计算字符数,因为并非每个字符集都是一个字节表示(正如您所假设的那样)。例如,MySQL中的utf8每个字符最多使用3个字节,767/3≈255个字符,而对于utf8mb4,最多使用4个字节表示,767/4≈191个字符。
众所周知,MySQL
column1 varchar(20) utf8_general_ci
column2 varchar(500) utf8_general_ci
这个问题
MySQL中有最大键长度限制。
InnoDB -最大密钥长度为1536字节(8kb页面大小)和768字节(4kb页面大小)(来源:Dev.MySQL.com)
MyISAM -最大密钥长度是1000字节(来源Dev.MySQL.com)。
这些都是以字节为单位计算的!因此,一个UTF-8字符可能需要一个以上的字节才能存储到密钥中。
因此,你只有两个直接的解决方案:
只索引文本类型的前n个字符。
创建一个全文搜索-所有内容都可以在文本中搜索,以一种类似ElasticSearch的方式
索引文本类型的前N个字符
如果您正在创建一个表,请使用以下语法来索引某些字段的前255个字符:KEY sometextkey (SomeText(255))。像这样:
CREATE TABLE `MyTable` (
`id` int(11) NOT NULL auto_increment,
`SomeText` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `sometextkey` (`SomeText`(255))
);
如果你已经有了这个表,那么你可以用add unique (ConfigValue(20));为一个字段添加一个唯一键。像这样:
ALTER TABLE
MyTable
ADD UNIQUE(`ConfigValue`(20));
如果字段名不是保留的MySQL关键字,则在字段名周围不需要反引号(' ' ')。
创建全文搜索
全文搜索将允许您搜索文本字段的全部值。如果你使用自然语言模式,它会进行全词匹配,如果你使用其他模式之一,它会进行部分词匹配。查看更多关于全文本的选项:Dev.MySQL.com
创建您的文本表,并添加全文索引…
ALTER TABLE
MyTable
ADD FULLTEXT INDEX
`SomeTextKey` (`SomeTextField` DESC);
然后像这样搜索你的桌子……
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;