如何用Node.js读取大文本文件并高效插入PostgreSQL数据库?
优化Node.js批量插入PostgreSQL的性能方案
针对大文件数据插入的性能问题,原代码的核心瓶颈在于单条数据高频插入和一次性加载超大文件,以下是具体优化方案:
原代码的性能痛点
- 数据库交互开销过大:每条数据单独执行INSERT,频繁的网络往返、事务初始化和提交占了绝大多数耗时。
- 内存资源浪费:
fs.readFileSync直接把50万行文件全部读入内存,既占用大量内存,还会阻塞Node.js事件循环。 - 冗余IO拖慢速度:每条插入都打印日志,大量控制台输出额外消耗系统资源。
- 数据分割逻辑瑕疵:初始循环中
i%10===0会先推入空数组,导致第一条数据无效,还可能打乱字段对应关系。
优化后的实现代码
const fs = require('fs'); const readline = require('readline'); const { Client } = require('pg'); // 数据库连接配置 const client = new Client({ host: '你的数据库地址', port: 5432, user: '用户名', password: '密码', database: '数据库名' }); // 批量插入批次大小,可根据服务器性能调整(建议1000-5000) const BATCH_SIZE = 1000; let batchData = []; // 批量插入函数 const insertBatch = async () => { if (batchData.length === 0) return; try { // 构建多行INSERT语句,处理单引号转义以避免SQL注入 const valueStrings = batchData.map(row => `(${row.map(val => `'${val.replace(/'/g, "''")}'`).join(',')})` ).join(','); const sql = `INSERT INTO requirement(container, module, mod_devdate, part_no, qty, tapdate, tap_qty, taptime, sup_cd, namc_id) VALUES ${valueStrings}`; await client.query(sql); console.log(`已插入 ${batchData.length} 条数据`); batchData = []; } catch (err) { console.error('批量插入失败:', err); throw err; } }; // 流式读取并处理文件 const processDataFile = async () => { await client.connect(); const rl = readline.createInterface({ input: fs.createReadStream('data.txt', 'utf-8'), crlfDelay: Infinity // 兼容所有换行格式 }); for await (const line of rl) { // 按|分割字段,保留空白内容 const fields = line.split('|'); // 确保字段数为10,不足的补空字符串 while (fields.length < 10) fields.push(''); // 清理字段中的控制字符,保留原有空白内容 const processedFields = fields.slice(0,10).map(f => f.replace(/\x00|\r\n/g, '')); batchData.push(processedFields); // 达到批次大小则执行插入 if (batchData.length >= BATCH_SIZE) { await insertBatch(); } } // 处理剩余的不足一批的数据 await insertBatch(); await client.end(); console.log('所有数据插入完成'); }; processDataFile().catch(err => console.error('处理流程失败:', err));
进阶优化建议
- 使用COPY命令:PostgreSQL的
COPY FROM是批量插入效率最高的方式,适合超大规模数据。可以将每行转换为CSV格式后,通过COPY requirement FROM STDIN WITH (FORMAT csv, DELIMITER '|')导入,性能比多行INSERT提升数倍。 - 调整批次大小:根据服务器内存和数据库负载,测试不同批次大小(如2000、5000),找到最优值。
- 事务批量提交:在批量插入前执行
BEGIN,所有批次完成后执行COMMIT,减少事务提交的开销。 - 连接池复用:如果有后续数据库操作,改用连接池(
pg.Pool)代替单连接,提升连接利用率。
内容的提问来源于stack exchange,提问作者piyushCodes6
相关产品推荐
相关产品推荐

