Java 读取大文件CSV,然后将数据存入redis中
前些天,项目需要用到从csv文件中读取数据,然后将数据存入redis中。我看了一下csv文件的大小,1G多。这个虽然算不上大文件,但是考虑到以后如果数据量增加可能到2G、3G、甚至更大,所以就不打算用普通读取文件的方式去读取这个文件。因为普通读取文件是将文件全部加载进内存,然后再内存中读取。这种情况下,文件比较小那还好说,如果文件大了,就会占用大量的内存。
出于这样的考虑,我决定用读取大文件的方式去读取这个CSV文件,代码如下
try (FileInputStream inputStream = new FileInputStream(path);
Scanner sc = new Scanner(inputStream)) {
LogUtils.info(df.format(new Date()) + "读取文件中...\n" + "文件名为:" + name);
/**
* 读取csv文件
* 因为文件过大,所以需要用scanner每行读取
* 用FileInputStream 相当于在内存和文件之见加了一个数据传输管道
* 然后用Scanner去读取这个流,一行一行的去读这个文件
*/
while (sc.hasNextLine()) {
String line = sc.nextLine();
//文件是CSV文件,CSV文件中的每一列是用","隔开的,这样就可以得到每一列的元素
String[] strArray = line.split(",");
for (String str:strArray) {
System.out.println(str);
}
}
} catch (Exception e) {
LogUtils.error(e.getMessage(), e);
e.printStackTrace();
}
}
这样就可以将大文件读取出来了,这时候就需要将数据存放进redis中,整体项目是用SpringBoot去搭建的,这里我用普通的key-value形式存了,如果有其他需求,改一下存储的数据结构就行了。当数据量特别大的时候,如果只是简简单单的用下面的代码的话浪费的时间和资源是非常大的,这时候我们就可以采取批量操作。用pipline的方式,每10000次提交一次,那么为什么是10000这个数字呢?因为我测试过,pipline是有一个极限值的东西。如果一次提交的次数太多的话,速度反而会降下来,经过我的测试发现10000是一个速度相对较快的数字。
redisTemplate.opsForValue(key,value);
回到题目,我们是读取CSV文件,然后将数据存入redis中,所以这个存入Redis的操作就在上面那个读取CSV文件的while循环中,这里只是简单地演示。
Map<String, byte[]> map = new HashMap<>();
while (sc.hasNextLine()) {
String line = sc.nextLine();
String[] strArray = line.split(",");
key=strArray[0];
value=strArray[1];
map.put(key,value);
if (i % 10000 == 0) {
System.out.println(df.format(new Date()));
//这个是我们用pipline提交的方法
loadRedisPipline(map);
//这里一定要记得清空这个map
map.clear();
}
@Autowired
private StringRedisTemplate redisTemplate;
public void loadRedisPipline(Map<String, byte[]> map) {
redisTemplate.executePipelined(new RedisCallback<Map<String, String>>() {
@Override
public Map<String, String> doInRedis(RedisConnection connection) throws DataAccessException {
connection.openPipeline();
//遍历这个map,然后存入key与value
for (String key : map.keySet()) {
connection.set(key.getBytes(),map.get(key));
}
return null;
}
});
}
这样我们就可以将大的CSV文件存入到redis中,而且速度相对来说是比较快的。当然我们可以再优化一下,用ProtoBuf将value序列化,然后存入redis中。不过我这里只是一个演示,就没有用PB去序列化