暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

基于 BLOB/TEXT 列上创建索引失败引起的思考

00后DBA实录 2025-01-10
58
在我的公众号的发消息界面点击 “加我滴群” 即可获取我的群的二维码,欢迎向我提问,我将基于官方文档给你最好的解释。

背景

使用工具将 Oracle 表结构转 mysql 表结构后喜提报错:

ERROR 1170 (42000): BLOB/TEXT column 'xml' used in key specification without a key length

关于导入的方式

由于当时使用的是,登录后再 source xx.sql
,所以报错了还不太好找,这不炸了吗:

[root@localhost testddl]# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.32 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

root@localhost [(none)] 23:26:14 > use ddl;
Database changed
root@localhost [ddl] 23:26:20 > source longtextidx.sql ;
ERROR 1170 (42000): BLOB/TEXT column 'xml' used in key specification without a key length

文件的行数都没显示,只有一个 xml
 的字段名,表比较多找半天,所以迁移数据一定要在命令行之外使用 -e
 或者 <
,要不然指不定就报错了你还不知道哪里报错了。

如:

[root@localhost testddl]# mysql -f ddl < longtextidx.sql 
ERROR 1170 (42000) at line 1: BLOB/TEXT column 'xml' used in key specification without a key length

这样就会显示行数。

为什么报错 1170

从 key specification without a key length
 这里大概能猜到可能是约束错误,大概率是索引(index
/key
)。

找到那个报错的表,我这里是测试数据就一个表:

[root@localhost testddl]# cat longtextidx.sql 
create table longtextidx(id int primary key,xml longtext,key idx_xml(xml));

直接拿到建表语句就能看见关于 xml 的约束 key idx_xml(xml)
 出现的问题。

为什么这里指示没有长度限制,我这里猜到是 mysql 对于 BLOB/TEXT 上索引做的限制,因为对这两种数据类型的存储大概率是长数据,这就会导致 2 个问题:

  1. 索引选择性
  2. 索引树大小

即使建了这个索引也不一定会走,反而增加优化器选择最佳索引的成本和对表 DML 更新二级索引的成本,所以有这个要求对 BLOB/TEXT 做前缀索引的要求。

文档的描述:

In most respects, you can regard a BLOB
 column as a VARBINARY
 column that can be as large as you like. Similarly, you can regard a TEXT
 column as a VARCHAR
 column. BLOB
 and TEXT
 differ from VARBINARY
 and VARCHAR
 in the following ways:

  • For indexes on BLOB
     and TEXT
     columns, you must specify an index prefix length. For CHAR
     and VARCHAR
    , a prefix length is optional. See Section 10.3.5, “Column Indexes”.
  • BLOB
     and TEXT
     columns cannot have DEFAULT
     values.

如何选择最佳前缀数

使用这个 SQL 去计算选择性:

select count(distinct substring(column,1,n))/count(*) from table;

例如刚才的这个表就可以这么做:

select count(distinct substring(xml,1,50))/count(*) from longtextidx;

截取前 50 个字符判断对于全量的区分度:

  • 当结果接近 1 时,表前 50 个字符能很好地区分数据,每个前缀值几乎唯一
  • 当结果远小于 1 时,表示前 50 个字符的重复值较多,无法很好地区分数据。

取的值越大存储空间就越大,取一个存储空间和选择性的最佳值即可,如 0.6~0.7

创建前缀索引

示例:

create index idx_xml on longtextidx(xml(50));

使用 REDUNDANT
 或 COMPACT
 行格式的 InnoDB 表,前缀最长为 767 字节。使用 DYNAMIC
 或 COMPRESSED
 行格式的 InnoDB 表,前缀长度限制为 3072 字节。

前缀限制是以字节为单位测量的,而在 CREATE TABLE
ALTER TABLE
 和 CREATE INDEX
 语句中,非二进制字符串类 ( CHAR
VARCHAR
TEXT
) 的前缀长度是按字符数解释的,二进制字符串类型(BINARY
VARBINARY
BLOB
)的前缀长度是按字节数解释的。在为使用多字节字符集的非二进制字符串列指定前缀长度时,请注意这一点。

参考

https://dev.mysql.com/doc/refman/8.0/en/blob.html


往期回顾


感谢阅读!关注我获取更多精彩内容。


文章转载自00后DBA实录,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论