什么可能导致 InnoDB 表的单个 UPDATE 性能非常缓慢?
Posted
技术标签:
【中文标题】什么可能导致 InnoDB 表的单个 UPDATE 性能非常缓慢?【英文标题】:What could cause very slow performance of single UPDATEs of a InnoDB table? 【发布时间】:2014-07-18 10:47:30 【问题描述】:我的网络应用中有一个用于存储会话数据的表。它的性能很差,我不知道为什么。慢查询日志显示更新一行需要 6 到 60 秒。
CREATE TABLE `sessions` (
`id` char(40) COLLATE utf8_unicode_ci NOT NULL,
`payload` text COLLATE utf8_unicode_ci NOT NULL,
`last_activity` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `session_id_unique` (`id`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
PK 是一个char(40
),它存储了由该项目使用的框架 (Laravel) 生成的唯一会话哈希。
(我知道 PK 和唯一索引的冗余,但我已经尝试了所有组合,它对我的测试性能没有任何影响。这是它的当前状态。)
表格很小 - 不到 200 行。
慢查询日志中的典型查询如下所示:
INSERT INTO sessions (id, payload, last_activity)
VALUES ('d195825ddefbc606e9087546d1254e9be97147eb',
'YTo1OntzOjY6Il90b2tlbiI7czo0MDoi...around 700 chars...oiMCI7fX0=',
1405679480)
ON DUPLICATE KEY UPDATE
payload=VALUES(payload), last_activity=VALUES(last_activity);
我已经做了一些显而易见的事情,比如检查表格是否损坏。我尝试添加一个专用的 PK 列作为自动增量 int
,我尝试不使用 PK,不使用唯一索引,将 text
列交换为非常大的 varchar
,您可以命名它。
我已尝试将表切换为使用 MyISAM,但仍然很慢。
我所做的一切似乎都没有任何区别 - 表执行得非常慢。
我的下一个想法是查询。这是由框架生成的,但如果失败,我已经测试将其破解为UPDATE
和INSERT
。 UPDATE
语句继续缓慢。
我已经阅读了很多关于慢速 INSERT
和 UPDATE
语句的问题,但这些问题通常与批量事务有关。这只是每个用户每个请求的一次插入/更新。该站点不是很忙,而是在自己的 VPS 上,资源丰富。
什么可能导致缓慢?
【问题讨论】:
我不认为你需要VALUES()
在查询的最后一行包装列名
系统上是否还有其他运行可能会锁定表?
桌上有触发器吗?
服务器的 my.cnf 有什么奇怪的地方吗?
例如你没有常规登录?
【参考方案1】:
这不是答案,但 SE 评论长度太短了。所以。
如果您直接在命令行上运行相同的 INSERT ... ON DUPLICATE KEY UPDATE... 语句会发生什么?请尝试使用和不使用应用程序。应用程序可能会人为地减慢此 UPDATE(例如,在 INNODB 中,可能会打开一个事务,但在消耗大量时间后提交。您也使用不支持事务的 MyISAM 进行了测试。也许在这种情况下,一个显式 LOCK 可以考虑到同样的效果。如果框架使用这个技巧,我不确定,我不知道laravel)尝试进行基准测试,看看是否有并发效果。
另一个问题:这是单台服务器吗?或者它是一个复制到一个或多个从属的主控?
除了这个问题,还有几点意见:
id 的值是十六进制字符串。该列是 unicode。这意味着保留 3*40 字节,而仅使用 40 个字节。这是一种浪费,通常会使事情效率低下。使用 BINARY 或 ASCII 作为字符编码会好得多。更好的是,将 id 列更改为 BINARY 数据类型并存储(未十六进制)二进制值 innodb PK 表的散列将数据分散到页面中。使用 auto_incrment pk 或根本不显式声明 pk(这将导致 innodb 在内部创建自己的 autoincrement pk)的想法是个好主意。 看起来负载是 base64 编码的。再次将字符编码指定为 unicode。 Ascii 或 Binary(字符编码,而不是数据类型)更合适。 ID唯一索引中的HASH关键字没有意义。 InnoDB 不实现 HASH 索引。不幸的是 mysql 对此完全保持沉默(参见http://bugs.mysql.com/bug.php?id=73326)(虽然这个列表确实提供了改进的角度,但似乎不太可能用这个来解决极度缓慢的问题。肯定有其他事情发生)
【讨论】:
这些是我不知道的一些有趣的观察@roland,谢谢。 删除 PK 可能也是一个好主意,因为索引不利于插入速度,而且您可能不会加入太多会话? 我个人认为你不应该创建没有 PK 的表。这不是加入与否的问题,只是每个表都应该有一个PK的基本原则。不幸的是,由于 InnoDB 的实现细节,自动递增 pk 的性能优于 hash 或 guid,但事实就是如此。 这些都是好点,谢谢。不过,我认为这些都不应该导致 6、60 甚至 120 秒的查询时间,对吗? @drewm,不,正如您可能注意到的那样,我也指出了这一点 - 除了应用程序减慢速度。所以我的问题仍然存在:如果您绕过应用程序并通过命令行会发生什么?【参考方案2】:令人沮丧的是,答案是这种情况是磁盘坏了。存储阵列中的一个磁盘坏了,因此写入需要很长时间才能完成。就是这样。
【讨论】:
以上是关于什么可能导致 InnoDB 表的单个 UPDATE 性能非常缓慢?的主要内容,如果未能解决你的问题,请参考以下文章
innodb_file_per_table - 转换为InnoDB
MySQL InnoDB Engine--数据页存储和UPDATE操作