面试题答案
一键面试处理数据库服务器短暂不可用
- 重试机制:
- 当检测到数据库连接失败(由于服务器短暂不可用)时,采用指数退避算法进行重试。例如:
const mysql = require('mysql'); const pool = mysql.createPool({ host: 'your - host', user: 'your - user', password: 'your - password', database: 'your - database' }); function queryWithRetry(sql, values, retries = 3, delay = 1000) { return new Promise((resolve, reject) => { const attempt = () => { pool.getConnection((err, connection) => { if (err) { if (retries > 0) { setTimeout(() => { attempt(); }, delay); delay = delay * 2; retries--; } else { reject(err); } } else { connection.query(sql, values, (queryErr, results) => { connection.release(); if (queryErr) { reject(queryErr); } else { resolve(results); } }); } }); }; attempt(); }); }
- 健康检查:
- 定期(例如每隔一定时间间隔,如10秒)通过执行简单的SQL查询(如
SELECT 1
)来检查数据库服务器的可用性。如果查询失败,标记数据库为不可用,并触发重试机制。
setInterval(() => { pool.getConnection((err, connection) => { if (err) { console.error('Database connection error during health check:', err); return; } connection.query('SELECT 1', (queryErr, results) => { connection.release(); if (queryErr) { console.error('Database seems unavailable during health check:', queryErr); } else { console.log('Database is available.'); } }); }); }, 10000);
- 定期(例如每隔一定时间间隔,如10秒)通过执行简单的SQL查询(如
处理连接泄露
- 连接超时设置:
- 在连接池配置中设置连接的最大使用时间(例如10分钟)。如果一个连接使用时间超过这个限制,强制回收该连接。
const pool = mysql.createPool({ host: 'your - host', user: 'your - user', password: 'your - password', database: 'your - database', connectionLimit: 10, acquireTimeout: 600000 // 10分钟 });
- 监控与日志记录:
- 实现一个监控系统,记录每个连接的获取时间、释放时间和使用时长。如果发现有连接长时间未释放,通过日志输出详细信息,以便排查问题。
const connectionMap = new Map(); pool.getConnection((err, connection) => { if (err) { console.error('Error getting connection:', err); } else { const startTime = new Date(); connectionMap.set(connection, startTime); connection.on('release', () => { const endTime = new Date(); const duration = endTime - startTime; console.log(`Connection used for ${duration} ms`); connectionMap.delete(connection); }); } });
分布式系统中多 Node.js 实例连接池协同避免资源竞争
- 集中式配置管理:
- 使用一个集中式的配置管理工具(如Consul、Etcd等)来存储数据库连接池的配置信息,包括数据库地址、用户名、密码、连接池大小等。所有Node.js实例从这个集中式配置中心获取配置,确保配置的一致性。
- 分布式锁:
- 利用分布式锁(如基于Redis的分布式锁)来控制数据库连接资源的获取。在每个Node.js实例获取数据库连接之前,先尝试获取分布式锁。如果获取成功,则可以安全地获取数据库连接;如果获取失败,则等待一段时间后重试。
- 例如,使用ioredis库实现基于Redis的分布式锁:
const Redis = require('ioredis'); const redis = new Redis(); async function getConnectionWithLock(pool) { const lockKey = 'database - connection - lock'; const lockValue = Date.now(); const lockAcquired = await redis.set(lockKey, lockValue, 'NX', 'EX', 10); if (lockAcquired) { try { return await new Promise((resolve, reject) => { pool.getConnection((err, connection) => { if (err) { reject(err); } else { resolve(connection); } }); }); } finally { await redis.del(lockKey); } } else { await new Promise(resolve => setTimeout(resolve, 100)); return getConnectionWithLock(pool); } }
- 连接池分区:
- 根据一定的规则(如按数据分片、按业务模块等)对数据库连接池进行分区。每个Node.js实例负责管理和使用特定分区的连接池,避免不同实例之间对连接资源的竞争。例如,对于一个分库分表的数据库,可以让不同的Node.js实例负责不同库或表的连接管理。