【问题标题】:MySQL query randomly takes "forever"MySQL 查询随机取“永远”
【发布时间】:2009-06-10 16:28:02
【问题描述】:

我们有一个 XMPP 应用程序,它使用 MySQL 来存储信息。到目前为止,我们没有遇到任何特定的负载问题,但我正在努力为最坏的情况(或最好的,就用户而言;))做好准备。

安装此 MySQL 服务器的主机是具有 2GB RAM 的 Slicehost 切片。

昨天,我激活了慢查询日志记录,以确保我们实际上没有什么慢的。不幸的是,似乎实际上发现了很多慢查询:

从 /var/log/mysql/mysql-slow.log 读取 mysql 慢查询日志 计数:109 Time=25.57s (2787s) Lock=0.00s (0s) Rows=1.0 (109), xxxxx[xxxxx]@[172.21.xxx.xxx] SELECT * FROM `feeds` WHERE (`id` = 'S') LIMIT N

这对我来说真的很奇怪,因为 id 实际上是一个主键...... 该表是 InnoDB

我做了解释:

mysql> EXPLAIN SELECT * FROM `feeds` WHERE (`id` = '2650') LIMIT 1; +----+-------------+--------+-------+-------------- -+---------+---------+--------+------+--------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+-------+-------------- -+---------+---------+--------+------+--------+ | 1 |简单 |饲料 |常量 |初级 |初级 | 4 |常量 | 1 | | +----+-------------+--------+-------+-------------- -+---------+---------+--------+------+--------+ 一组中的 1 行(0.00 秒)

发生这种情况一定有另一个原因。在我们的日志中实际上有很多类似的慢查询(使用主键的查询)。

我认为在这里发布 MySQL 设置是有意义的:

mysql> 显示变量; +---------------------------------+---------------- --------------+ |变量名 |价值 | +---------------------------------+---------------- --------------+ | auto_increment_increment | 1 | | auto_increment_offset | 1 | | automatic_sp_privileges |开 | |后台日志 | 50 | |基于| /usr/ | | binlog_cache_size | 32768 | | bulk_insert_buffer_size | 8388608 | | character_set_client |拉丁语1 | |字符集连接 |拉丁语1 | |字符集数据库 |拉丁语1 | |字符集文件系统 |二进制 | |字符集结果 |拉丁语1 | | character_set_server |拉丁语1 | |字符集系统 | utf8 | |字符集目录 | /usr/share/mysql/charsets/ | | collat​​ion_connection | latin1_swedish_ci | | collat​​ion_database | latin1_swedish_ci | |排序服务器 | latin1_swedish_ci | |完成类型 | 0 | |并发插入 | 1 | |连接超时 | 10 | |数据目录 | /var/lib/mysql/ | |日期格式 | %Y-%m-%d | |日期时间格式 | %Y-%m-%d %H:%i:%s | | default_week_format | 0 | | delay_key_write |开 | |延迟插入限制 | 100 | |延迟插入超时 | 300 | |延迟队列大小 | 1000 | | div_precision_increment | 4 | | keep_files_on_create |关闭 | | engine_condition_pushdown |关闭 | | expire_logs_days | 10 | |冲洗 |关闭 | |冲洗时间 | 0 | | ft_boolean_syntax | + ->

我们的大多数请求都是“基本的”,但是,我们需要极快的速度!

对于究竟是什么让 MySQL 如此缓慢有什么想法吗?

[SUMMARY]总结各种答案:

  • 删除“LIMIT”,更改 WHERE id = “X” 进入 WHERE id = X
  • 确保我没有任何 运行的脚本(备份或其他) 有时会消耗很多 资源
  • 确保“主机”实际上不是罪魁祸首。

【问题讨论】:

  • 表中有多少条记录?我在不是特别大的表上发生过这种情况,并通过添加索引来修复它..
  • 确实,这个表现在相当小:20k 行。
  • 由于是主键,主键不是自动索引的吗?
  • 不确定我是否理解...(嘿,你破坏了布局;))。我会假设主键是索引
  • 抱歉,现在修复了布局,不知道为什么会这样...我试图将代码块(缩进 4 个空格)更改为预块,这样它就不会尝试像代码一样突出显示它。我的问题是提姆,他说他通过添加索引解决了类似的问题,这在这里似乎不适用,因为主键列应该被索引(作为主键)

标签: mysql performance optimization innodb


【解决方案1】:

查看慢速的均值和方差,您的 VM 主机存在问题(不幸的是,这不在您的控制范围内)。

对于那些指出内存/磁盘 I/O 的人来说,这些数字太大了。磁盘应该在 100 毫秒内返回,而不是几秒钟。

【讨论】:

  • 我倾向于认为“Slicehost Slice”会产生间歇性的 25 秒延迟,而不是责怪 MySQL。
【解决方案2】:

您的查询非常简单,并且鉴于 id 是主键,在正常情况下,即使在一个巨大的表上也不可能需要那么长时间。这里只是一个猜测,但也许服务器是问题所在?据我了解(从查看他们主页的 30 秒开始),Slicehost 为您提供了更强大服务器的虚拟机“切片”。会不会是同一台服务器上的其他片不时地进行大量磁盘读取,暂时窃取您的所有资源?或者当管理员从机器上为其他用户创建/删除切片时,可能会发生这种情况。

这种情况经常发生吗?

【讨论】:

  • 从技术上讲,资源在 CPU 方面是“有保证的”,虽然我不知道磁盘访问,但我希望得到同样的保证。它在大约 12 小时内发生了 109 次。所以很多次......但比例很小,因为这种查询在 12 小时内发生了大约 100 万次。
【解决方案3】:

如果 id 是主键,为什么要添加 LIMIT 子句?

您是否尝试过指定所需的列名而不是使用 *?

另外,您的 Id 列是 int 吗?通过指定 '1' 而不是 1,您可能没有使用索引。

试试

SELECT * FROM Feeds WHERE id = 1

而不是

SELECT * FROM Feeds WHERE id = '1'

编辑评论

在我看来,最好明确指定列名,因为将来您可能需要向该表中添加应用程序不需要的列。此时,您开始提取比需要更多的数据。

【讨论】:

  • 嗯...你是对的,LIMIT 在这里没用...但是我们需要所有字段。你认为指定它们比 * 更好吗?
  • LIMIT 在这种情况下是没有用的,但这不应该导致检索 1 行 10 列数据需要 25 秒,对吧?
  • @Julien,我认为您实际上并没有使用索引
  • 好的,我将指定我们未来性能所需的列。
  • 我已经尝试过使用和不使用引号......问题是鉴于我无法轻松重现问题,我在两者之间没有任何区别......可以这真的是个问题吗?
【解决方案4】:

我以前见过这个问题。

您的索引位于 integer 字段上,而 where 子句键是 string。您的索引被您导致类型转换的事实所击败。在 where 子句中取消引用您的密钥。

我对 mysql 的这种行为感到非常惊讶,它无法检测到这种情况何时发生,这非常令人失望。

【讨论】:

  • 但是,为什么它只发生在一小部分查询中呢?
【解决方案5】:

不幸的是,去年我在这类事情上积累了很多经验。

我同意其他人的观点,这可能是 CPU/磁盘延迟问题(由于虚拟主机)。有什么方法可以从主机获取磁盘延迟数?也许有尖峰。

我也同意该查询在指定限制子句和引用索引时有点奇怪。 SELECT * 位我完全可以理解。

我猜 InnoDB 没有足够的内存,但是行数太少并且给 InnoDB 1 gig,不是这样。

我猜查询是错误的。我以前见过 MySQL 做这种事情。某些查询耗时过长或导致其他查询开始堆积。但您看到的查询耗时过长,这些查询是简单的小事,绝不应该花很长时间。

我有几个建议给你:

  • 是否有某种正在运行的自动备份可能会锁定表?
  • 这是否会在任何类型的常规或可预测的时间间隔内发生?
  • 发生这种情况时,您是否曾经登录并查看过完整的进程列表?
  • 它是否与任何特定的事情相吻合(任何时候人们运行某个报告等)?
  • 您是否有任何非常大的表在处理查询时会占用您的所有内存,从而阻止该表进入(不太可能)?
  • 一直都是这样吗?是最近开始的吗? MySQL版本有变化吗?您是否可以尝试其他版本的 MySQL(更新点版本、Percona Performance 版本等)?

有时在此过程中查看完整的流程列表可能是最有帮助的。

去年我们遇到这种事情的时候,就是看进程列表,终于抓住了真正的问题。

【讨论】:

  • PS:我希望你能最终结束你的回答? :)
  • 感谢您提出的许多问题。我不知道“磁盘延迟数”。我怎样才能得到它们? - 有一个备份,但在“磁盘”级别:快照。 - 间隔:我不知道。有什么好的方法可以查出来吗? - 我永远无法“看到”尖峰。 - 我们没有任何非常大的表(最多 500k 行) - 我们实际上刚刚进入 prod ......所以,从技术上讲,它才刚刚开始,但它一如既往地发生了!还是谢谢!
  • 我会问你的主人,他们应该能够告诉你。如果你解释问题并告诉他们发生了什么,他们应该知道要寻找什么。也就是说,我希望它是其他查询。你必须有可怕的磁盘延迟才能成为问题。
  • 嗯...好的,那我需要查找什么查询!有什么线索吗?
【解决方案6】:

如果表比内存缓存中可以保存的更大,那么可能是其中一些查询需要在某些不幸的时刻触及磁盘,而其他东西给它们带来了很大的负载?

MySQL 调优有时有点像魔法。高 key-buffer 缓存争用也会导致明显的减速。

您也可以尝试在mysql performance blog 中搜索线索和似是而非的理论。

【讨论】:

  • “高 key-buffer 缓存争用也会导致明显的减速。” ... 真的?我认为缓存的目的实际上是为了防止减速?我将发布 MySQL 变量,以便您告诉我您的想法。
  • @Julien,当一个查询运行并且一些数据被缓存时会发生缓存争用,刷新所有以前的数据,这个会变快而其他的会变慢。如果您重复运行相同类型的查询,平均起来是一件好事,但如果您交替使用两种查询,您将失去缓存可以提供的好处,并强制每次读取磁盘。它们存在缓存争用,因为它们正在争夺缓存。
  • 我真的可以增加缓存来防止这种情况吗?
  • key-buffer 缓存中有互斥锁。我听说过禁用键缓冲区缓存(以及它的互斥体)允许更多并发从而提高性能的情况。但是,测量两次切割一次,否则性能会下降。
【解决方案7】:

这可能是由于名称解析延迟造成的,具体取决于您的设置。您可以使用以下方法测试从服务器查找 DNS 的速度:

nslookup www.domain.com

如果您收到缓慢的响应,请尝试在您的 /etc/my.cnf 文件中设置以下内容:

skip-name-resolve
# and/or:
skip-networking

另外,最好绑定 IP 地址和端口号,以消除对连接路径的疑虑:

bind-address=127.0.0.1
port=3306

否则我会查看表锁定并从那里进行故障排除。

【讨论】:

    【解决方案8】:

    如果您使用的是 MyISAM,您可能会遇到并发问题。

    【讨论】:

      猜你喜欢
      • 2021-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-11
      • 2014-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多