将 charset 设置为 utf8mb4 会使查询非常慢

Posted

技术标签:

【中文标题】将 charset 设置为 utf8mb4 会使查询非常慢【英文标题】:Setting charset to utf8mb4 makes query very slow 【发布时间】:2020-05-24 02:18:00 【问题描述】:

我已经对此进行了数小时的调试,如果我有任何明显的遗漏,我将不胜感激。我有一个 30k 行的 mysql 表。架构如下:

CREATE TABLE `log` (
  `ip` varchar(15) COLLATE utf8mb4_unicode_520_ci NOT NULL,
  `date` int(11) NOT NULL,
  `requested` mediumtext COLLATE utf8mb4_unicode_520_ci NOT NULL,
  `response` mediumtext COLLATE utf8mb4_unicode_520_ci NOT NULL,
  `id` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;

索引是:

ALTER TABLE `logs`
  ADD PRIMARY KEY (`id`) USING BTREE,
  ADD KEY `date` (`date`);

phpmyadmin 中,当我查询 SELECT ip, date, requested FROM log ORDER BY date DESC LIMIT 0, 1000 时,查询需要几毫秒。但是,在我的 PHP 代码中,当我执行相同的查询时:

$mysqli = new mysqli(...);

$s = microtime(true);
$mysqli->set_charset('utf8mb4');
$query = $mysqli->prepare('SELECT ip, date, requested FROM log ORDER BY date DESC LIMIT 0, 1000');
$query->execute();
$query->bind_result($ip, $date, $requested);
while($query->fetch()) 
  // ... just echoing the results


$e = microtime(true);
echo $e - $s;

需要 6-7 秒。 id列为主键,索引设置为PRIMARYdate

一些观察和调试步骤:

如果我删除 set_charset,则需要 1.5 秒。

如果我保留 utf8mb4 字符集但只选择 ipdate,则查询将在 ~0.002 秒内完成。

我在网上搜索了解决方案,例如this solution 没有任何区别。

EXPLAIN 表示possible_keysnull,这很奇怪,因为似乎应该使用date 作为索引。即使FORCE INDEX 也不会使用索引。

在我将排序规则更改为 utf8mb4_unicode_520_ci 之前它工作正常——之前是 utf8_unicode。

我错过了什么?我怎样才能加快这个查询到合理的程度?非常感谢你! :)

【问题讨论】:

你的表结构没有索引。 并且您不应将 IP 地址存储为 VARCHAR(15)。谷歌INET_ATONINET_NTOA 这仍然是 myisam 有充分的理由吗? ... 和 date 不应该是 INT(11)。如果您想存储 x32 时间戳 - 至少将其设为 UNSIGNED。 另外,你应该避免没有索引的分页。我说的是“偏移”部分。 【参考方案1】:
SELECT ip, date, requested FROM log
    ORDER BY date DESC LIMIT 0, 1000

需要INDEX(date)

如果您想从ip 中获取最新的 20 个,

SELECT ip, date, requested FROM log
    WHERE ip = '11.22.33.44'
    ORDER BY date DESC LIMIT 0, 20

需要INDEX(IP, date)

如果ip 是一个IP 地址,那么ascii 就足够了。这只会比utf8mb4 稍微快一点。字符集不是减速的原因;缺少索引是。

放缓的另一个原因是铲掉 1000 行大(?)行。

一个可能减速和时间变化很大的原因是innodb_buffer_pool_size的值如果buffer_pool太小而无法容纳数据;它将撞击磁盘。你有多少内存? buffer_pool_size 应设置为 可用 RAM 的 70% 左右。

为什么dateINT?请参阅数据类型 DATETIMEDATETIMESTAMP

请提供EXPLAIN;你说的有些事情彼此不一致。并通过EXPLAIN FORMAT=JSON SELECT ... 获取更多信息。 (如果该格式不可用,那么是时候升级您的 MySQL 版本了。)

你是否忽略了 IPv6 的存在?

【讨论】:

以上是关于将 charset 设置为 utf8mb4 会使查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章

使用 PHPMyAdmin 将 MySQL-Charset 从 utf8 更改为 utf8mb4

MySQL数据库 - 将characterset和collat ion转换为utf8mb4和utf8mb4_unicode_ci?

Django (2019, “Can’t initialize character set utf8mb4 (path: /usr/share/mysql/charsets/)”) 错误解决方案(示例

MySQL Workbench 不考虑配置的字符集 utf8mb4

将 right 属性设置为 -100% 会使 body 水平滚动

如果将键盘设置为特定语言并将继续作为返回键,则 UITextField 会使应用程序崩溃