你好,java 向数据库添加大量数据时内存溢出 在不改变内存的情况下如何解决? 你当时是怎么解决的

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你好,java 向数据库添加大量数据时内存溢出 在不改变内存的情况下如何解决? 你当时是怎么解决的相关的知识,希望对你有一定的参考价值。

比如想将一个1000W数据的数据库表,导出到文件;此时,你要么进行分页,oracle当然用三层包装即可,mysql用limit,不过分页每次都会新的查询,而且随着翻页,会越来越慢,其实我们想拿到一个句柄,然后向下游动,编译一部分数据(如10000行)将写文件一次(写文件细节不多说了,这个是最基本的),需要注意的时候每次buffer的数据,在用outputstream写入的时候,最好flush一下,将缓冲区清空下;接下来,执行一个没有where条件的SQL,会不会将内存撑爆?是的,这个问题我们值得去思考下,通过API发现可以对SQL进行一些操作,例如,通过:PreparedStatement statement = connection.prepareStatement(sql),这是默认得到的预编译,还可以通过设置:PreparedStatement statement = connection.prepareStatement(sql , ResultSet.TYPE_FORWARD_ONLY , ResultSet.CONCUR_READ_ONLY);
来设置游标的方式,以至于游标不是将数据直接cache到本地内存,然后通过设置statement.setFetchSize(200);设置游标每次遍历的大小;OK,这个其实我用过,oracle用了和没用没区别,因为oracle的jdbc API默认就是不会将数据cache到java的内存中的,而mysql里头设置根本无效,我上面说了一堆废话,呵呵,我只是想说,java提供的标准API也未必有效,很多时候要看厂商的实现机制,还有这个设置是很多网上说有效的,但是这纯属抄袭;对于oracle上面说了不用关心,他本身就不是cache到内存,所以java内存不会导致什么问题,如果是mysql,首先必须使用5以上的版本,然后在连接参数上加上useCursorFetch=true这个参数,至于游标大小可以通过连接参数上加上:defaultFetchSize=1000来设置,例如:
jdbc:mysql://xxx.xxx.xxx.xxx:3306/abc?zeroDateTimeBehavior=convertToNull&useCursorFetch=true&defaultFetchSize=1000
上次被这个问题纠结了很久(mysql的数据老导致程序内存膨胀,并行2个直接系统就宕了),还去看了很多源码才发现奇迹竟然在这里,最后经过mysql文档的确认,然后进行测试,并行多个,而且数据量都是500W以上的,都不会导致内存膨胀,GC一切正常,这个问题终于完结了。

参考资料:xieyuoo_blog

参考技术A 2万条数据加载到内存中会出现内存溢出?有点假啊。。。你先试一下不插数据库会不会溢出,如果没有,解决办法:批量插入数据库,2000条一批吧,应该没问题。我当时20多万数据,耗时不到2秒。 参考技术B 分批次添加啊...你读取表的时候 是一条条读取的吧 这里应该用到循环了
你在循环1000次的时候或者100次的时候 提交一次
参考技术C 20,000个数据将被加载到内存中的内存溢出?有点假啊。 。 。尽量不要插在数据库中会溢出,如果没有量的解决方案:插入到数据库中,2000年的一批应该是没有问题的。我是20多万数据,消耗小于2秒。 参考技术D 批量插入,当累积到一定量的数据时先刷新到数据库,然后再循环

POI大量数据读取内存溢出分析及解决方案

点击关注公众号,实用技术文章及时了解

来源:blog.csdn.net/liangjf85/article/

details/84914798

在使用POI进行excel操作时,当数据量较大时经常会产生内存溢出异常。下面我们通过分析如何解决该问题

一、POI结构图

二、内存溢出问题

在项目中遇到二十万行数据要写入到excel中时会内存溢出,一般方法是调大tomcat的内存,但是调到2048M还是会内存溢出报错。因此我们分析其原因。

我们通过分析其源码,得出其实现步骤为通过InputStream一行行读取到TreeMap类型的HSSFRow结构体中,因此当数据量大时就会造成内存溢出。

public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes)
            throws IOException
    
        super(directory);
        String workbookName = getWorkbookDirEntryName(directory);
 
        this.preserveNodes = preserveNodes;
 
        // If we're not preserving nodes, don't track the
        //  POIFS any more
        if(! preserveNodes) 
            clearDirectory();
        
 
        _sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
        names  = new ArrayList<HSSFName>(INITIAL_CAPACITY);
 
        // Grab the data from the workbook stream, however
        //  it happens to be spelled.
        InputStream stream = directory.createDocumentInputStream(workbookName);
 
        List<Record> records = RecordFactory.createRecords(stream);
 
        workbook = InternalWorkbook.createWorkbook(records);
        setPropertiesFromWorkbook(workbook);
        int recOffset = workbook.getNumRecords();
 
        // convert all LabelRecord records to LabelSSTRecord
        convertLabelRecords(records, recOffset);
        RecordStream rs = new RecordStream(records, recOffset);
        while (rs.hasNext()) 
            try 
                InternalSheet sheet = InternalSheet.createSheet(rs);
                _sheets.add(new HSSFSheet(this, sheet));
             catch (UnsupportedBOFType eb) 
                // Hopefully there's a supported one after this!
                log.log(POILogger.WARN, "Unsupported BOF found of type " + eb.getType());
            
        
 
        for (int i = 0 ; i < workbook.getNumNames() ; ++i)
            NameRecord nameRecord = workbook.getNameRecord(i);
            HSSFName name = new HSSFName(this, nameRecord, workbook.getNameCommentRecord(nameRecord));
            names.add(name);
        
    
/**
     * add a row to the sheet
     *
     * @param addLow whether to add the row to the low level model - false if its already there
     */
 
    private void addRow(HSSFRow row, boolean addLow) 
        _rows.put(Integer.valueOf(row.getRowNum()), row);
        if (addLow) 
            _sheet.addRow(row.getRowRecord());
        
        boolean firstRow = _rows.size() == 1;
        if (row.getRowNum() > getLastRowNum() || firstRow) 
            _lastrow = row.getRowNum();
        
        if (row.getRowNum() < getFirstRowNum() || firstRow) 
            _firstrow = row.getRowNum();
        
    

excel数据行读取到内存的存储结构如下:

三、解决方案

poi官网给了一种大批量数据写入的方法,使用SXXFWorkbook类进行大批量写入操作解决了这个问题,可以监控该样例,我们会发现整体内存呈现锯齿状,能够及时回收,内存相对比较平稳。

package org.bird.poi;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
 
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.junit.Assert;
 
public class XSSFWriter 
 
 private static SXSSFWorkbook wb;
 
 public static void main(String[] args) throws IOException 
        wb = new SXSSFWorkbook(10000);
        Sheet sh = wb.createSheet();  
        for(int rownum = 0; rownum < 100000; rownum++)  
            Row row = sh.createRow(rownum);  
            for(int cellnum = 0; cellnum < 10; cellnum++)  
                Cell cell = row.createCell(cellnum);  
                String address = new CellReference(cell).formatAsString();  
                cell.setCellValue(address);  
              
  
          
  
        // Rows with rownum < 900 are flushed and not accessible  
        for(int rownum = 0; rownum < 90000; rownum++)  
          Assert.assertNull(sh.getRow(rownum));  
          
  
        // ther last 100 rows are still in memory  
        for(int rownum = 90000; rownum < 100000; rownum++)  
            Assert.assertNotNull(sh.getRow(rownum));  
          
        URL url = XSSFWriter.class.getClassLoader().getResource("");
          
        FileOutputStream out = new FileOutputStream(url.getPath() + File.separator + "wirter.xlsx");  
        wb.write(out);  
        out.close();  
  
        // dispose of temporary files backing this workbook on disk  
        wb.dispose();  
 

推荐

主流Java进阶技术(学习资料分享)

Java面试题宝典

加入Spring技术开发社区

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

以上是关于你好,java 向数据库添加大量数据时内存溢出 在不改变内存的情况下如何解决? 你当时是怎么解决的的主要内容,如果未能解决你的问题,请参考以下文章

Java实现Excel文件通过XSSF和SAX方式读取大量数据避免内存溢出的方法

Java前提下, MySQL数据库,一次性存储大量数据导致内存溢出

java内存区域/内存溢出汇总

POI大量数据读取内存溢出分析及解决方案

POI大量数据读取内存溢出分析及解决方案

java程序内存溢出一般啥原因