黑白梦黑白梦

  • 文章
  • 专栏
  • 文章
  • 专栏
全部文章

通过 npm 运行免安装 PostgreSQL、MySQL、MongoDB 与 Redis 数据库服务

发布于 2026-10-25约 14 分钟

在本地开发、自动化测试以及各类受限环境(如缺乏系统管理员权限、无法安装 Docker 或未预装数据库的开发机)中,通过 npm 可以直接运行原生的数据库服务端。利用 Node.js 子进程拉起对应操作系统的原生预编译二进制引擎,监听标准 TCP 端口并暴露连接 URI,可作为标准数据库直连使用。

涉及的核心数据库及其对应 npm 包如下:

  • PostgreSQL(embedded-postgres):自动拉取原生 PostgreSQL 引擎并执行集群初始化(initdb),支持临时运行或本地数据持久化,完整兼容原生 SQL 语法、连接池与扩展能力。
  • MySQL(mysql-memory-server):基于真实 MySQL / MariaDB 二进制文件拉起服务进程,提供完整的原生 MySQL 方言与特性支持(如 JSON_EXTRACT、ON DUPLICATE KEY UPDATE),规避纯 JS 模拟器的语法兼容差异。
  • MongoDB(mongodb-memory-server):下载并运行官方 MongoDB 引擎,支持内存存储模式与多节点副本集(Replica Set)模式,覆盖事务与变更流(Change Streams)测试需求。
  • Redis(redis-memory-server):跨平台拉起原生 Redis 服务(Linux 与 macOS 环境使用官方二进制,Windows 环境使用兼容协议的 Memurai),具备较低的内存开销与快速启动特性,原生支持 Redis 数据结构、Lua 脚本执行及发布订阅机制。

这四个包均基于当前操作系统与架构自动匹配二进制文件,生命周期可控且支持随进程销毁自动清理,为缺少容器环境或无需持久部署的特殊场景提供了免安装的本地数据库运行方式。

核心原理:它们是如何工作的?

不同于用 JavaScript 重写一套解析器的“玩具级模拟器”,这四个库走的都是“原生二进制子进程”路线:

  1. 自动匹配与下载:在安装或首次运行时,它们会自动识别当前操作系统(macOS / Linux / Windows)和 CPU 架构(x64 / ARM64),从官方或镜像源下载预编译好的真实数据库二进制包并缓存在本地。
  2. 拉起真实子进程:通过 Node.js 的 child_process 在后台启动真实的数据库服务,监听本地空闲的 TCP 端口。
  3. 提供标准 URI 连接:对外暴露与生产环境 100% 一致的网络协议。无论是 Prisma、TypeORM、Drizzle、Mongoose,还是 GUI 客户端(Navicat、DBeaver),都可以直接用标准 URI 连上去。
  4. 生命周期随进程销毁:在单元测试或开发服务结束时,调用 .stop() 即可优雅关闭进程,并自动清理临时数据目录。

核心数据库包使用指南

1. MongoDB:mongodb-memory-server

mongodb-memory-server 是这一领域的开山鼻祖,周下载量超百万次。它默认使用内存存储引擎(ephemeralForTest),数据完全不落盘,启动和读写极快;同时还贴心地内置了副本集(Replica Set)支持,完美解决 MongoDB 事务和 Change Streams 必须在副本集下才能运行的痛点。

安装:

Bash
npm install -D mongodb-memory-server

代码示例(启动副本集并获取 URI):

JavaScript
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(); // 自动清理进程与临时文件
}

2. PostgreSQL:embedded-postgres

在 PostgreSQL 领域,当你需要真实的并发连接池、完整的扩展支持以及标准端口监听时,embedded-postgres 是最稳妥的选择。它会自动拉取对应系统的原生 Postgres 二进制包,并帮你屏蔽掉繁琐的 initdb 和权限配置过程。

安装:

Bash
npm install -D embedded-postgres

代码示例:

JavaScript
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();
}

3. MySQL:mysql-memory-server

由于 MySQL 缺少像 SQLite 或 pg-mem 那样高兼容度的纯 JS 内存替代品,过去在跑 MySQL 单元测试时往往只能妥协用 SQLite 替代,导致方言函数(如 JSON_EXTRACT、ON DUPLICATE KEY UPDATE)频繁报错。mysql-memory-server 彻底解决了这个问题。

安装:

Bash
npm install -D mysql-memory-server

代码示例:

JavaScript
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();
}

4. Redis:redis-memory-server

redis-memory-server 能够在几十毫秒内拉起一个真实的 redis-server 实例(在 Linux/macOS 下使用官方源码编译,在 Windows 下自动匹配兼容 Redis 协议的原生 Memurai 二进制程序),每个实例仅占用约 4MB 内存。无论是测试复杂的 Lua 脚本、Stream 队列还是 Redlock 分布式锁,都能做到与线上环境零偏差。

安装:

Bash
npm install -D redis-memory-server

代码示例:

JavaScript
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(),推荐采取以下策略:

  • 全局生命周期复用:整个测试周期仅拉起一次数据库进程实例。
  • 毫秒级数据隔离:
    • PostgreSQL:利用 CREATE DATABASE test_1 TEMPLATE base_db 进行模板克隆,或在测试用例后执行 TRUNCATE TABLE ... CASCADE 及事务回滚。
    • MongoDB:在 afterEach 中遍历集合执行 deleteMany({})。
    • Redis:在 afterEach 中调用 await redis.flushall() 清空键值。

解决网络下载与 CI 缓存问题

这四类工具在二进制分发机制上存在差异:embedded-postgres 采用 npm 架构子包(@embedded-postgres/*)分发,配置常规 npm 镜像源即可直接拉取;而 MongoDB、MySQL 与 Redis 因二进制体积较大(通常达数百兆)未直接打包至 npm,需在首次运行时由脚本从外部官方源下载。

为避免网络延迟或流水线阻塞,可采取以下应对方案:

  • npm 镜像同步(PostgreSQL):embedded-postgres 无需额外配置专用下载源,确保当前 npm 镜像源包含对应平台的 @embedded-postgres/* 包即可。
  • 配置镜像环境变量(MongoDB / Redis):mongodb-memory-server 支持在环境变量或 .npmrc 中配置 MONGOMS_DOWNLOAD_MIRROR(或指向内网的 MONGOMS_DOWNLOAD_URL);redis-memory-server 支持配置 REDISMS_DOWNLOAD_MIRROR(或 REDISMS_DOWNLOAD_URL)。
  • 复用本地缓存(MySQL):mysql-memory-server 默认请求官方 CDN,可在配置中启用 downloadBinaryOnce: true,并在内网环境或 CI 流水线中预置缓存文件。
  • 配置 CI 缓存策略:在 GitHub Actions 或 GitLab CI 中,将二进制缓存目录(如 ~/.cache/mongodb-binaries、~/.cache/redis-memory-server 及 node_modules/.cache)加入构建缓存,避免重复拉取。

结语

在日常高频的本地单机开发和追求极速反馈的单元测试场景下,embedded-postgres、mysql-memory-server、mongodb-memory-server 和 redis-memory-server 提供了一种更加轻量、纯粹、内聚于 Node.js 工具链的解决方案。

目录
核心原理:它们是如何工作的?核心数据库包使用指南1. MongoDB:mongodb-memory-server2. PostgreSQL:embedded-postgres3. MySQL:mysql-memory-server4. Redis:redis-memory-server全景对比与速查表进阶工程实践:如何将它们融入你的项目?区分本地开发与自动化测试配置单元测试性能优化与数据隔离解决网络下载与 CI 缓存问题结语
上一篇测试视频构建:免版权素材获取与 FFmpeg 生成指定时长视频

©2015-2026 黑白梦 粤ICP备15018165号

联系: heibaimeng@foxmail.com