将 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
列为主键,索引设置为PRIMARY
和date
。
一些观察和调试步骤:
如果我删除 set_charset
,则需要 1.5 秒。
如果我保留 utf8mb4
字符集但只选择 ip
和 date
,则查询将在 ~0.002 秒内完成。
我在网上搜索了解决方案,例如this solution 没有任何区别。
EXPLAIN
表示possible_keys
是null
,这很奇怪,因为似乎应该使用date
作为索引。即使FORCE INDEX
也不会使用索引。
在我将排序规则更改为 utf8mb4_unicode_520_ci
之前它工作正常——之前是 utf8_unicode。
我错过了什么?我怎样才能加快这个查询到合理的程度?非常感谢你! :)
【问题讨论】:
你的表结构没有索引。 并且您不应将 IP 地址存储为 VARCHAR(15)。谷歌INET_ATON
和INET_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% 左右。
为什么date
是INT
?请参阅数据类型 DATETIME
、DATE
、TIMESTAMP
。
请提供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