事情是这样子的~~
由于某种原因,简单来说就是导致需要从线上导出几张千万级别的数据表到开发服进行某种计算的性能测试,然后导入表的时候漏了一张,有权限的同事已经下班了,需要把这张表生成到开发服,表的内容不重要关键要有和用户表一一对应的记录。
所以,要想办法短时间根据一张已有的表生成另一张表。
总不能直接insert into ... select
直接一把梭哈吧,另外也试了下批量读取,然后批量插入,效果比较慢
找了一圈,发现mybatis有个流读取的功能,刚好合适
- dao层
直接来个获取所有用户的sql
@Select("select * from user ")
Cursor<User> selectAllUser();
- service
读取数据并且生成新表数据入库
@Service("tflowService")
public class FlowService {
@Autowired
UserDao userDao;
@Autowired
UserMapper userMapper;
@Autowired
UserVipMapper userVipMapper;
@Transactional
//测试使用
public void test(int limit) throws Exception{
//直接获取所有,内存不够直接GG了
//List<User> list = userMapper.selectAll();
//list.forEach(x-> System.out.println(x.getId()));
//流式读取
try (Cursor<User> cursor = userDao.selectAllUser()) {
cursor.forEach(foo -> System.out.println(foo.getId()));
}
}
@Transactional
//实际使用
public void test1() throws Exception{
try (Cursor<User> cursor = userDao.selectAllUser()) {
AtomicInteger a = new AtomicInteger();
List<UserVip> uvs = new ArrayList<>(10000);
Date date = new Date();
cursor.forEach(x -> {
UserVip uv = UserVip.builder().id(x.getId()).createTime(date).updateTime(date).status(Byte.valueOf("1")).build();
if (a.get() != 0 && a.get()%10000 == 0){
userVipMapper.insertList(uvs);
uvs.clear();
System.out.println("第" + a.get()/10000 + "批");
}
uvs.add(uv);
a.incrementAndGet();
});
if (!CollectionUtils.isEmpty(uvs)){
userVipMapper.insertList(uvs);
}
}
}
这里有两点可以稍加注意一下
try() 括号中有获取资源的语句,在运行结束后会自动释放
二是 @Transactional 的作用,它仅仅为了让数据库连接不会再获取第一条数据之后释放
其实这里try()的这个作用在这里不明显,不用也可以
我这里大概有600w条数据,花了两分多钟,对于我这个需求还是算能满足的了~~