国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

java使用jdbcTemplate查詢并插入百萬(wàn)級(jí)數(shù)據(jù)解決方案

這篇具有很好參考價(jià)值的文章主要介紹了java使用jdbcTemplate查詢并插入百萬(wàn)級(jí)數(shù)據(jù)解決方案。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

背景:使用JdbcTemplate查詢500萬(wàn)數(shù)據(jù),然后插入到數(shù)據(jù)庫(kù)。

這么多的數(shù)據(jù)按照普通的方式直接查詢?nèi)缓蟛迦?,服?wù)器肯定會(huì)掛掉,我嘗試過使用分頁(yè)查詢的方式去進(jìn)行分批查詢插入,雖然也能達(dá)到保證服務(wù)器不掛掉的效果,但是有一個(gè)嚴(yán)重的問題,每次查詢的數(shù)據(jù)很難保證順序性,第一次一查詢的數(shù)據(jù)可能又出現(xiàn)在第N次的查詢結(jié)果中,雖然可以通過在查詢sql中加上排序,可以保證多次查詢的順序不變,但是這種分頁(yè)查詢方式還是不夠嚴(yán)謹(jǐn),因?yàn)樵诙啻尾樵冞^程中,可能數(shù)據(jù)有新增或刪除,即使保證了排序唯一性,也會(huì)導(dǎo)致數(shù)據(jù)少取或取重復(fù)問題。

這個(gè)過程中需要解決的問題:

一、內(nèi)存溢出

使用jdbcTemplate.queryForList查詢一次讀取500萬(wàn)條數(shù)據(jù),會(huì)占用大量?jī)?nèi)存,一般的服務(wù)器都會(huì)內(nèi)存溢出報(bào)錯(cuò),jdbcTemplate默認(rèn)使用RowMapperResultSetExtractor來(lái)處理ResultSet結(jié)果集,會(huì)將數(shù)據(jù)全部讀取到內(nèi)存:

java查詢500萬(wàn)條數(shù)據(jù),java,開發(fā)語(yǔ)言

java查詢500萬(wàn)條數(shù)據(jù),java,開發(fā)語(yǔ)言

因此我們需要自己寫一個(gè)實(shí)現(xiàn)類繼承ResultSetExtractor,去實(shí)現(xiàn)讀取ResultSet的邏輯。

一、批量插入速度慢

我們使用jdbcTemplate的batchUpdate方法批量保存數(shù)據(jù)時(shí),要想真正進(jìn)行批量保存需要幾個(gè)條件

1.首先要數(shù)據(jù)庫(kù)本身要支持批量更新,一般主流數(shù)據(jù)庫(kù)都會(huì)支持。

2.插入的sql語(yǔ)句不要使用子查詢

插入語(yǔ)句只使用insert into table() values()這種,不要在values中使用select語(yǔ)句

3.數(shù)據(jù)源連接設(shè)置rewriteBatchedStatements=true這個(gè)參數(shù)

在oracle驅(qū)動(dòng)中rewriteBatchedStatements參數(shù)默認(rèn)是開啟的,mysql沒有開啟,需要在數(shù)據(jù)源url連接中手動(dòng)設(shè)置:

java查詢500萬(wàn)條數(shù)據(jù),java,開發(fā)語(yǔ)言

?自定義ResultSetExtractor如下:

package com.zhou.db.model;


import com.zhou.db.util.SqlUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.JdbcUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 查詢數(shù)據(jù)自定義處理ResultSet
 * @author lang.zhou
 * @since 2023/1/9 17:42
 */
@Slf4j
public abstract class DataMapCallBackExtractor implements ResultSetExtractor<List<Map<String,Object>>> {

    /**
     * 每次讀取10000條時(shí)開始插入
     */
    @Getter
    private int batchQuerySize = 1000;

    @Getter
    private List<DbColumn> columnList  = new ArrayList<>(0);

    /**
     * 數(shù)據(jù)條數(shù)
     */
    @Getter
    private int dataCount = 0;

    public DataMapCallBackExtractor() {
    }

    public DataMapCallBackExtractor(int batchQuerySize) {
        if(batchQuerySize > 1000){
            this.batchQuerySize = batchQuerySize;
        }
    }

    @Override
    public List<Map<String,Object>> extractData(ResultSet rs) throws SQLException, DataAccessException {
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        //結(jié)果集列數(shù)
        int count = resultSetMetaData.getColumnCount();
        //已經(jīng)執(zhí)行回調(diào)的次數(shù)
        int times = 0;
        //讀取列信息
        for (int i = 1; i < count + 1; i++) {
            columnList.add(SqlUtil.readResultColumn(resultSetMetaData,i));
        }
        //讀取列信息后回調(diào)
        this.columnInfoCallback(columnList);
        List<Map<String, Object>> list = new ArrayList<>();
        while(rs.next()){
            //總條數(shù)增加
            dataCount ++;
            Map<String, Object> e = new LinkedHashMap<>(count);
            //讀取這一行的數(shù)據(jù)
            for (int i = 1; i < count + 1; i++) {
                e.putIfAbsent(JdbcUtils.lookupColumnName(resultSetMetaData, i), JdbcUtils.getResultSetValue(rs, i));
            }
            list.add(e);
            //讀取滿10000條時(shí)開始插入數(shù)據(jù)
            if(list.size() >= batchQuerySize){
                times ++;

                this.dataCallback(list,times,dataCount);
                //處理完成清空已讀取的數(shù)據(jù),釋放內(nèi)存
                list.clear();
            }
        }
        //可能最后一次讀取不滿10000條,插入剩余的數(shù)據(jù)
        if(list.size() > 0){
            times ++;
            this.dataCallback(list,times,dataCount);
            list.clear();
        }
        return new ArrayList<>(0);
    }

    /**
     * 讀取batchQuerySize條數(shù)據(jù)后自定義處理回調(diào)
     */
    public abstract void dataCallback(List<Map<String, Object>> list, int times, int n);

    /**
     * 讀取列信息后回調(diào)
     */
    public void columnInfoCallback(List<DbColumn> columnList){

    }

}

?我們拿到ResultSet后,每次只讀取10000條數(shù)據(jù)存到List中,然后將這些數(shù)據(jù)插入數(shù)據(jù)庫(kù),在插入結(jié)束之后清空這個(gè)List,jvm會(huì)回收這些數(shù)據(jù)釋放內(nèi)存,一直重復(fù)這個(gè)過程直到結(jié)果集讀取完畢,這樣能保證內(nèi)存中只流程10000條數(shù)據(jù),就避免了內(nèi)存泄漏的情況產(chǎn)生。

分批插入代碼,提升插入速度:

    /**
     * 數(shù)據(jù)分批插入
     */
    public void batchSizeUpdate(List<Map<String,Object>> list, String sql,NamedParameterJdbcTemplate namedParameterJdbcTemplate, int batchSize){
        int size = list.size();
        int n = size / batchSize;
        int l = size % batchSize;
        if(l > 0){
            n++;
        }
        log.info("總共分"+n+"次插入");
        for (int i = 0; i < n; i++) {
            int start = i*batchSize;
            int end = (i+1)*batchSize;
            if(end > size){
                end = size;
            }
            batchUpdate(list.subList(start,end),sql, namedParameterJdbcTemplate);
            log.info("第"+(i+1)+"次插入完畢");
        }
    }

    private void batchUpdate(List<Map<String,Object>> list, String sql,NamedParameterJdbcTemplate namedParameterJdbcTemplate){
        Map<String,?> [] param = new Map[list.size()];
        for(int c= 0;c<list.size();c++){
            param[c] = list.get(c);
        }
        namedParameterJdbcTemplate.batchUpdate(sql,param);
    }

?最終調(diào)用方式:

    //一次讀取10000條后進(jìn)行回調(diào)
    DataMapCallBackExtractor extractor = new DataMapCallBackExtractor(10000){
        @Override
        public void dataCallback(List<Map<String, Object>> list, int times, int n) {
            log.info("第{}次讀取{}條,共{}條",times,list.size(),n);
            //分批插入,一次1000條
            batchSizeUpdate(list,insertSql,insertJdbcTemplate);
        }

        @Override
        public void columnInfoCallback(List<DbColumn> columnList) {
            //讀取結(jié)果集之前回調(diào),拿到列信息進(jìn)行一些處理
            //比如拼接插入sql
        }
    };
    jdbcTemplate.query(sql, new HashMap<>(0),extractor);

?SqlUtil中讀取列信息的代碼:

    /**
     * 從ResultSet中讀取sql列信息
     * @param rs    結(jié)果集
     * @param i     列位置
     */
    @SneakyThrows
    public static DbColumn readResultColumn(ResultSetMetaData rs,int i){
        DbColumn c = new DbColumn();
        c.setName(rs.getColumnName(i));
        c.setComments(rs.getColumnLabel(i));
        int type = rs.getColumnType(i);
        c.setDataType(rs.getColumnTypeName(i));

        c.setNullable(rs.isNullable(i) == ResultSetMetaData.columnNoNulls ? "N" : "Y");
        if(type == Types.VARCHAR || type == Types.CHAR || type == Types.LONGVARCHAR || type == Types.CLOB){
            c.setDataLength(rs.getColumnDisplaySize(i));
        }else if(type == Types.NUMERIC || type == Types.INTEGER || type == Types.BIGINT || type == Types.DECIMAL
                || type == Types.DOUBLE || type == Types.FLOAT || type == Types.REAL || type == Types.SMALLINT || type == Types.TINYINT){
            c.setDataLength(rs.getPrecision(i));
        }else if(type == Types.DATE || type == Types.TIMESTAMP){
            c.setDataLength(rs.getPrecision(i));
        }
        c.setDataScale(rs.getScale(i));
        return c;
    }

字段列信息實(shí)體:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-768619.html


import lombok.Data;

import java.util.Objects;

/**
 * 數(shù)據(jù)庫(kù)表字段基本信息
 * @author lang.zhou
 * @since 2022/10/17 14:31
 */
@Data
public class DbColumn {

    private String tableName;
    /**
     * 字段名
     */
    private String name;
    /**
     * 字段描述
     */
    private String comments;
    /**
     * 可為空
     */
    private String nullable = "Y";
    private String dataType = null;
    private Integer isPk = 0;
    private Integer dataLength = 0;
    private Integer dataScale = 0;
    public boolean isPk(){
        return name != null && isPk > 0;
    }
    public boolean isDate(){
        return name != null && ("DATE".equalsIgnoreCase(dataType) || "TIMESTAMP".equalsIgnoreCase(dataType) || "DATETIME".equalsIgnoreCase(dataType));
    }
    public boolean isNumber(){
        return name != null && ("NUMBER".equalsIgnoreCase(dataType) || "DECIMAL".equalsIgnoreCase(dataType) || "INTEGER".equalsIgnoreCase(dataType)
                || "INT".equalsIgnoreCase(dataType)|| "BIGINT".equalsIgnoreCase(dataType)|| "DOUBLE".equalsIgnoreCase(dataType)|| "LONG".equalsIgnoreCase(dataType)));
    }
    public boolean isChar(){
        return name != null && "CHAR".equalsIgnoreCase(dataType);
    }
    public boolean allowNull(){
        return !isPk() && Objects.equals(nullable,"Y");
    }
}

到了這里,關(guān)于java使用jdbcTemplate查詢并插入百萬(wàn)級(jí)數(shù)據(jù)解決方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • QT 實(shí)現(xiàn)百萬(wàn)級(jí)的數(shù)據(jù)顯示內(nèi)存消耗幾十兆

    QT 實(shí)現(xiàn)百萬(wàn)級(jí)的數(shù)據(jù)顯示內(nèi)存消耗幾十兆

    用QT 開發(fā)了一個(gè)上位機(jī)的工具用來(lái)解析串口的數(shù)據(jù),數(shù)據(jù)量比較大 ,如果QT tableview 控件完全顯示,內(nèi)存消耗較大,所以解析結(jié)果先建立sql 數(shù)據(jù)索引,然后通過垂直滾動(dòng)條的變化動(dòng)態(tài)地獲取數(shù)據(jù),每次從數(shù)據(jù)庫(kù)中提取50條,測(cè)試下來(lái)內(nèi)存消耗較小,可以實(shí)現(xiàn)百萬(wàn)或者千萬(wàn)級(jí)的

    2024年02月11日
    瀏覽(26)
  • E往無(wú)前 | 海量數(shù)據(jù)ES 擴(kuò)展難?騰訊云大數(shù)據(jù)ES 擴(kuò)展百萬(wàn)級(jí)分片也“So Easy~”

    E往無(wú)前 | 海量數(shù)據(jù)ES 擴(kuò)展難?騰訊云大數(shù)據(jù)ES 擴(kuò)展百萬(wàn)級(jí)分片也“So Easy~”

    《E往無(wú)前》系列將著重展現(xiàn)騰訊云ES在持續(xù)深入優(yōu)化客戶所關(guān)心的「??!快!穩(wěn)!」訴求,能夠在低成本的同時(shí)兼顧高可用、高性能、高穩(wěn)定等特性,可以滿足微盟、小紅書、微信支付等內(nèi)外部大客戶的核心場(chǎng)景需求。 E往無(wú)前?|?海量數(shù)據(jù)ES擴(kuò)展難?騰訊云ES 擴(kuò)展百萬(wàn)級(jí)分片

    2024年02月06日
    瀏覽(24)
  • CTO:給我一個(gè)SpringBoot實(shí)現(xiàn)MySQL百萬(wàn)級(jí)數(shù)據(jù)量導(dǎo)出并避免OOM的解決方案

    CTO:給我一個(gè)SpringBoot實(shí)現(xiàn)MySQL百萬(wàn)級(jí)數(shù)據(jù)量導(dǎo)出并避免OOM的解決方案

    動(dòng)態(tài)數(shù)據(jù)導(dǎo)出是一般項(xiàng)目都會(huì)涉及到的功能。它的基本實(shí)現(xiàn)邏輯就是從mysql查詢數(shù)據(jù),加載到內(nèi)存,然后從內(nèi)存創(chuàng)建excel或者csv,以流的形式響應(yīng)給前端。 參考:https://grokonez.com/spring-framework/spring-boot/excel-file-download-from-springboot-restapi-apache-poi-mysql。 SpringBoot下載excel基本都是這

    2023年04月13日
    瀏覽(21)
  • MySQL 百萬(wàn)級(jí)/千萬(wàn)級(jí)表 全量更新

    業(yè)務(wù)需求:今天從生成測(cè)試環(huán)境遷移了一批百萬(wàn)級(jí)/千萬(wàn)級(jí)表的數(shù)據(jù),領(lǐng)導(dǎo)要求將這批數(shù)據(jù)進(jìn)行脫敏處理(將真實(shí)姓名 、電話、郵箱、身份證號(hào)等敏感信息進(jìn)行替換)。遷移數(shù)據(jù)記錄數(shù)如下(小于百萬(wàn)級(jí)的全量更新不是本文重點(diǎn)): 表名 表名含義 行記錄數(shù) base_house 房屋表 42

    2024年02月05日
    瀏覽(20)
  • TCP服務(wù)器的演變過程:使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型實(shí)現(xiàn)百萬(wàn)級(jí)并發(fā)(詳細(xì)代碼)

    TCP服務(wù)器的演變過程:使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型實(shí)現(xiàn)百萬(wàn)級(jí)并發(fā)(詳細(xì)代碼)

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗(yàn)開局一塊磚,大廈全靠壘。 為了避免篇幅過長(zhǎng)使讀者感到乏味,對(duì)【TCP服務(wù)器的開發(fā)】進(jìn)行分階段實(shí)現(xiàn),一步步進(jìn)行優(yōu)化升級(jí)。 本節(jié),在上一章節(jié)介紹了如何使用epoll開發(fā)高效的服務(wù)器,本節(jié)將介紹使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型,實(shí)

    2024年02月01日
    瀏覽(30)
  • SpringBoot高效批量插入百萬(wàn)數(shù)據(jù)

    SpringBoot高效批量插入百萬(wàn)數(shù)據(jù) 前言:我相信很多小伙伴和我一樣在初學(xué)的時(shí)候,面對(duì)幾萬(wàn)幾十萬(wàn)數(shù)據(jù)插入,不知道如何下手,面對(duì)百萬(wàn)級(jí)別數(shù)據(jù)時(shí)更是不知所措,我們大部分初學(xué)者,很多人都喜歡for循環(huán)插入數(shù)據(jù),或者是開啟多個(gè)線程,然后分批使用for循環(huán)插入,當(dāng)我們需

    2024年04月18日
    瀏覽(21)
  • 美團(tuán)面試:Kafka如何處理百萬(wàn)級(jí)消息隊(duì)列?

    在今天的大數(shù)據(jù)時(shí)代,處理海量數(shù)據(jù)已成為各行各業(yè)的標(biāo)配。特別是在消息隊(duì)列領(lǐng)域,Apache Kafka 作為一個(gè)分布式流處理平臺(tái),因其高吞吐量、可擴(kuò)展性、容錯(cuò)性以及低延遲的特性而廣受歡迎。但當(dāng)面對(duì)真正的百萬(wàn)級(jí)甚至更高量級(jí)的消息處理時(shí),如何有效地利用 Kafka,確保數(shù)據(jù)

    2024年02月20日
    瀏覽(18)
  • 同屏實(shí)時(shí)渲染百萬(wàn)級(jí)獨(dú)立的3D可渲染對(duì)象

    同屏實(shí)時(shí)渲染百萬(wàn)級(jí)獨(dú)立的3D可渲染對(duì)象

    大規(guī)模渲染在游戲、家裝、或者其他生產(chǎn)制造相關(guān)的環(huán)境下有直接的剛需,能獨(dú)立渲染的3D對(duì)象越多,越容易實(shí)現(xiàn)復(fù)雜的場(chǎng)景需求。 下圖是WebGPU版200多萬(wàn)(2 * 1024 * 1024)個(gè)可渲染的3D對(duì)象,的實(shí)時(shí)渲染情況截圖。

    2024年02月08日
    瀏覽(19)
  • 窄帶高清技術(shù)之百萬(wàn)級(jí)并發(fā)下的演唱會(huì)直播細(xì)節(jié)修復(fù)

    史無(wú)前例,高清又不卡。 5月,百視TV聯(lián)合上海人民廣播電臺(tái)、時(shí)代峰峻共同出品的《東方風(fēng)云榜》,絢爛呈現(xiàn)一場(chǎng)三十周年音樂分享會(huì)·時(shí)代少年團(tuán)《理想之途》。有人說,這是一場(chǎng)似夢(mèng)非夢(mèng)的記憶。 演唱會(huì)由“樂園”、“少年”、“烏托邦”三大篇章組成,精心之作引來(lái)社

    2024年02月07日
    瀏覽(18)
  • 萬(wàn)級(jí)數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝

    萬(wàn)級(jí)數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝

    時(shí)間 更新內(nèi)容 2023/09/23 fix: 每個(gè)sheet大小和存儲(chǔ)內(nèi)存條數(shù)一致的bug update: 增大一個(gè)sheet的默認(rèn)容量 我們不妨先給大家講一個(gè)概念,利用此概念我們正好給大家介紹一個(gè)數(shù)據(jù)庫(kù)優(yōu)化的小技巧: 需求如下:將一個(gè)地市表的數(shù)據(jù)導(dǎo)出70萬(wàn)條。 如果你不假思索,直接一條sql語(yǔ)句搞上去

    2024年02月11日
    瀏覽(48)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包