如何在Node.js与Mongoose中实现定时自动插入数据?
在Node.js + Mongoose中实现定时自动插入数据(每月末为例)
实现这类定时任务最常用的方案是借助Node.js的定时任务库,下面以node-schedule和node-cron两个主流库为例,给出具体实现步骤:
方案一:使用node-schedule(直观的时间规则定义)
1. 安装依赖
npm install node-schedule # 或 yarn add node-schedule
2. 编写定时任务逻辑
假设你有两个Mongoose模型:
CurrentData:存储实时业务数据MonthlyArchive:用于存储每月末归档的数据
示例代码:
const schedule = require('node-schedule'); const mongoose = require('mongoose'); const CurrentData = require('./models/CurrentData'); // 引入你的模型 const MonthlyArchive = require('./models/MonthlyArchive'); // 定义每月末23:59:59的执行规则 const monthlyEndRule = new schedule.RecurrenceRule(); monthlyEndRule.date = null; // null 代表每月最后一天 monthlyEndRule.hour = 23; monthlyEndRule.minute = 59; monthlyEndRule.second = 59; monthlyEndRule.tz = 'Asia/Shanghai'; // 按需设置时区,避免服务器时区偏差 // 创建定时任务 schedule.scheduleJob(monthlyEndRule, async () => { try { // 1. 检查当月是否已完成归档(避免重复执行) const today = new Date(); const currentMonth = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`; const existingArchive = await MonthlyArchive.findOne({ archivedMonth: currentMonth }); if (existingArchive) { console.log(`当月(${currentMonth})已完成归档,跳过执行`); return; } // 2. 获取需要归档的当前数据 const currentRecords = await CurrentData.find({}); // 3. 转换数据格式并插入归档集合 const archivePromises = currentRecords.map(record => { return MonthlyArchive.create({ archivedMonth: currentMonth, originalData: record.toObject(), // 转换为普通对象存储 archivedAt: new Date() }); }); await Promise.all(archivePromises); console.log(`当月(${currentMonth})数据归档完成,共归档${currentRecords.length}条数据`); } catch (error) { console.error('月末归档任务执行失败:', error); // 这里可以添加告警逻辑(比如邮件、企业微信通知) } });
方案二:使用node-cron(基于Cron表达式)
1. 安装依赖
npm install node-cron # 或 yarn add node-cron
2. 编写定时任务逻辑
Cron表达式59 23 L * *代表每月最后一天的23:59执行任务:
const cron = require('node-cron'); const mongoose = require('mongoose'); const CurrentData = require('./models/CurrentData'); const MonthlyArchive = require('./models/MonthlyArchive'); // 定义Cron定时任务 cron.schedule('59 23 L * *', async () => { try { const today = new Date(); const currentMonth = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`; const existingArchive = await MonthlyArchive.findOne({ archivedMonth: currentMonth }); if (existingArchive) { console.log(`当月(${currentMonth})已归档,跳过`); return; } const currentRecords = await CurrentData.find({}); const archivePromises = currentRecords.map(record => { return MonthlyArchive.create({ archivedMonth: currentMonth, originalData: record.toObject(), archivedAt: new Date() }); }); await Promise.all(archivePromises); console.log(`当月(${currentMonth})归档完成,${currentRecords.length}条数据入库`); } catch (error) { console.error('归档任务出错:', error); } }, { timezone: 'Asia/Shanghai' // 指定时区 });
关键注意事项
- 进程守护:确保Node.js进程持续运行,生产环境建议用PM2、systemd等工具守护进程,避免进程意外退出导致定时任务失效。
- 幂等性:每次执行任务前先检查是否已完成当月归档,避免服务器重启或任务重复触发导致数据重复插入。
- 错误处理:必须捕获异步操作的异常,避免单个任务失败导致整个定时任务崩溃,必要时添加告警机制。
- 性能优化:如果数据量很大,不要一次性查询所有数据,建议用分页查询分批处理,避免内存溢出。
内容的提问来源于stack exchange,提问作者jee chang wook
相关产品推荐
相关产品推荐

