我在nodejs v8.9.4中使用mysql2
模块。
这是我从消息队列中获取满足以下条件的消息的函数:
状态==0
如果 status==1
的 botId
数量小于 10
如果 wait
表中 botId+chatId
的 retry_after
并且 botId
小于 NOW
(时间戳)
如果没有与 status==1
chatId
static async Find(activeMessageIds, maxActiveMsgPerBot) {
let params = [maxActiveMsgPerBot];
let filterActiveMessageIds = ' ';
let time = Util.GetTimeStamp();
if (activeMessageIds && activeMessageIds.length) {
filterActiveMessageIds = 'q.id NOT IN (?) AND ';
params.push(activeMessageIds);
}
let q =
`select q.*
from bot_message_queue q
left join bot_message_queue_wait w on q.botId=w.botId AND q.chatId=w.chatId
left join bot_message_queue_wait w2 on q.botId=w2.botId AND w2.chatId=0
where
q.status=0 AND
q.botId NOT IN (select q2.botId from bot_message_queue q2 where q2.status=1 group by q2.botId HAVING COUNT(q2.botId)>?) AND
${filterActiveMessageIds}
q.chatId NOT IN (select q3.chatId from bot_message_queue q3 where q3.status=1 group by q3.chatId) AND
(w.retry_after IS NULL OR w.retry_after <= ?) AND
(w2.retry_after IS NULL OR w2.retry_after <= ?)
order by q.priority DESC,q.id ASC
limit 1;`;
params.push(time);
params.push(time);
let con = await DB.connection();
let result = await DB.query(q, params, con);
if (result && result.length) {
result = result[0];
let updateQ = `update bot_message_queue set status=1 where id=?;`;
await DB.query(updateQ, [result.id], con);
} else
result = null;
con.release();
return result;
}
此查询在我的本地开发系统上运行良好。它在 phpmyadmin 服务器中也可以在几毫秒内正常运行。
但是当它运行时抛出nodejs+mysql2,CPU使用率上升到100% 该表中只有 2K 行。
CREATE TABLE IF NOT EXISTS `bot_message_queue` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`botId` int(10) UNSIGNED NOT NULL,
`chatId` varchar(50) CHARACTER SET utf8 NOT NULL,
`type` varchar(50) DEFAULT NULL,
`message` longtext NOT NULL,
`add_date` int(10) UNSIGNED NOT NULL,
`status` tinyint(2) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=waiting,1=sendig,2=sent,3=error',
`priority` tinyint(1) UNSIGNED NOT NULL DEFAULT '5' COMMENT '5=normal messages,<5 = bulk messages',
`delay_after` int(10) UNSIGNED NOT NULL DEFAULT '1000',
`send_date` int(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `botId` (`botId`,`status`),
KEY `botId_2` (`botId`,`chatId`,`status`,`priority`),
KEY `chatId` (`chatId`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `bot_message_queue_wait` (
`botId` int(10) UNSIGNED NOT NULL,
`chatId` varchar(50) CHARACTER SET utf8 NOT NULL,
`retry_after` int(10) UNSIGNED NOT NULL,
PRIMARY KEY (`botId`,`chatId`),
KEY `retry_after` (`retry_after`),
KEY `botId` (`botId`,`chatId`,`retry_after`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
更新:Real table data here
更新2:
获取消息时间:
- 最短:1788 毫秒
- 最大:44285 毫秒
- 平均:20185.4 毫秒
直到昨天为止,最大值大约是 20 毫秒:( 现在是 40 秒!!!
更新 3:我合并了这 2 个连接和位置:
left join bot_message_queue_wait w on q.botId=w.botId AND q.chatId=w.chatId
left join bot_message_queue_wait w2 on q.botId=w2.botId AND w2.chatId=0
(w.retry_after IS NULL OR w.retry_after <= ?) AND
(w2.retry_after IS NULL OR w2.retry_after <= ?)
变成一个,我希望这能按预期工作!
left join bot_message_queue_wait w on q.botId=w.botId AND ( q.chatId=w.chatId OR w.chatId=0 )
暂时我删除了 2 个 where,查询时间恢复正常。
q.botId NOT IN (select ...)
q.chatId NOT IN (select ...)
因此,这两个查询是阻塞点,需要修复。
最佳答案
NOT IN ( SELECT ... )
很难优化。
OR
无法优化。
在ORDER BY
中,混合DESC
和ASC
消除了索引的使用(直到8.0)。考虑将 ASC
更改为 DESC
。之后,INDEX(priority, id)
可能有所帮助。
什么是${filterActiveMessageIds}
?
不需要GROUP BY
NOT IN ( SELECT q3.chatId
from bot_message_queue q3
where q3.status=1
group by q3.chatId )
INDEX(status, chatid)
按此顺序将使该子查询受益。
INDEX(status, botid)
按此顺序
有关索引创建的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql
关于mysql - Nodejs Mysql 优化查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49937703/