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 原理 |