NodeJS全栈开发面试题讲解——P3数据库(MySQL / MongoDB / Redis)
3.1 如何用 Node.js 连接 MySQL?你用过哪些 ORM?
面试官您好,我先介绍如何用 Node.js 连接 MySQL,然后补充我常用的 ORM 工具。
🔌 原生连接 MySQL
使用 mysql2
模块:
npm install mysql2
const mysql = require('mysql2/promise');const pool = mysql.createPool({host: 'localhost',user: 'root',database: 'test',password: '123456',waitForConnections: true,connectionLimit: 10,
});async function queryUsers() {const [rows] = await pool.query('SELECT * FROM users');console.log(rows);
}
🛠 我用过的 ORM:
ORM 工具 | 适用范围 | 特点 |
---|---|---|
Sequelize | MySQL/PostgreSQL/SQLite | 常用于 Node.js 项目,支持关系定义、事务等 |
TypeORM | NestJS(+ TypeScript) | 装饰器风格,强类型支持好,适合 Nest 项目 |
Prisma | Modern ORM | 自动生成 TS 类型,支持代码提示,开发效率高 |
✅ 3.2 Sequelize(或 TypeORM)如何实现一对多、多对多关系?
ORM 的关系映射是重点,我以 Sequelize 为例说明一对多、多对多怎么建模。
🔗 一对多(One-To-Many)
场景:一个用户有多个文章
User.hasMany(Post); // 在 Post 中生成 userId 外键
Post.belongsTo(User); // 反向关系
查询:
User.findAll({ include: Post });
🔗 多对多(Many-To-Many)
场景:文章可以有多个标签,标签可以属于多个文章
Post.belongsToMany(Tag, { through: 'PostTags' });
Tag.belongsToMany(Post, { through: 'PostTags' });
ORM 自动生成中间表 PostTags
,并能联表查询。
🧩 TypeORM 实现方式:
@Entity()
export class User {@OneToMany(() => Post, post => post.user)posts: Post[];
}@Entity()
export class Post {@ManyToOne(() => User, user => user.posts)user: User;
}
多对多:
@ManyToMany(() => Tag, tag => tag.posts)
@JoinTable()
tags: Tag[];
✅ 3.3 Redis 在全栈项目中常见用途有哪些?举例说明
Redis 是非常常见的中间件,我用它处理过缓存、限流、队列、会话等功能,下面我举几个常用场景:
📌 常见用途和示例:
用途 | 示例 |
---|---|
✅ 数据缓存 | 用户信息、商品列表、排行榜缓存 |
✅ Session 管理 | 登录状态存 Redis,支持多服务共享 |
✅ 分布式锁 | 防止秒杀系统超卖、抢单并发 |
✅ 消息队列 | 使用 Redis List 实现异步下单 |
✅ 访问限流 | IP 限流、接口防刷 |
示例:接口缓存
const key = `user:${userId}`;
const cached = await redis.get(key);if (cached) return JSON.parse(cached);const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
await redis.setEx(key, 60, JSON.stringify(user)); // 缓存 1 分钟
✅ 3.4 如何实现缓存更新策略(如 Cache-Aside)?
面试官,我用过多种缓存策略,最常用的是 Cache-Aside,也叫旁路缓存。
🧠 Cache-Aside 模式:
-
读请求先查缓存,没有再查数据库,然后写入缓存;
-
写请求时,更新数据库,然后删除或更新缓存。
// 读取
async function getUser(id) {const key = `user:${id}`;const cache = await redis.get(key);if (cache) return JSON.parse(cache);const user = await db.query('...');await redis.setEx(key, 300, JSON.stringify(user));return user;
}// 更新
async function updateUser(id, data) {await db.update('users', data);await redis.del(`user:${id}`); // 延迟写
}
✅ 其他策略(了解加分):
策略 | 特点 |
---|---|
Write-Through | 每次写数据库也写缓存 |
Write-Behind | 写操作先写缓存,后台再更新数据库 |
Cache-Aside | 应用控制缓存的读取和失效(最灵活) |
✅ 3.5 如何处理事务?MySQL 事务是如何保证一致性的?
事务是后端开发的重中之重,关系到数据一致性。我以 Sequelize 和原生方式分别说明,并简要介绍事务四大特性。
🔁 Sequelize 中事务处理:
const t = await sequelize.transaction();try {await User.update(..., { transaction: t });await Order.create(..., { transaction: t });await t.commit();
} catch (err) {await t.rollback();
}
⚙ 原生 MySQL 示例(mysql2):
const conn = await pool.getConnection();
try {await conn.beginTransaction();await conn.query('UPDATE account SET balance = balance - 100 WHERE id=1');await conn.query('UPDATE account SET balance = balance + 100 WHERE id=2');await conn.commit();
} catch (err) {await conn.rollback();
}
✅ MySQL 的事务一致性靠什么保证?
ACID 四大特性:
特性 | 含义 |
---|---|
A 原子性 | 要么全部成功,要么全部失败 |
C 一致性 | 执行完事务后,数据从一个一致状态到另一个状态 |
I 隔离性 | 多事务并发不互相干扰(通过隔离级别控制) |
D 持久性 | 提交后永久生效,断电也不丢失(靠 redo log) |
🚧 MySQL 是如何实现事务的?
-
使用 InnoDB 引擎;
-
依赖于 redo log(重做日志) 和 undo log(回滚日志);
-
控制隔离性通过设置
REPEATABLE READ
,READ COMMITTED
等隔离级别。
✅ 总结:
问题编号 | 技术点 |
---|---|
3.1 | Node 连接 MySQL,使用 ORM(Sequelize / TypeORM) |
3.2 | 一对多、多对多关系映射与查询 |
3.3 | Redis 多场景应用:缓存、限流、队列等 |
3.4 | Cache-Aside 缓存策略的实现与代码演示 |
3.5 | 事务操作方式 + MySQL ACID 原理 |