【问题标题】:Need Some Guidance to Optimize Reporting in MySQL需要一些指导来优化 MySQL 中的报告
【发布时间】:2019-01-23 15:32:43
【问题描述】:

我的团队维护着一个每周处理数百万条记录的应用/数据库。这个过程相当简单:

  • 向各种活动的联系人发送通知
  • 发送通知时将contact_id、campaign_id、message_id、created_at、updated_at写入日志
  • 读取每个 notificationID/notification_messageID 的记录计数,并在报告中显示给用户。

日志的写入和读取过程需要非常长的时间,我们正在寻找优化它的方法。

write 语句在发送通知时发生。它在一个查询中批量插入 20 条记录。这是一个例子:

INSERT INTO `contact_notification_logs` (`id`, `contact_id`, `campaign_id`, 
`message_id`, `created_at`, `updated_at`, `is_reset`) 
VALUES 
(NULL, '1', '1', '1', '2019-01-23 20:16:21', '2019-01-23 20:16:24', 
'0'),

发生了两个读取语句:

  1. 这个非常简单,它在列出所有活动的页面上运行,并显示今天发送的当前通知计数:
SELECT COUNT(id) FROM contact_notification_logs 
WHERE DATE(created_at) = '[current date]'

这个虽然简单,但执行起来仍然需要很长时间。

  1. 第二个读取语句有点复杂,因为它内置在应用程序的报告工具中,用户可以在其中指定参数,但根“选择计数”是相同的。

这是一个例子:

SELECT COUNT(id) FROM contact_email_logs 
WHERE DATE(created_at) > '2018-12-23'
AND DATE(created_at) < '2019-01-23'
AND campaign_id = 27
AND message_id = 133

加分几点:

  1. 数据需要能够实时拉取。这意味着如果我想在这个确切的时间点检查所有通知活动的计数,我可以。所以查询会在那个时候运行以计数。

  2. contact_notification_logs 中有 28,740,585 条记录。

我是否遗漏了一些明显的东西,可以让我们优化这些查询的运行时间?

【问题讨论】:

    标签: mysql database optimization query-optimization


    【解决方案1】:

    对于第一个读取查询: 您在 created_at 字段上有索引吗?

    对于第二次读取查询: 您是否有基于三个字段的索引:created_at、campaign_id 和 message_id?

    如果没有,看看https://dev.mysql.com/doc/refman/5.5/en/create-index.html

    【讨论】:

    • 嘿!感谢您的回复。我们对所有字段都有索引,但日期字段除外。不知道为什么 - 我正在检查。
    【解决方案2】:

    无效的日期范围导致检查的行数过多

    WHERE DATE(created_at) > '2018-12-23'
      AND DATE(created_at) < '2019-01-23'
      AND campaign_id = 27
      AND message_id = 133
    

    不要这样写日期比较。它不能使用涉及created_at 的索引,因为它隐藏在函数调用中(DATE())。而是:

    WHERE created_at >= '2018-12-23'
      AND created_at  < '2018-12-23' + INTERVAL 1 MONTH
    

    如果 DATE() 的东西是由 3rd 方包生成的,你需要放弃它。

    缺乏合适的索引

    那么……你需要一个复合索引:

    INDEX(campaign_id, message_id,   -- in either order
          created_at)                -- after those
    

    为了简单的“今天”

    SELECT COUNT(*) FROM contact_notification_logs 
        WHERE created_at >= '[current date]'
          AND created_at  < '[current date]' + INTERVAL 1 DAY
    
    INDEX(created_at)  -- the previous index will not help for _this_ query
    

    需要汇总表

    28M 行,你可能会发现我上面的建议是不够​​的。要再获得 10 倍的改进,build and maintain a Summary Table。建议使用天数,而不是数周或数月作为解决方案。

    其他

    除非您需要检查id 是否为NULL,否则不要使用COUNT(id)。相反,请使用通用模式:COUNT(*)

    如果created_atDATE 类型,则原始查询为一个月,减去一天。如果是DATETIME,则缺少开始日期的午夜。使用我的代码,无论数据类型如何,它都能正常工作。

    如需进一步讨论,请提供SHOW CREATE TABLE

    【讨论】:

      猜你喜欢
      • 2017-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-23
      • 2012-03-02
      • 2011-08-05
      • 1970-01-01
      相关资源
      最近更新 更多