你学废了吗:对比下node的mysql和mysql2

上次写了一篇node使用mysql的入门:juejin.cn/post/730224...,结果写完才发现mysql模块有一个新的版本:mysql2。那就来看看mysql2模块对比mysql模块有什么区别。

mysql模块的问题

nodejs的mysql模块虽然功能强大,但是由于发布的时间比较久,后续的版本有些地方跟不上大前端技术的迭代脚步(个人觉得是项目过于庞大,支持新的语法需要做大量的重构),还是存在一些缺点:

  1. 回调问题,mysql的操作都是通过回调函数来执行,这就回到了我们熟悉的"回调地狱",过多的回调函数嵌套,会大大的破坏代码的可读性和可维护性。
  2. Promise和async/await,跟早起的node其他模块一样,mysql模块并不支持Promise和async/await语法,要使用这些语法,需要自形封装mysql的操作。
  3. 缺少高级功能支持,例如:语句预处理、流式查询等,对新版本的mysql支持不足(mysql8的密码加密格式问题等)
  4. 性能问题,在大数据量或者高并发的情况下,mysql模块会出现性能受限的情况,主要原因是其无法发挥nodejs的异步特性和性能特性。

针对上面的问题,mysql2做了很多改进,mysql2基于mysql-native项目进行改进,同时兼容mysql的API。

mysql2的使用

安装

shell 复制代码
npm install mysql2

建立链接

js 复制代码
const mysql2 = require('mysql2');
const connection = mysql2.createConnection({
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'demo'
});

执行sql

js 复制代码
// 查询
connection.query(
  'select * from demo',
  function(err, results, fields) {
    console.log(results);
  }
);

// 插入
connection.execute(
  'insert into demo set name="sk"',
   function(err, results, fields) {
    console.log(results);
  }
)

写法看起来跟mysql也没什么不一样,这主要是为了向下兼容。但是sql的执行结果会自动转换格式,不需要再自行处理:

js 复制代码
connection.query(
  'select * from demo',
  function(err, results, fields) {
    console.log(results); //  [ { id: 1, name: '0' }, { id: 2, name: '1' } ]
  }
);

如果需要使用mysql2的新特性,需要引入对应的模块。

使用Promise和async/await

要使用promise的特性,需要引入promise的模块

js 复制代码
const mysql2 = require('mysql2/promise'); // 替换掉原来的require('mysql2')

引入promise模块后就可以使用promise的写法:

js 复制代码
// promise
mysql2.createConnection({
    host: 'localhost',
    user: 'root',
    password: '123456',
    database: 'demo'
}).then(res => {
    connection = res;
});

// async/await
const connection = await mysql2.createConnection({
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'demo'
});

// 查询
connection.query('select * from demo2').then(([results, fields]) => {
    console.log(results);
});

let [results, fields] = await connection.query('select * from demo2 where id > 3');

注意:这里返回的结果是一个数组,包括results和fields,results为执行的结果,fields为与结果相关的字段的元数据。

使用TypeScript

mysql2内置TS的支持。

js 复制代码
import mysql2, { RowDataPacket } from 'mysql2';

const connection = mysql2.createConnection({
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'demo'
});

connection.query<RowDataPacket[]>('select * from demo2', (err, rows) => {
  console.log(rows);
});

具体内容可以参考官方文档:github.com/sidorares/n...

其他

其他新特性包括连接池、流式查询等,这些涉及到其他的mysql知识点,后面有机会再聊,使用的方法可以参考官方文档:github.com/sidorares/n...

mysql2与mysql的性能对比

mysql2官方说明第一个点就是它比mysql模块具备更好的性能。那么mysql2是怎么做到的?

首先,最大的点应该是使用了libmysqlclient,libmysqlclient是使用C语言实现的库,mysql2是基于该库开发,使得其可以跟mysql数据库进行低级别的通讯,从而提高性能和效率。使用编译后的库启动速度也更高。与mysql模块对比,mysql模块使用的是JavaScript实现的,没有依赖其他库,只能使用node底层的异步I/O和事件循环,这对其性能影响比较大。

其次,在协议解析器上做了更多的改进:

  1. 更高效的数据解析方式:采用更高效的数据解析方式,能更快地解析MySQL协议中的数据,如使用Buffer对象来存储数据,使用ES6的解构赋值来解析数据等,使得数据解析更加快速和高效。
  2. 更高效的数据序列化方式:采用了更高效的数据序列化方式,能更快地将数据序列化为MySQL协议中的格式。如使用ES6的模板字符串来生成SQL语句,使用Buffer对象来存储数据等。
  3. 增加流式解析的支持:数据可以在解析的同时被处理,不需要等待整个数据包解析完成。

其他的还有像连接池之类的,减少创建链接的额外开销等方式来减少性能消耗。

做个小实验

接下来我们来做个对比实验,我们在同一个数据库创建两个一样的表,分别用mysql和mysql2模块来进行插入和查询,对比下时间的消耗。

表结构如下,只有一个id和name,id自增。mysql使用demo1表,mysql2使用demo2表。

sql 复制代码
CREATE TABLE `demo1` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL COMMENT 'name',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='demo表1';

插入数据的代码:

js 复制代码
function insertRow(i) {
    return new Promise((resolve, reject) => {
        connection.query(
            `insert into demo1 set name='${i}'`, 
            (error, results, fields) => {
                if (error) {
                    reject();
                    throw error;
                }
                resolve();
            }
        );
    });
}

async function run(times) {
    let i = 0;
  	console.time();
    for(i; i < times; i++) {
        await insertRow(i);
    }
    console.timeEnd();
}

为了尽量减少代码写法不一致带来的误差,mysql2使用向下兼容的代码,保证执行的代码一致。同时在相同配置的容器中执行代码,减少系统层面的误差。

我们执行代码,分别插入1000、4000、7000、10000条数据,每种次数执行5次,不会清表,看看两边的耗时对比:

次数 1000 4000 7000 10000
mysql 1 3.065s 13.784s 20.749s 27.704s
2 2.923s 11.573s 20.362s 27.652s
3 2.941s 11.941s 20.254s 31.822s
4 2.917s 11.767s 24.963s 31.362s
5 2.839s 11.457s 27.387s 31.450s
平均值 2.937s 12.104s 22.743s 29.997s
mysql2 1 3.085s 11.776s 19.406s 28.061s
2 2.830s 11.380s 19.229s 27.601s
3 2.850s 11.424s 19.157s 27.292s
4 2.874s 11.620s 19.070s 27.234s
5 2.790s 11.387s 23.615s 27.947s
平均值 2.885s 11.517s 20.095s 27.627s

可能在高频插入的情况下,mysql2的性能会更优一些:

接下来我们看看数据查询的情况:

查询的代码:

js 复制代码
function select(rows) {
    console.time();
    connection.query(
        `select name from demo1 limit ${rows}`, 
        (error, results, fields) => {
            if (error) {
                throw error;
            }
            console.timeEnd();
        }
    );
}

我们分别查询:1000、5000、10000、20000、25000条数据,每次查询重复一次,看看性能的对比:

次数 1000 5000 10000 20000 25000
mysql 1 10.622ms 17.358ms 19.193ms 26.517ms 27.842ms
2 2.64ms 6.366ms 8.962ms 11.735ms 15.87ms
3 2.733ms 4.452ms 5.599ms 9.328ms 11.247ms
4 3.45ms 3.357ms 5.844ms 7.765ms 18.947ms
5 1.873ms 3.072ms 4.635ms 7.773ms 9.666ms
平均值 4.264ms 6.921ms 8.847ms 12.623ms 16.714ms
去除第一次平均值 2.674ms 4.312ms 6.260ms 9.1502ms 13.933ms
mysql2 1 35.555ms 40.431ms 43.244ms 48.772ms 48.815ms
2 1.636ms 4.386ms 6.949ms 12.606ms 13.701ms
3 1.736ms 3.822ms 6.46ms 7.888ms 15.633ms
4 1.556ms 3.351ms 3.906ms 12.852ms 10.309ms
5 1.577ms 2.727ms 3.317ms 5.998ms 12.669ms
平均值 8.412ms 10.943ms 12.775ms 17.623ms 20.225ms
去除第一次平均值 1.626ms 3.573ms 5.158ms 9.836ms 13.078ms

可以看到,mysql2在第一次查询的时候明显慢一些,应该是与底层建立链接的时间消耗比较大,之后的消耗的时间明显小于mysql。

但是这个性能还是不够强,使用mysql2的连接池和流式查询,再看看整体的性能:

js 复制代码
const mysql = require('mysql2');

// 创建链接池
const pool =  mysql.createPool({
    host: 'local-mysql',
    port: 3306,
    user: 'root',
    password: '123456',
    database: 'demo',
    connectionLimit: 20 // 最多20个链接
});

// 执行查询
pool.getConnection(
    (err, connection) => { 
        if (err) {
          throw err;
        }
        const query = connection.query('select name from demo1 limit 10000');
        console.time();
        query
        .stream()
        .on('data', (row) => { })
        .on('error', (err) => {})
        .on('end', () => { 
            console.timeEnd(); 
          }); 
	}
);

执行结果如下:

次数 1000 5000 10000 20000 25000
1 4.855ms 11.471ms 15.148ms 18.993ms 20.689ms
2 2.421ms 7.133ms 8.781ms 13.117ms 14.738ms
3 2.64ms 3.72ms 4.863ms 7.158ms 7.498ms
4 1.522ms 3.18ms 5.483ms 6.909ms 7.451ms
5 1.241ms 2.417ms 4.493ms 5.801ms 8.017ms

性能明显提升了很多。

总结

本文对node的mysql和mysql2模块做了简单的对比,我们做了个小实验对比了两者在插入和查询的性能对比,当然这只是一个小实验,场景简单,样本也比较少,但是还是能看出mysql2的性能更优。从整个对比来看,mysql2在新技术特性适配和性能上明显优于mysql模块。如果是新项目或者项目比较好更新,建议使用mysql2,如果使用第三方封装的mysql库,可以看下是基于哪个mysq模块,如果没有依赖亦可以对比下其与mysql2的性能。

相关推荐
想用offer打牌3 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX5 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法5 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端