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會比較耗時間,因此可以根據文件大小,來判斷使用哪種導入方式。