草庐IT

mysql - 替代 COUNT for innodb 以防止表扫描?

coder 2023-06-11 原文

我设法整理了一个适合我需要的查询,尽管比我希望的要复杂。但是,对于表的大小,查询比它应该的慢(0.17s)。原因,基于EXPLAIN下面提供,是因为在meta_relationships上有表扫描表,因为它有 COUNTWHERE条款 innodb发动机。

查询:

SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
RIGHT JOIN meta_relationships ON (posts.post_id = meta_relationships.object_id)
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = computers AND meta_relationships.object_id 
NOT IN (SELECT meta_relationships.object_id FROM meta_relationships
        GROUP BY meta_relationships.object_id HAVING count(*) > 1)
GROUP BY meta_relationships.object_id

这个特定的查询,选择只有 computers 的帖子类别。 count > 1的目的是排除包含 computers/hardware 的帖子, computers/software等。选择的类别越多,计数就越高。

理想情况下,我想让它像这样运行:
WHERE meta.meta_name IN ('computers') AND meta_relationships.meta_order IN (0)


WHERE meta.meta_name IN ('computers','software') 
AND meta_relationships.meta_order IN (0,1)

等等..

但不幸的是这不起作用,因为它没有考虑到可能存在 meta_relationships.meta_order = 2。

我试过了...
WHERE meta.meta_name IN ('computers')
GROUP BY meta_relationships.meta_order
HAVING meta_relationships.meta_order IN (0) AND meta_relationships.meta_order NOT IN (1)

但它没有返回正确的行数。

说明:
id  select_type   table               type    possible_keys          key               key_len ref                                   rows   Extra   
1   PRIMARY       meta                ref     PRIMARY,idx_meta_name  idx_meta_name     602     const                                 1      Using where; Using index; Using temporary; Using filesort
1   PRIMARY       meta_data           ref     PRIMARY,idx_meta_id    idx_meta_id       8       database.meta.meta_id                 1  
1   PRIMARY       meta_relationships  ref     idx_meta_data_id       idx_meta_data_id  8       database.meta_data.meta_data_id       11     Using where
1   PRIMARY       posts               eq_ref  PRIMARY                PRIMARY           4       database.meta_relationships.object_id 1  
2   MATERIALIZED  meta_relationships  index   NULL                   idx_object_id     4       NULL                                  14679  Using index

表/索引:

此表包含类别和标签名称。
索引:
主键 ( meta_id ), 键 idx_meta_name ( meta_name )
元数据
此表包含有关类别和标签的附加数据,例如类型(类别或标签)、描述、父级、计数。
索引:
主键 ( meta_data_id ), 键 idx_meta_id ( meta_id )
元关系
这是一个结点/查找表。它包含posts_id 的外键、meta_data_id 的外键,还包含类别的顺序。
索引:
主键 ( relationship_id ), 键 idx_object_id ( object_id ), key idx_meta_data_id ( meta_data_id )
  • 计数允许我只选择具有正确类别级别的帖子。例如,类别computers 有仅包含computers 类别的帖子,但也有包含computer/hardware 的帖子。计数过滤掉包含这些额外类别的帖子。我希望这是有道理的。
  • 我相信优化查询的关键是完全摆脱 COUNT .
  • COUNT 的替代品可能会使用 meta_relationships.meta_ordermeta_data.parent相反。
  • meta_relationships表将快速增长,并且在当前大小(~15K 行)下,我希望在 100 秒而不是 10 秒内实现执行时间。
  • 由于WHERE中需要有多个条件每个类别/标签的子句,任何针对动态查询优化的答案都是首选。
  • 我用 sample data 创建了一个 IDE .

  • 如何优化此查询?

    编辑:

    我永远无法找到解决此问题的最佳解决方案。这确实是 smcjones 对改进索引的建议的组合,我建议对其进行 EXPLAIN看着 EXPLAIN Output Format然后将索引更改为任何可以提供最佳性能的索引。
    此外,hpf 建议添加另一列总计数也有很大帮助。最后,在更改索引后,我最终使用了这个查询。
    SELECT posts.post_id,posts.post_name,
    GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
    GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
    FROM posts
    JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
    JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
    JOIN meta ON meta_data.meta_id = meta.meta_id
    WHERE posts.meta_count = 2
    GROUP BY posts.post_id
    HAVING category = 'category,subcategory'
    

    摆脱后COUNT ,最大的性能杀手是 GROUP BYORDER BY ,但索引是你最好的 friend 。我在做 GROUP BY 时了解到, WHERE条款很重要,越具体越好。

    最佳答案

    结合优化查询优化你的表,你将有快速的查询。但是,如果没有优化的表,就无法进行快速查询。
    我再怎么强调都不为过:如果您的表的结构正确且索引数量正确,那么您不应该在像 GROUP BY... HAVING 这样的查询中遇到任何完整的表读取,除非您按照设计这样做。
    根据您的示例,我创建了 this SQLFiddle .
    将其与 SQLFiddle #2 进行比较,其中我添加了索引并添加了 UNIQUE指数对meta.meta_naame .
    根据我的测试,Fiddle #2 更快。
    优化您的查询
    这个查询让我发疯,即使在我提出索引是优化它的最佳方法之后也是如此。尽管我仍然认为该表是提高性能的最大机会,但似乎必须有更好的方法在 MySQL 中运行此查询。我在解决这个问题后得到了启示,并使用了以下查询(见 in SQLFiddle #3):

    SELECT posts.post_id,posts.post_name,posts.post_title,posts.post_description,posts.date,meta.meta_name
       FROM posts
       LEFT JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
       LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
       LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
       WHERE meta.meta_name = 'animals'
       GROUP BY meta_relationships.object_id
       HAVING sum(meta_relationships.object_id) = min(meta_relationships.object_id);
    
    HAVING sum() = min()GROUP BY应该检查每种类型是否有多个记录。显然,每次记录出现时,它都会增加总和。 (编辑:在后续测试中,这似乎与 count(meta_relationships.object_id) = 1 具有相同的影响。哦,重点是我相信您可以删除子查询并获得相同的结果)。
    我想明确一点,除非我提供给您的查询有任何优化,否则您不会注意到 WHERE meta.meta_name = 'animals' 部分。正在查询索引(最好是唯一索引,因为我怀疑您需要的不仅仅是这些索引,它会防止意外复制数据)。
    所以,而不是看起来像这样的表:
    CREATE TABLE meta_data (
      meta_data_id BIGINT,
      meta_id BIGINT,
      type VARCHAR(50),
      description VARCHAR(200),
      parent BIGINT,
      count BIGINT);
    
    您应该确保像这样添加主键和索引:
    CREATE TABLE meta_data (
      meta_data_id BIGINT,
      meta_id BIGINT,
      type VARCHAR(50),
      description VARCHAR(200),
      parent BIGINT,
      count BIGINT,
      PRIMARY KEY (meta_data_id,meta_id),
      INDEX ix_meta_id (meta_id)
    );
    
    不要做得太过分,但每个表都应该有一个主键,并且任何时候您对特定值进行聚合或查询时,都应该有索引。
    当不使用索引时,MySQL 将遍历表的每一行,直到找到您想要的内容。在像您这样的有限示例中,这不会花费太长时间(即使它仍然明显变慢),但是当您添加数千或更多记录时,这将变得非常痛苦。
    将来,在查看查询时,请尝试确定全表扫描发生的位置,并查看该列是否有索引。无论您在哪里聚合或使用 WHERE,都是一个很好的起点。句法。
    关于 count 的说明柱子
    我还没找到放count列到表中很有帮助。它可能会导致一些非常严重的完整性问题。如果一个表优化得当,应该很容易使用count()并获取当前计数。如果你想把它放在表格中,你可以使用 VIEW ,虽然这不是最有效的拉动方式。
    放置问题count表中的列是您需要更新该计数,使用 TRIGGER或者,更糟糕的是,应用程序逻辑。随着您的程序向外扩展,该逻辑可能会丢失或被掩埋。添加该列是对规范化的一种偏差,当发生这样的事情时,应该有一个 非常好理由。
    关于是否有充分的理由这样做存在一些争论,但我认为我明智地避开这场争论,因为双方都有很好的争论。相反,我会选择一个小得多的战斗,并说我认为在这个用例中这会让你头疼而不是好处,所以它可能值得 A/B 测试。

    关于mysql - 替代 COUNT for innodb 以防止表扫描?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29443769/

    有关mysql - 替代 COUNT for innodb 以防止表扫描?的更多相关文章

    1. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    2. ruby-on-rails - 更好的替代方法 try( :output). try( :data). try( :name)? - 2

      “输出”是一个序列化的OpenStruct。定义标题try(:output).try(:data).try(:title)结束什么会更好?:) 最佳答案 或者只是这样:deftitleoutput.data.titlerescuenilend 关于ruby-on-rails-更好的替代方法try(:output).try(:data).try(:name)?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

    3. 使用canal同步MySQL数据到ES - 2

      文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

    4. ruby - 允许主机名包含下划线的 URI.parse 的替代方法 - 2

      我正在使用DMOZ的listofurltopics,其中包含一些具有包含下划线的主机名的url。例如:608609TheOuterHeaven610InformationandimagegalleryofMcFarlane'sactionfiguresforTrigun,Akira,TenchiMuyoandotherJapaneseSci-Fianimations.611Top/Arts/Animation/Anime/Collectibles/Models_and_Figures/Action_Figures612虽然此url可以在网络浏览器中使用(或者至少在我的浏览器中可以使用:

    5. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

      我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

    6. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

      我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

    7. Ruby 扫描/获取直到 EOF - 2

      我想扫描未知数量的行,直到扫描完所有行。我如何在ruby中做到这一点?例如:putreturnsbetweenparagraphsforlinebreakadd2spacesatend_italic_or**bold**输入不是来自"file",而是通过STDIN。 最佳答案 在ruby​​中有很多方法可以做到这一点。大多数情况下,您希望一次处理一行,例如,您可以使用whileline=getsend或STDIN.each_linedo|line|end或者通过使用-n开关运行ruby​​,例如,这意味着上述循环之一(在每次迭代中将

    8. ruby-on-rails - 当我通过 rvm 使用 rails3 时,如何在 ubuntu 上安装 mysql2 gem? - 2

      我正在尝试绕过rails配置这个极其复杂的迷宫。到目前为止,我设法在ubuntu上设置了rvm(出于某种原因,ruby在ubuntu存储库中已经过时了)。我设法建立了一个Rails项目。我希望我的测试项目使用mysql而不是mysqlite。当我尝试“rakedb:migrate”时,出现错误:“!!!缺少mysql2gem。将其添加到您的Gemfile:gem'mysql2'”当我尝试“geminstallmysql”时,出现错误,告诉我需要为安装命令提供参数。但是,参数列表很大,我不知道该选择哪些。如何通过在ubuntu上运行的rvm和mysql获取rails3?谢谢。

    9. ruby-on-rails - jRails 替代品 - 2

      你知道jrails的替代品吗?它或多或少已经过时(使用jQuery1.5-现在1.7是当前版本)。有人知道替代方案吗?谢谢编辑:我知道如何使用jqueryallone构建rails助手-但我喜欢rails助手,所以我不想单独使用jquery(没有jrails) 最佳答案 我一直在Rails中使用Prototype助手,最近我决定转而使用JQuery。起初我查看了JRails,因为它是一个直接替代品,因此需要最少的工作。但是!在阅读了更多关于JQuery的信息并尝试使用它之后,我逐渐明白,结合使用Rails和JQuery的最佳方式就是

    10. ruby-on-rails - 能够处理 rar/tar/zip/7z 的 Ruby/rubyzip 替代方案? - 2

      关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我想知道是否有人知道Ruby的ruby​​zip替代品,它可以处理各种格式,特别是zip/rar/7z?我知道libarchive,但它对我的目的来说并不完整(它是一个很好的gem)。(澄清一下,libarchive-对我不起作用-因为

    随机推荐