我的团队维护着一个应用/数据库,该应用/数据库每周处理数百万条记录。该过程非常简单:
对日志的写入和读取过程会花费非常长的时间,我们正在寻找一种优化它的方法。
在发送通知时会发生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'),
发生两个读取语句:
SELECT COUNT(id) FROM contact_notification_logs
WHERE DATE(created_at) = '[current date]'
虽然简单,但执行起来仍然需要很长时间。
这里是一个例子:
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
一些额外的要点:
需要能够实时提取数据。意思是如果我想在这个确切的时间点检查所有通知活动的计数,我可以。因此,查询可以同时计算所有时间。
contact_notification_logs中有28,740,585条记录。
我在这里是否遗漏了一些明显的东西,以便我们优化这些查询的运行时间?
答案 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
。