草庐IT

mysql - LIMIT 1 很慢,针对特定的记录,使用不同的key

coder 2023-06-10 原文

我正在诊断一个间歇性的慢速查询,并且在 MySQL 中发现了一个我无法解释的奇怪行为。只有在执行 LIMIT 1 时,它才会针对特定情况选择不同的非最佳 key 策略。

表格(为简洁起见删除了一些未引用的数据列)

CREATE TABLE `ch_log` (
    `cl_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `cl_unit_id` INT(11) NOT NULL DEFAULT '0',
    `cl_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
    `cl_type` CHAR(1) NOT NULL DEFAULT '',
    `cl_data` TEXT NOT NULL,
    `cl_event` VARCHAR(255) NULL DEFAULT NULL,
    `cl_timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `cl_record_status` CHAR(1) NOT NULL DEFAULT 'a',
    PRIMARY KEY (`cl_id`),
    INDEX `cl_type` (`cl_type`),
    INDEX `cl_date` (`cl_date`),
    INDEX `cl_event` (`cl_event`),
    INDEX `cl_unit_id` (`cl_unit_id`),
    INDEX `log_type_unit_id` (`cl_unit_id`, `cl_type`),
    INDEX `unique_user` (`cl_user_number`, `cl_unit_id`)
)
ENGINE=InnoDB
AUTO_INCREMENT=419582094;

这是查询,仅针对一个特定的 cl_unit_id 运行缓慢:

EXPLAIN
SELECT *
FROM `ch_log`
WHERE `ch_log_type` ='I' and ch_log_event = 'G'  
AND cl_unit_id=1234
ORDER BY cl_date DESC 
LIMIT 1;
id|select_type|table |type |possible_keys                               |key    |key_len|ref|rows|Extra
1 |SIMPLE     |ch_log|index|cl_type,cl_event,cl_unit_id,log_type_unit_id|cl_date|8      |\N |5295|Using where

对于 cl_unit_id 的所有其他值,它使用更快的 log_type_unit_id 键。

id|select_type|table |type|possible_keys                                           |key             |key_len|ref        |rows|Extra
1 |SIMPLE     |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5      |const,const|3804|Using where; Using filesort
  • 所有查询大约需要 0.01 秒。
  • “慢单位”查询需要 10-15 分钟!

我看不出这个“单位”的数据有什么奇怪的:

  • 1234 单元只有 6 条类型 I 和事件 G 的记录。
  • 其他单位有更多。
  • 1234 单元总共只有 32,000 条日志,这很正常。
  • 数据本身是正常的,没有变大变老。
  • 数据库中大约有 3,000 个“单元”,代表设备日志记录。 cl_unit_id 是他们唯一的 PK(尽管没有限制)。

一般信息

  • 总共有3000万条记录,约12GB
  • mysql 5.1.69-log
  • Centos 64 位
  • 数据在逐渐变化(3000 万 = 3 个月的日志)但我不知道以前是否发生过这种情况

我已经尝试过并可以“解决”问题的方法:

  1. 删除 LIMIT 1 - 查询以毫秒为单位运行并返回数据。

  2. 更改为 LIMIT 2 或其他组合,例如2,3 - 以毫秒为单位运行。

  3. 添加索引提示 - 解决问题:

    FROM `ch_log` USE INDEX (log_type_unit_id)
    

    但是...我不想将其硬编码到应用程序中。

  4. 在主键上添加第二个 order by 也“解决”了它:

    ORDER BY cl_id, cl_date DESC 
    

    给予解释:

    id|select_type|table |type|possible_keys                                           |key             |key_len|ref        |rows|Extra
    1 |SIMPLE     |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5      |const,const|6870|Using where
    

    这与提示的类型略有不同,检查了更多记录 (6,000),但仍然在 10 毫秒内运行。

同样,我可以做到这一点,但我不喜欢使用我不理解的副作用。

所以我认为我的主要问题是:

a) 为什么它只发生在 LIMIT 1 上?

b) 数据本身 怎么会对关键策略产生如此大的影响?以及数据的哪个方面,从指数的数量和分布来看似乎很典型。

最佳答案

Mysql 会选择一个解释计划并使用不同的索引,这取决于它认为在统计上是最佳选择。对于您所有的第一个问题,这就是答案:

  1. 删除 LIMIT 1 - 查询以毫秒为单位运行并返回数据。 和 -> 是的,检查一下,解释计划很好
  2. 更改为 LIMIT 2 或其他组合,例如2,3 - 以毫秒为单位运行。 -> 同样适用。优化器选择了不同的索引,因为突然之间,预期的 block 读取量变成了 LIMIT 1 的两倍(这只是一种可能性)
  3. 添加一个索引提示解决它 -> 当然,你强制一个好的解释计划
  4. 在主键上添加第二个 order by 也“解决”了它 -> 是的,因为巧合,结果是更好的解释计划

现在,这只回答了一半的问题。

a) why does it only happen for LIMIT 1?

它实际上不仅因为 LIMIT 1 而发生,还因为

  • 您的数据统计重新分区(指导优化器的决策)
  • 您的 ORDER BY DESC 子句。尝试使用 ORDER BY ... ASC,您可能也会看到改进。

这种现象是众所周知的。请read on .

公认的解决方案之一(文章底部)是强制索引,方法同您一样。是的,有时候,这是有道理的。否则的话,这个提示的东西早就被彻底抹杀了。机器人不可能总是完美的:-)

b) how can the data itself affect the key-strategy so much? And what aspect of the data, seeing as the quantity and spread in the indexes seems typical.

你说的对,差价通常会搞砸。不仅优化器可能会根据准确的统计信息做出错误的决定,而且它也可能因为表上的增量而完全关闭 is right below 1 / 16th of the total row count ...

关于mysql - LIMIT 1 很慢,针对特定的记录,使用不同的key,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31420780/

有关mysql - LIMIT 1 很慢,针对特定的记录,使用不同的key的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐