mysql层面去重:https://www.cnblogs.com/duanxiaojun/p/6855680.html
数据库层面具体使用哪个sql语句去重,根据业务情况来定。
1.Excel批量导入10w数据量,多用户同时导入,2个字段相同去重(不包括主键)
数据库连接池默认开启连接50,最大100
由于mybatis有一次sql的大小限制或者数据库也有大小限制,因此可以将其分为多个list集合,使用ExcutorService、callable、futuretask、countdownlatch(用于计算分段list集合个数),多线程并发插入数据。程序方面对Excel中的数据去重:将数据封装为对象,对象重写equals方法和hashCode方法,这里equals就只对那2个字段进行比较即可,并将对象放入set中去重。数据库方面去重:利用数据库设置联合唯一索引。然后通过insert ignore into语句去执行。insert ignore into:重复或语句错误报错都会被忽略(根据主键和唯一索引判断重复)
2.Excel批量导入10w数据量,多用户同时导入,除去主键其余字段相同去重
程序方面的去重与上一点大致相同。区别是equals和hashCode需要判断全部的属性字段。
以上也可以考虑用redis的zset去重,但是会增加网络延时问题,以及每次都要以网络形式分批去读取redis中的数据,并且反序列化,会增加一定的网络不及时响应等问题。如果程序没有考虑从缓存中读取数据,使用redis去重存储数据,是得不偿失的。如果本身系统查数据都是从redis中获取,那么使用redis的zset存储数据库的数据是可以的。
引入一个问题,如何知道redis上的哪些数据是没有被持久化到数据库中的呢?
经济允许,可以创建两个zset集合,一个zset集合(A)是缓存全部数据,一个zset集合(B)存需要插入到数据库的全部数据。那么多个用户并行上传数据后,将这些数据都存入A和B中,由于前端是从缓存A中获取数据,所以很快就能响应,然后后台异步操作对B中的数据多线程的存入数据库中,将持久化到数据库中的数据再从B中删除即可。这里就需要数据库层面sql再次对insert的数据进行重复控制,将插入到数据库与已存在数据库中的数据进行重复控制。(zset 是根据score参数来判定排序顺序,且存入的数据是不重复的,因此可以根据业务来确定score值,如果是根据创建时间排序,socre就可以存入创建时间字段的时间戳,zrange 命令从小到大排序,zrevrange 命令从大到小排序)
3.导入100w+数据
如果批量导入100w+的数据,存在的技术难点:
1)一次读取加载到内存会OOM;
2)调用接口保存一次传输数据量大,网络传输压力大;
3)一句SQL批量插入,对数据库压力大,如果同时操作该表,会造成死锁情况;
4)使用excel会造成大量的繁琐操作,由于数据不可磁盘分区操作,一次性读入会导致1)问题。
解决:
1)将文件以流的形式存入磁盘中,然后通过多线程去分区读取(使用FileChannel、RandomAccessFile实现);
2)根据分区读取的内容数量进行数据库连接的调用,因此不会造成网络传输压力大的问题;
3)分批插入数据到数据库,就不会造成数据库压力大;
4)提示用户将excel保存为csv格式,使用csv的优点在于,一条数据就是一行有利于多线程分区读取文件,不会造成一条数据被分成两部分。
用到文件内存映射。
需要注意的是,如果数据量小的话,不适宜这样做,应为初始化MappedByteBuffer会比较耗时间,因此可以根据文件大小,来判断使用哪种导入方式。