需要一些指导来优化MySQL中的报告

时间:2019-01-23 15:32:44

标签: mysql database optimization query-optimization

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

  • 向各种广告系列的联系人发送通知
  • 发送通知时,将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条记录。

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

2 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

无效的日期范围会导致检查太多行

import itertools

D = [[[0, 0], [0, 1], [0, 2], [0, 1]], [[0, 0], [1, 0], [1, 1], [1, 0]]]

P = list(itertools.chain.from_iterable(zip(*D)))
print(P)

不要这样写日期比较。它不能使用包含[[0, 0], [0, 0], [0, 1], [1, 0], [0, 2], [1, 1], [0, 1], [1, 0]] 的索引,因为它被隐藏在函数调用(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()

需要摘要表

对于28M行,您可能会发现上面的建议不够。要获得10倍的改进,请build and maintain a Summary Table。建议使用几天而不是几周或几个月的分辨率。

其他

除非您需要检查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 ,否则不要使用COUNT(id)。而是使用通用模式:id

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

为进一步讨论,请提供DATE