Sequelize批量插入SQLite仅6k条,137k条API数据丢失求助
问题:137k条数据仅插入6k到SQLite,导出文本却完整
我通过多轮API调用获取了137k条数据,计划写入SQLite数据库,但最终仅成功插入约6k条记录;不过将数据导出到文本文件时,能得到完整的137k条内容。以下是我的数据库插入相关代码:
index.js
const db = require("./config/database"); const items = require("./models/items"); var fs = require('fs'); const baseURL = 'xxx'; const namespace = 'static-eu'; const region = 'eu'; const client = 'xxx'; const secret = 'xxx'; const tokenURL = 'xxx'; const getToken = async function(client, secret) { const body = "grant_type=client_credentials"; const response = await fetch(tokenURL, { method: 'post', body: body, headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + Buffer.from(client + ':' + secret).toString('base64')} }); const data = await response.json(); const jsondata = JSON.stringify(data); const json = JSON.parse(jsondata); return json['access_token']; } const getItems = async function(ID) { const access_token = await getToken(client, secret); const reqURL = baseURL + '/data/wow/search/item?namespace=' + namespace + '&_pageSize=1000&orderby=id&id=['+ ID + ',]&access_token=' + access_token; const response = await fetch(reqURL, { method: 'get', //headers: {'Authorization': 'Bearer ' + access_token} }); const data = await response.json(); const jsondata = JSON.stringify(data); const json = JSON.parse(jsondata); var arrayID = []; json.results.forEach(function (item, index){ try { items.upsert({ itemID: json.results[index].data.id, desc_de: json.results[index].data.name.de_DE, desc_en: json.results[index].data.name.en_US, media: json.results[index].data.id, item_class_de: json.results[index].data.item_class.name.de_DE, item_class_en: json.results[index].data.item_class.name.en_US, }); } catch (error) { return console.log(error.name + error.message); } arrayID.push(json.results[index].data.id); }); const nextID = (Math.max(...arrayID)) + 1; return nextID; } const getAll = async function() { let ID = 1; while(1==1) { console.log(ID); ID = await getItems(ID); }}; db.authenticate() .then(() => { console.log('Logged in to DB!'); items.init(db); items.sync(); }) .catch(err => console.log(err)); getAll();
items.js
const { DataTypes, Model } = require('sequelize'); module.exports = class items extends Model { static init(sequelize){ return super.init({ itemID: { type: DataTypes.INTEGER, allowNull: false, unique: true }, desc_de: DataTypes.STRING, desc_en: DataTypes.STRING, media: DataTypes.INTEGER, item_class_de: DataTypes.STRING, item_class_en: DataTypes.STRING, }, { tableName: 'items', timestamps: true, sequelize }); } }
database.js
const { Sequelize } = require('sequelize'); module.exports = new Sequelize('database', 'user', 'password', { host: 'localhost', dialect: 'sqlite', logging: false, storage: './data/items.sqlite', });
问题原因及解决方案
核心问题
- 异步操作未等待:
items.upsert()是异步Promise,但forEach循环不会等待它完成,导致大量插入操作被“丢弃”,还没执行就进入下一轮循环。 - 错误处理中断循环:
catch里的return会直接终止整个forEach,一旦某条数据插入出错,后续所有数据都不会处理。 - 表同步未完成就插入:
items.sync()是异步操作,还没创建好表就开始插入,导致部分插入失败。 - 重复请求Token:每次
getItems都调用getToken,频繁请求可能触发API限流,间接导致数据获取或插入中断。
解决方案
1. 替换forEach为for...of,等待每个插入完成
把json.results.forEach改成for...of循环,用await等待upsert完成,确保每条数据都被处理:
// 替换原forEach部分 var arrayID = []; for (const item of json.results) { try { await items.upsert({ itemID: item.data.id, desc_de: item.data.name.de_DE, desc_en: item.data.name.en_US, media: item.data.id, item_class_de: item.data.item_class.name.de_DE, item_class_en: item.data.item_class.name.en_US, }); arrayID.push(item.data.id); } catch (error) { console.error(`插入ID ${item.data.id}失败:`, error.message); // 去掉return,继续处理下一条数据 } }
2. 缓存Token,避免重复请求
Token一般有有效期,缓存起来不用每次请求都获取:
let cachedToken = null; let tokenExpiry = 0; // 过期时间戳 const getToken = async function(client, secret) { const now = Date.now(); // 如果缓存Token未过期,直接返回 if (cachedToken && now < tokenExpiry) { return cachedToken; } const body = "grant_type=client_credentials"; const response = await fetch(tokenURL, { method: 'post', body: body, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + Buffer.from(client + ':' + secret).toString('base64') } }); const data = await response.json(); cachedToken = data.access_token; // 用API返回的expires_in设置过期时间,默认3600秒 tokenExpiry = now + (data.expires_in || 3600) * 1000; return cachedToken; }
3. 等待表同步完成再启动插入
在db.authenticate()的回调里,等待items.sync()完成后再调用getAll():
db.authenticate() .then(async () => { console.log('已连接到数据库!'); items.init(db); await items.sync(); // 等待表创建/同步完成 await getAll(); // 再启动数据插入 }) .catch(err => console.log(err)); // 删掉原来单独调用的getAll()
4. 批量插入优化(可选,大幅提升效率)
对于大量数据,用bulkCreate配合updateOnDuplicate实现批量upsert,比单条插入快得多:
// 在getItems里,先收集所有数据,再批量插入 const itemsToUpsert = json.results.map(item => ({ itemID: item.data.id, desc_de: item.data.name.de_DE, desc_en: item.data.name.en_US, media: item.data.id, item_class_de: item.data.item_class.name.de_DE, item_class_en: item.data.item_class.name.en_US, })); try { await items.bulkCreate(itemsToUpsert, { updateOnDuplicate: ['desc_de', 'desc_en', 'media', 'item_class_de', 'item_class_en'] }); // 收集ID arrayID.push(...itemsToUpsert.map(item => item.itemID)); } catch (error) { console.error('批量插入失败:', error.message); }
内容的提问来源于stack exchange,提问作者Christoph Ritzer
相关产品推荐
相关产品推荐

