nodejs v12.13.1
redis 3.20.1000 windows版(不要鄙视我)
mysql 8.0
在npm上安装redis,cron定时任务模块,mysql模块.
主要针对物联网系统,带的设备数量比较多,写入操作居多,读取操作并不是很多.
与设备通讯时会产生大量的写入操作,如果直接用mysql单条写入,效率极其低下,node的性能根本发挥不出来,使用redis做缓存,定时任务从缓存读取数据,并单次批量存入数据库,减少数据库io,效率和稳定性能够大幅提升.
redis使用list类型保存缓存数据,随便写个get接口,收到请求时只要redis保存了,就可以返回结果.
let que = (req,res,next)=> {
//适合存库的数据体
let inObj = ['id','2020-01-06',222,'fwe2242df32edg']
client.lpush('imgList',JSON.stringify(inObj),redis.print)
res.send('xxx')
}
使用cron模块启动一个10秒执行一次的定时任务,具体间隔根据业务自测.
如果有请求发生,接口压入list中的数据就会在这里获取到,如果list中有数据,就会取出所有数据,转换成可以批量存入mysql的二维数组,最核心的代码就是那三个叹号的注释,测了一天,放在这里数据才不会丢失.
如果放在lrange里面,高并发时总会丢几条数据,经测试redis写入操作并没有丢,mysql批量存储也不会丢,那就是lrange的异步操作内使用del时,可能又压入了几条数据,读取到的items和del的数据不一致导致.放到紧挨着lrange后面就不会发生这种情况了.
但是redis也是单线程模型,这样写总感觉会读取不到数据,但事实是这样写很稳定,可能是lrange执行会极其短暂的阻塞进程,也没有找到相关的说明,忘指点…
const dataInJob = new cronJob('*/10 * * * * *', ()=> {
console.log('定时任务启动')
client.lrange('imgList', 0, -1, (err, items)=> {
if (err) throw err
if (items.length) {
let arr = []
for (let i = 0; i < items.length; i++) {
arr.push(JSON.parse(items[i]))
}
//saveFile(arr)
saveDb(arr)
}
})
//一定要写在这里,不然会丢数据!!!
client.del('imgList')
}, null, false, 'Asia/Shanghai')
async function saveDb(list) {
let sql = 'INSERT INTO meter_data (module_num,DATE,VALUE,img_url) VALUES ?;'
await db(sql,[list])
}
最后使用ab压测工具
每次1000个并发,共计10000个,反复测试稳定运行.
mysql稳稳的保存住10000条数据
也可以当作消息队列一条一条顺序慢慢处理,但那样不方便批量写入数据,执行效率并不是很高.具体可以搜索mysql单条写入和批量写入的对比文章.
代码只是测试,没有错误处理,生产中使用仍需完善.