mysql - Nodejs Mysql 优化查询

标签 mysql node.js performance left-join

我在nodejs v8.9.4中使用mysql2模块。
这是我从消息队列中获取满足以下条件的消息的函数:

状态==0
如果 status==1botId 数量小于 10
如果 wait 表中 botId+chatIdretry_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中,混合DESCASC消除了索引的使用(直到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/

相关文章:

Mysql 连接 URL openshift

javascript - 无法获取 Node v5.X 中全局安装的 Node.js 模块的列表

mysql - 连接数据库并在 Node 中创建数据库

php - 搜索多个值,按相关性排序(php + MySQL,无全文搜索)

MySQL 约束检查

PHP 不会在 Raspberry Pi 上的 Apache 中与 MySQL 接口(interface)

php - 无法访问 MySQL 数据库

angularjs - 使用 Angular.js 和 Node.js 将当前集合插入到另一个服务器中

java - 如何提高包裹在 ThreadLocal 中的 SimpleDateFormat 的性能?

javascript - 当我们有 gzip 时,为什么人们压缩 css 和 javascript?