一 业务场景

公司年终要办一个抢购活动,抢购活动维护有一份名单(txt文件),只有名单中的用户可以参加抢购活动,所以需要把名单导入到内存数据库中,以便于检验用户是否有资格。

原先的设计分为两步,第一步先把文件导入到数据库中,而后第二步操作将数据库中的数据同步到redis中。

二 存在问题

当数据n百万的时候是能够满足需求的,但是当这个白名单数量到达n千万的时候,第二步基本就无法操作了,从数据库中同步到redis,即使分页每次都要一个全表扫描(因为没做索引,而且即使有索引也只能有限的提高),速度非常慢,要1-2小时。

三 解决方案

在第一步同步到数据库中时,需要对每一行的txt进行扫描,进行一些业务处理,然后存入白名单数据表,而这一步的时候完全就可以存入redis了,所以删除第二步,在入库的同时存入到redis即可。将时间减少了1-2小时

四 优化

后来细思下,数据为何要存到数据库中?存到数据库中是为了什么呢?难道是为了校验?1500W数据你要校验到什么时候,而且假设存到数据库丢数据了,哪又如何来校验呢?所以,最好的方法是直接用文件存储。

文件存储有个问题是还需要挂载另外一个文件盘,带来系统运维的复杂性。那么,干脆直接用源文件,直接做插入即可。

五 分块插入

  1. 首先将文件进行分块,将分块后的文件发送到各个微服务。文件上传时会直接读到内存,如果虚拟机的分配的内存不够,服务会崩溃掉,所以如果1500w文件直接上传,要么将java虚拟机空间设置的足够大,要么在前端就进行分片。
  2. 每个微服务再分线程进行处理。因为是高IO操作,所以线程池大小最好为CPU数量的2倍。
  3. 柔性判断,如果插入redis延迟过高,或者插入数据库线程过多,都会引起系统异常。这里可以用Guava的retry包来判断线程的返回值,如果返回值是延迟过高或者线程过多,均啥都不做,直接返回,否则进行redis或者数据库操作。

方老司
2k 声望224 粉丝

教育皆祸害,[链接]