【问题标题】:Relatively simple SQL query with join refuses to be efficient相对简单的带join的SQL查询拒绝高效
【发布时间】:2015-12-10 09:49:00
【问题描述】:

我在优化 SQL 中的某个查询时遇到了一些问题(使用 MariaDB),为您提供一些上下文:我有一个带有“事件”(将它们视为日志条目)的系统,可以在票证上发生,也可以在除了门票之外的其他一些对象(这就是我将 event 和 ticket_event 表分开的原因)。我想按 display_time 排序所有ticket_events。事件表现在有大约 2000 万行。

CREATE TABLE IF NOT EXISTS `event` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(255) DEFAULT NULL,
  `data` text,
  `display_time` datetime DEFAULT NULL,
  `created_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_for_display_time_and_id` (`id`,`display_time`),
  KEY `index_for_display_time` (`display_time`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `ticket_event` (
  `id` int(11) NOT NULL,
  `ticket_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket_id` (`ticket_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `ticket_event`
  ADD CONSTRAINT `ticket_event_ibfk_1` FOREIGN KEY (`id`) REFERENCES `event` (`id`),
  ADD CONSTRAINT `ticket_event_ibfk_2` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`);

如您所见,我已经使用了一些键(我还为 (id, ticket_id) 制作了一个,因为我再次删除了它,所以现在这里没有显示)我执行的查询:

SELECT * FROM ticket_event
INNER JOIN event ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25

该查询需要相当长的时间来执行(如果我过滤特定的ticket_id,大约需要30s,如果不过滤它甚至无法可靠地完成它)。如果我对查询运行解释,它显示它执行文件排序 + 临时:
我玩了一下力指数等,但这似乎没有解决任何问题,或者我做错了。

有没有人看到我做错了什么或者我可以在这里优化什么?我非常不希望通过添加ticket_id/host_id 等作为列来使“事件”成为一个宽表,如果它们不适用则将它们设为NULL。

提前致谢!

编辑:EXPLAIN 的额外图像与表中的实际行:

【问题讨论】:

    标签: mysql sql query-optimization mariadb


    【解决方案1】:

    好的,如果你尝试强制索引怎么办?

    SELECT * FROM ticket_event
    INNER JOIN event 
    FORCE INDEX (index_for_display_time) 
    ON event.id = ticket_event.id
    ORDER BY display_time DESC
    LIMIT 25;
    

    【讨论】:

    • 这解决了它,显然我以错误的方式使用了力索引。 0.0025 秒的查询是完全可以接受的。谢谢!
    【解决方案2】:

    即使您使用 LIMIT,您的查询也会从每一行中选择每一列。您是否尝试过按 id 选择一个特定的行?

    【讨论】:

    • 我尝试同时拥有SELECT event.id 并添加了WHERE ticket_event.ticket_id = xxx,两者都给出相同的结果/速度慢(显然它更快,但仍然无法接受,它仍在进行文件排序)
    • 是的,从那些文档中我觉得 event.id + event.display_time 上的键可以使它工作。但它没有
    • 能否请您尝试执行优化命令:优化表事件;优化表ticket_event;
    • 目前临时文件夹中没有足够的空间,当我修复它时会再次添加评论(可能需要一些时间,因为我必须联系我的开发人员:P)。优化会不会让它不使用文件排序?
    【解决方案3】:
    KEY `index_for_display_time_and_id` (`id`,`display_time`),
    

    没用;算了吧。这是没用的,因为您使用的是 InnoDB,它将数据“集群”存储在 PK (id) 上。

    请将ticket_event.id 更改为event_idid 令人困惑,因为它感觉就像是映射表的 PK,它就是。可是等等!那没有意义?每个活动只有一张票?那么为什么ticket_event 存在呢?为什么不把ticket_id 放在event 中?

    对于多对多表,做

    CREATE TABLE IF NOT EXISTS `ticket_event` (
      `event_id`  int(11) NOT NULL,
      `ticket_id` int(11) NOT NULL,
      PRIMARY KEY (`event_id`, ticket_id),  -- for lookup one direction
      KEY         (`ticket_id`, event_id)   -- for the other direction
    ) ENGINE=InnoDB DEFAULT;
    

    【讨论】:

    • 因为 ticket_event 只是大约 10 个不同的事件表之一。如果我在事件表中添加 10 个多对多表或 10 列,它会变得更加混乱
    【解决方案4】:

    也许你会通过尝试这个来获得更好的性能:

    SELECT * 
    FROM ticket_event 
    INNER JOIN (select * from event ORDER BY display_time DESC limit 25) as b
    ON b.id = ticket_event.id;
    

    【讨论】:

    • 这样的问题是,如果最近的 25 个事件不是全部​​票务事件,它不会显示 25 个事件(因为子查询已经限制为 25 个结果)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-08
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多