MySQL varchar 索引长度

2023-11-21

我有一个这样的表:

CREATE TABLE `products` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(512) NOT NULL,
  `description` text,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8;

和这样的一个:

CREATE TABLE `product_variants` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `product_id` int(11) unsigned NOT NULL,
  `product_code` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `product_code` (`product_code`),
  KEY `product_variant_product_fk` (`product_id`),
  CONSTRAINT `product_variant_product_fk` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1037 DEFAULT CHARSET=utf8;

和这样的 SQL 语句

SELECT p.id AS id, p.name AS name, p.description AS description, pv.id AS product_variant_id, pv.product_code AS product_code
FROM products p
INNER JOIN product_variants pv ON pv.product_id = p.id
ORDER BY p.name ASC
LIMIT 300 OFFSET 0;

如果我解释的话会给我这个:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| id | select_type | table | type | possible_keys              | key                        | key_len | ref     | rows   | Extra          |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
|  1 | SIMPLE      | p     | ALL  | PRIMARY                    | NULL                       | NULL    | NULL    | 993658 | Using filesort |
|  1 | SIMPLE      | pv    | ref  | product_variant_product_fk | product_variant_product_fk | 4       | db.p.id |      1 |                |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
2 rows in set (0.00 sec)

对于一百万行来说,这相当慢。我尝试过添加索引 产品名称:

ALTER TABLE products ADD INDEX `product_name_idx` (name(512));

这给出了:

mysql> show indexes from products;
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name         | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| products |          0 | PRIMARY          |            1 | id              | A         |      993658 |     NULL | NULL   |      | BTREE      |         |               |
| products |          1 | product_manf_fk  |            1 | manufacturer_id | A         |          18 |     NULL | NULL   | YES  | BTREE      |         |               |
| products |          1 | product_name_idx |            1 | name            | A         |         201 |      255 | NULL   |      | BTREE      |         |               |
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

我认为 Sub_part 列显示了已经存在的前缀 索引(以字节为单位),如所述这一页.

当我重新解释查询时,我得到:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
| id | select_type | table | type | possible_keys              | key                        | key_len | ref     | rows   | Extra          |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
|  1 | SIMPLE      | p     | ALL  | PRIMARY                    | NULL                       | NULL    | NULL    | 993658 | Using filesort |
|  1 | SIMPLE      | pv    | ref  | product_variant_product_fk | product_variant_product_fk | 4       | db.p.id |      1 |                |
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+
2 rows in set (0.00 sec)

看起来新索引没有被使用。如上所述这一页,如果索引是,则不会用于排序 前缀索引。事实上,如果我用以下方法截断数据:

alter table products modify `name`  varchar(255) not null;

解释给出:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+
| id | select_type | table | type  | possible_keys              | key                        | key_len | ref                                          | rows | Extra |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+
|  1 | SIMPLE      | p     | index | PRIMARY                    | product_name_idx           | 767     | NULL                                         |  300 |       |
|  1 | SIMPLE      | pv    | ref   | product_variant_product_fk | product_variant_product_fk | 4       | oh_2c98c233_69fe_4f06_ad0d_fe6f85a5beac.p.id |    1 |       |
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+

我认为这支持了这一点。然而,它说这一页那 InnoDB 表最多可以有 767 字节的索引。如果长度在 字节,为什么它拒绝超过255?如果它在 字符,它是如何决定每个UTF-8字符的长度的?是 它只是假设3?

另外,我正在使用这个版本的 MySQL:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.27-log |
+------------+
1 row in set (0.00 sec)

2021 年 9 月编辑: 我已经使用 MySQL 8.0 几年了,所以这里有一些更新的信息。

MySQL手册现在有信息丰富的页面关于之间的转换utf8mb3(目前也称为utf8) and utf8mb4. utf8mb3 已弃用并将被删除最终;当它被删除时,它当前的别名,utf8, 将参考utf8mb4反而。

已弃用utf8mb3,您可以在索引中存储最多 255 个字符,同时使用utf8mb4,使用时最多 191COMPACT or REDUNDANT行格式。

With COMPRESSED or DYNAMICrow 格式,索引键前缀最多可达 3072 字节。使用它们,您可以索引最多 1024 个字符utf8mb3,以及 768 个字符utf8mb4.

以下是我之前的回答,它解释了数字背后的一些逻辑人物你可以索引与数量bytes.


由于我的研究,我必须修改我的答案。我最初发布了这个(引用我自己):

我相信答案是你无法知道有多少个字符 位于索引中,因为您无法知道字符有多少字节 将会(除非您执行某些操作来排除多字节字符)。

我不确定,但它可能仍然是正确的,但不是我想的那样。

这是正确的答案:

MySQL 假定每个 utf8 字符占 3 个字节。 255 个字符是您可以为每列指定的最大索引大小,因为 256x3=768,这打破了 767 字节的限制。

如果您不指定索引大小,MySQL 将选择最大大小(即每列 255)。 不能对长度大于 255 的 utf8 列设置 UNIQUE 约束,因为唯一索引必须包含整个单元格值。但可以使用常规索引 - 它只会索引前 255 个字符(或前 767 个字节?)。这对我来说仍然是个谜。

谜: 我明白为什么 MySQL 出于安全考虑假设每个字符 3 个字节,因为否则 UNIQUE 约束可能会被破坏。但文档似乎表明索引的大小实际上以字节为单位,而不是字符。所以,假设你输入了 255varchar(25 上的 char (765 字节) 索引6) 柱子。如果您存储的字符都是 ASCII、1 字节字符,例如 A-Z、a-z、0-9,那么您可以将整个列放入 767 字节索引中。看起来这就是实际发生的事情。

下面是我原来的答案中有关字符、字节等的更多信息。


根据维基百科, UTF-8 字符的长度可以是 1、2、3 或 4 个字节。 但是,根据这个 mysql 文档,最大字符大小为 3 个字节,因此任何超过 255 个字符的列索引都可能达到该字节限制。但据我了解,可能不是。如果大部分字符都在 ASCII 范围内,那么平均字符大小将接近 1 个字节。例如,如果您的平均字符大小为 1.3 字节(大部分为 1 字节,但也有大量 2-3 字节字符),则您可以指定索引为 767/1.3

因此,如果您主要存储 1 字节字符,则实际字符限制将更像是: 767 / 1.3 = 590。但事实证明,事实并非如此。 255 个字符是限制。

正如中提到的这个 MySQL 文档,

前缀限制以字节为单位,而前缀长度以字节为单位 CREATE INDEX 语句被解释为字符数 非二进制数据类型(CHAR、VARCHAR、TEXT)。考虑到这一点 为使用多字节的列指定前缀长度时 字符集。

MySQL 似乎建议人们像我刚才那样进行计算/猜测,以确定 varchar 列的键大小。但事实上你cannot为 utf8 列指定大于 255 的索引。

最后,如果你再次参考我的第二个链接,还有这个:

当启用 innodb_large_prefix 配置选项时,这 对于使用的 InnoDB 表,长度限制提高到 3072 字节 动态和压缩行格式。

因此,如果您愿意的话,通过一些调整,您似乎可以获得更大的索引。只需确保行格式是动态的或压缩的。在这种情况下,您可以指定 1023 或 1024 个字符的索引。


By the way, it turns out that you can store 4-byte characters using [the utf8mb4 character set][4]. The utf8 character set apparently stores only ["plane 0" characters][5].

EDIT:

我只是尝试在 varchar(511) 列上使用 tinyint(1) 列创建复合索引,并收到错误消息,指出最大索引大小为 767 字节。这让我相信 MySQL 假设 utf8 字符集列将包含每个字符 3 个字节(最大值),并允许您最多使用 255 个字符。但也许这仅适用于复合索引。当我了解更多信息时,我会更新我的答案。但现在我将其保留为编辑。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL varchar 索引长度 的相关文章

  • SELECT COUNT() 与 mysql_num_rows();

    我有一个大表 60 数百万条记录 我正在使用 PHP 脚本来浏览该表 PHP 脚本 带分页 加载速度非常快 因为 表引擎是InnoDB因此SELECT COUNT 非常慢并且mysql num rows 不是一个选项 所以我将总行数 我用来
  • 检测 MySQL 中的 utf8 损坏字符

    我有一个数据库 其中有一堆损坏的 utf8 字符分散在多个表中 字符列表不是很广泛 AFAIK 修复给定的表非常简单 update orderItem set itemName replace itemName 但我无法找到检测损坏字符的方
  • mysql 中的 max(长度(字段))

    如果我说 select max length Name from my table 我得到的结果是 18 但我也想要相关数据 所以如果我说 select max length Name Name from my table 这是行不通的 我
  • 学说 - 获取下一个和上一个记录

    这样我就已经获取了一些记录 我已创建日期字段 现在我想按日期获取下一条和上一条记录 通过以下方式让它工作 qb this gt createQueryBuilder a next qb gt expr gt gt a created dat
  • Mysql显示创建约束?

    有没有一种简单的方法来查询表的约束 特别是外键 就像显示创建表一样 但仅用于约束 thanks 普夫戈丁 要仅显示外键约束 您可以检查 information schema table constraints 中的constraint ty
  • 存储 MySQL GUID/UUID

    这是我能想到的将 UUID 生成的 MySQL GUID UUID 转换为二进制文件 16 的最佳方法 UNHEX REPLACE UUID 然后将其存储在 BINARY 16 中 我应该知道这样做有什么影响吗 从 MySQL 8 0 及以
  • 复制具有不同列名的 MySQL 表

    我需要将 table1 中与特定列匹配的所有行复制到具有不同列名称的 table2 中 例如 table1 name oldAddressBook table1 的列 name Name Surname Number table2 name
  • 如何在每次运行 python 程序时添加新列

    我希望我的表的第一列作为卷号 第二列作为名称 每当我运行 python 程序时 我想在表中添加一列日期 在这个新列中 我想填充从 user list 获得的列表将包含值 P A P P 等 如何处理 我尝试首先通过 alter 命令添加一列
  • Codeigniter,为MySQL创建表和用户

    我想以编程方式使用 CI 创建数据库和用户 到目前为止 我有这 2 个简单的 MySQL 语句 CREATE DATABASE testdb DEFAULT CHARACTER SET utf8 COLLATE utf8 general c
  • Join 表(关联表)有主键吗?多对多关系

    Join 表 关联表 有主键吗 多对多的关系 我见过一些带有主键的连接表 一些没有 有人可以解释一下连接表中何时会有主键吗 为什么 先感谢您 在纯 联接 或联结表中 所有字段都将成为主键的一部分 例如 让我们考虑下表 CREATE TABL
  • 计算 MySQL 中的行数以及实际行内容

    MySQL 中有没有办法执行单个 SQL 语句来返回所选行以及结果行数 我可以做这个 SELECT COUNT FROM BigTable WHERE firstname LIKE a 这给了我一个带有计数 37 781 的结果行 我可以像
  • 恐慌:拨打 tcp 127.0.0.1:3306:连接:连接被拒绝

    我正在尝试使用 golang gin gorm 从 docker 连接 mysql 服务器 构建本身已经成功 但是如标题所示 出现了以下错误 panic dial tcp 127 0 0 1 3306 connect connection
  • 未知的 MySQL 服务器主机 (PHP)

    当尝试连接到我的数据库服务器时 我遇到了以下问题未知主机 Warning mysqli mysqli mysqli mysqli HY000 2005 Unknown MySQL server host xxxxxxxxxxxxx port
  • 通过 Join-Where-Group 通过选择查询正确建立索引,避免使用临时;使用文件排序

    我已经搜索了很多关于下面描述的案例的解决方案 但不幸的是我没有找到类似的案例 我有以下场景 作为新用户 该网站拒绝了我的图片 但我可以通过邮件发送它 下面是它的文本表示 Table 1 swap plan Table 2 cell Clus
  • 将 3d NumPy 数组重塑为 2d NumPy 数组时遇到问题

    我正在研究图像处理问题 我的数据以 3 维 NumPy 数组的形式呈现 其中 x y z 条目是图像 z 的 x y 像素 数值强度值 有 100000 张图像 每张图像为 25x25 因此 数据矩阵的大小为 25x25x10000 我试图
  • 数据库字段中的空白不会被trim()删除

    我在 MySQL 的文本字段的段落开头有一些空格 Using trim var text field 在 PHP 中或TRIM text field MySQL 中的语句绝对不执行任何操作 这个空白可能是什么以及如何通过代码删除它 如果我进
  • 选择 MYSQL 行,但将行转换为列,将列转换为行

    我想选择数据库中的所有行 但我希望它们按相反的顺序排列 意思是 我想使用第一列数据作为新实体 并将实体作为第一列 我想你明白我的意思 这是一个例子 id name marks 1 Ram 45 2 Shyam 87 to id 1 2 Na
  • 如何使用 php 命令使注册表单高度安全?

    我想让代码对用户来说真正安全 下面的代码显示了 php 代码 我已将其用于我的网站 现在我已经使用了一些验证 例如密码和重复密码必须匹配 并且用户必须输入所有字段 为了使其更安全 我想插入安全命令 例如 PDO mysqli crypto
  • (mysql, php) 如何在插入数据之前获取auto_increment字段值?

    我正在将图像文件上传到存储服务器 在上传之前 我应该编写文件名 其中包含自动增量值 例如 12345 filename jpg 在插入数据库之前如何获取自动增量值 我只看到一种解决方案 插入空行 获取其自增值 删除这一行 使用 p 1 中的
  • Mongodb:$HINT 的性能影响

    我有一个使用复合索引并在 id 上排序的查询 复合索引在索引末尾有 id 它工作正常 直到我添加 gt我的查询的子句 IE 初始查询 db colletion find field1 blabla field2 blabla sort id

随机推荐