在本地开发、自动化测试以及各类受限环境(如缺乏系统管理员权限、无法安装 Docker 或未预装数据库的开发机)中,通过 npm 可以直接运行原生的数据库服务端。利用 Node.js 子进程拉起对应操作系统的原生预编译二进制引擎,监听标准 TCP 端口并暴露连接 URI,可作为标准数据库直连使用。
涉及的核心数据库及其对应 npm 包如下:
embedded-postgres):自动拉取原生 PostgreSQL 引擎并执行集群初始化(initdb),支持临时运行或本地数据持久化,完整兼容原生 SQL 语法、连接池与扩展能力。mysql-memory-server):基于真实 MySQL / MariaDB 二进制文件拉起服务进程,提供完整的原生 MySQL 方言与特性支持(如 JSON_EXTRACT、ON DUPLICATE KEY UPDATE),规避纯 JS 模拟器的语法兼容差异。mongodb-memory-server):下载并运行官方 MongoDB 引擎,支持内存存储模式与多节点副本集(Replica Set)模式,覆盖事务与变更流(Change Streams)测试需求。redis-memory-server):跨平台拉起原生 Redis 服务(Linux 与 macOS 环境使用官方二进制,Windows 环境使用兼容协议的 Memurai),具备较低的内存开销与快速启动特性,原生支持 Redis 数据结构、Lua 脚本执行及发布订阅机制。这四个包均基于当前操作系统与架构自动匹配二进制文件,生命周期可控且支持随进程销毁自动清理,为缺少容器环境或无需持久部署的特殊场景提供了免安装的本地数据库运行方式。
不同于用 JavaScript 重写一套解析器的“玩具级模拟器”,这四个库走的都是“原生二进制子进程”路线:
child_process 在后台启动真实的数据库服务,监听本地空闲的 TCP 端口。.stop() 即可优雅关闭进程,并自动清理临时数据目录。mongodb-memory-servermongodb-memory-server 是这一领域的开山鼻祖,周下载量超百万次。它默认使用内存存储引擎(ephemeralForTest),数据完全不落盘,启动和读写极快;同时还贴心地内置了副本集(Replica Set)支持,完美解决 MongoDB 事务和 Change Streams 必须在副本集下才能运行的痛点。
安装:
npm install -D mongodb-memory-server
代码示例(启动副本集并获取 URI):
import { MongoMemoryReplSet } from 'mongodb-memory-server';
import mongoose from 'mongoose';
async function startMongo() {
// 创建单节点副本集,完美支持 MongoDB 事务 (Transactions)
// 若无需事务,使用 MongoMemoryServer.create() 即可,启动更快
const replSet = await MongoMemoryReplSet.create({
replSet: { count: 1, storageEngine: 'wiredTiger' },
});
const uri = replSet.getUri();
console.log('MongoDB 已启动,URI:', uri);
// 输出示例: mongodb://127.0.0.1:54321/?replicaSet=testset
// 使用 Mongoose 正常连接
await mongoose.connect(uri, { dbName: 'my_app' });
// ... 执行业务或测试代码 ...
await mongoose.disconnect();
await replSet.stop(); // 自动清理进程与临时文件
}
embedded-postgres在 PostgreSQL 领域,当你需要真实的并发连接池、完整的扩展支持以及标准端口监听时,embedded-postgres 是最稳妥的选择。它会自动拉取对应系统的原生 Postgres 二进制包,并帮你屏蔽掉繁琐的 initdb 和权限配置过程。
安装:
npm install -D embedded-postgres
代码示例:
import EmbeddedPostgres from 'embedded-postgres';
import pg from 'pg';
async function startPostgres() {
const embeddedPg = new EmbeddedPostgres({
databaseDir: './.pg-data', // 数据目录
user: 'postgres',
password: 'password',
port: 5433,
persistent: false, // 设为 true 可在本地开发时持久化保存数据
});
// 初始化并启动服务
await embeddedPg.initialise();
await embeddedPg.start();
await embeddedPg.createDatabase('test_db');
const uri = 'postgresql://postgres:password@127.0.0.1:5433/test_db';
console.log('PostgreSQL 已启动,URI:', uri);
// 使用标准 pg 连接池连接
const pool = new pg.Pool({ connectionString: uri });
const { rows } = await pool.query('SELECT version();');
console.log(rows[0]);
await pool.end();
await embeddedPg.stop();
}
mysql-memory-server由于 MySQL 缺少像 SQLite 或 pg-mem 那样高兼容度的纯 JS 内存替代品,过去在跑 MySQL 单元测试时往往只能妥协用 SQLite 替代,导致方言函数(如 JSON_EXTRACT、ON DUPLICATE KEY UPDATE)频繁报错。mysql-memory-server 彻底解决了这个问题。
安装:
npm install -D mysql-memory-server
代码示例:
import { createDB } from 'mysql-memory-server';
import mysql from 'mysql2/promise';
async function startMySQL() {
// 自动下载并启动真实 MySQL 实例
const db = await createDB({
version: '8.0.x',
});
const uri = `mysql://${db.username}@127.0.0.1:${db.port}/${db.dbName}`;
console.log('MySQL 已启动,URI:', uri);
const connection = await mysql.createConnection(uri);
await connection.execute('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50))');
await connection.execute('INSERT INTO users VALUES (1, "Alice")');
const [rows] = await connection.execute('SELECT * FROM users');
console.log(rows); // [{ id: 1, name: 'Alice' }]
await connection.end();
await db.stop();
}
redis-memory-serverredis-memory-server 能够在几十毫秒内拉起一个真实的 redis-server 实例(在 Linux/macOS 下使用官方源码编译,在 Windows 下自动匹配兼容 Redis 协议的原生 Memurai 二进制程序),每个实例仅占用约 4MB 内存。无论是测试复杂的 Lua 脚本、Stream 队列还是 Redlock 分布式锁,都能做到与线上环境零偏差。
安装:
npm install -D redis-memory-server
代码示例:
import { RedisMemoryServer } from 'redis-memory-server';
import Redis from 'ioredis';
async function startRedis() {
const redisServer = new RedisMemoryServer();
const host = await redisServer.getHost();
const port = await redisServer.getPort();
const uri = `redis://${host}:${port}`;
console.log('Redis 已启动,URI:', uri);
const client = new Redis(uri);
// 完美支持真实 Lua 脚本执行
const val = await client.eval("return redis.call('set', KEYS[1], ARGV[1])", 1, 'mykey', 'hello');
console.log('Lua 执行结果:', val); // 'OK'
client.disconnect();
await redisServer.stop();
}
| 维度 | mongodb-memory-server |
embedded-postgres |
mysql-memory-server |
redis-memory-server |
|---|---|---|---|---|
| 底层引擎 | 官方 mongod 二进制 |
官方 postgres 二进制 |
官方 mysqld 二进制 |
官方 redis-server / Memurai |
| 冷启动耗时 | 约 0.5 ~ 1.5 秒 | 约 1.5 ~ 3 秒 | 约 2 ~ 4 秒 | 极快(约 20 ~ 100 毫秒) |
| 内存占用 | ~60 MB | ~30 MB | ~150 MB | ~4 MB |
| 数据持久化 | 支持(指定 dbPath 及 wiredTiger) |
支持(设置 persistent: true) |
默认临时目录(停止即删) | 支持(可通过 args 传参配置) |
| 杀手级特性 | 支持一键启动多节点副本集(支持事务) | 支持 TEMPLATE 数据库毫秒级克隆 |
原生支持 MySQL 8 全部方言与特性 | 原生支持所有 Redis 模块与复杂 Lua |
把这四个工具引入团队项目时,掌握以下几个工程技巧可以让体验再上一个台阶:
npm run dev):编写独立的 scripts/dev-services.ts 脚本,固定各个服务的端口号(如 PG 固定 5433、Redis 固定 6380),并开启持久化目录(如 ./.local-data/pg),将该目录加入 .gitignore,保证本地开发重启代码时数据不丢失。npm run test):在 Vitest 或 Jest 的 globalSetup 中启动这些服务,不指定端口由系统动态分配空闲端口,关闭持久化,并将动态生成的连接 URI 注入到环境变量 process.env.DATABASE_URL 中,支持多测试进程并发。拉起操作系统原生进程通常需要 1~3 秒,避免在每个测试用例中反复调用 start() 和 stop(),推荐采取以下策略:
CREATE DATABASE test_1 TEMPLATE base_db 进行模板克隆,或在测试用例后执行 TRUNCATE TABLE ... CASCADE 及事务回滚。afterEach 中遍历集合执行 deleteMany({})。afterEach 中调用 await redis.flushall() 清空键值。这四类工具在二进制分发机制上存在差异:embedded-postgres 采用 npm 架构子包(@embedded-postgres/*)分发,配置常规 npm 镜像源即可直接拉取;而 MongoDB、MySQL 与 Redis 因二进制体积较大(通常达数百兆)未直接打包至 npm,需在首次运行时由脚本从外部官方源下载。
为避免网络延迟或流水线阻塞,可采取以下应对方案:
embedded-postgres 无需额外配置专用下载源,确保当前 npm 镜像源包含对应平台的 @embedded-postgres/* 包即可。mongodb-memory-server 支持在环境变量或 .npmrc 中配置 MONGOMS_DOWNLOAD_MIRROR(或指向内网的 MONGOMS_DOWNLOAD_URL);redis-memory-server 支持配置 REDISMS_DOWNLOAD_MIRROR(或 REDISMS_DOWNLOAD_URL)。mysql-memory-server 默认请求官方 CDN,可在配置中启用 downloadBinaryOnce: true,并在内网环境或 CI 流水线中预置缓存文件。~/.cache/mongodb-binaries、~/.cache/redis-memory-server 及 node_modules/.cache)加入构建缓存,避免重复拉取。在日常高频的本地单机开发和追求极速反馈的单元测试场景下,embedded-postgres、mysql-memory-server、mongodb-memory-server 和 redis-memory-server 提供了一种更加轻量、纯粹、内聚于 Node.js 工具链的解决方案。