以前处理大量数据时总是使用如仩方法处理速度相当慢。今天经理教了个新招如下:
这样执行的就是批操作。上传100000数据比我之前方法快了nnnnnnnnnnnn倍得把之前写的都改掉。
总结: 测试数据8万多条响应时间3汾多钟。
</bean> 负责读取数据 在程序执行时一次性抓取全部数据后在批量的交给LedgerWriter进行写操作。当然也可以使用分页读取JdbcPagingItemReader但要分页数量与写入數量要大写相同,还可以对分页出来的数据进行添加悲观锁
本文主要研究一下在对jdbc进行大数據量读写相关异常的防御措施
一次性select大量的数据到内存最容易出现的是OOM的异常,这个时候可以从时间和数据大小两个维度进行限制
对于普通的功能分页操作是必须的,也是解决这个问题最简单的方法在相关功能实现的时候,要对生产的数据量进行提前预估确定好相應的分页数据量。
这个参数如果要对不同的sql来做通用设置可能不是太好设置,稍微有点野蛮和暴力可能某些某些查询出来的数据的列數不多也占用不了太多内存。需要单独设置但是现在实际功能实现上很少直接使用jdbc,而是使用jpa或mybatis因此具体就需要看jpa或mybatis有没有暴露这个參数值给你设置。但是对于通用的sql服务来说非常有必要设置下maxRows,比如不超过2w等来进行兜底的防范。
使用fetchSize来避免OOM的话有个限制条件就昰需要自己在遍历resultSet的过程中边遍历数据,边处理数据如果不是边遍历边处理,还是把结果集循环添加到list中返回在不是reactive模式的编程范式丅,这个fetchSize也就失去效果了因为最后你还是在内存中堆积所有的数据集再去处理,因此终究会有OOM的风险
限制时间的话有多个维度:
这个昰jdbc中最底层的连接socket的timeout参数设定,可以用来防止java与jdbc数据库程序设计由于网络原因或自身问题重启导致连接阻塞这个是非常有必要设置的,┅般是在连接url中设置
比如pgpg的单位与mysql不同,mysql是毫秒而pg是秒
但是现在一般使用的是java与jdbc数据库程序设计连接池,因此这个不设置通过设置連接池相关参数也是可以。
这个主要是设置statement的executeQuery的执行超时时间即从client端发出查询指令到接收到第一批数据的超时时间,通常是通过timer来实现嘚
但是这个在不同的java与jdbc数据库程序设计的jdbc driver的实现上有所不同,比如在fetch模式下mysql的executeQuery不会获取第一批数据而pg则会顺带拉取第一批数据再返回。这个参数只有在不是fetch模式下即一次性查询所有数据,才相对符合语义如果是fetch模式,该超时时间限制不了后续几批数据的拉取超时怹们只能取决于connection的socketTimeout参数。
在现实的编程中实现某个业务功能可能在一个事务中调用了很多个statement的查询transaction可以以事务为单位来限制这批操作的超时间。
可以设置全局的超时时间
也可以在transactional注解中单独设置比如
在使用连接池来进行java与jdbc数据库程序设计操作的时候,一般的连接池都会提供连接检测的功能比如在borrow的时候验证下连接是否是ok的
另外还提供对连接占用的超时suspect和abandon操作,来检测连接泄露如果上面那些操作都没囿设置或(默认)设置的值太大不合理,那么这个检测就是除了socketTimeout外的兜底操作了如果连接被借出超过指定时间未归还,则判定为连接泄露則会强制abandon,即close掉连接非常暴力,但也非常有用防止线程阻塞在java与jdbc数据库程序设计操作最后导致服务504或502
类似fetchSize,对于大量数据的插入或更噺操作jdbc提供了batch方法,用来批量操作因此对于大规模的数据操作时要注意内存中堆积的数据量,记得分批释放调用比较适合使用原生嘚jdbc来操作,jpa的save方法还是现在内存中对接了大量对象在flush的时候才执行批量和释放。
对于jdbc的大量数据读写操作要额外注意内存中对象的堆積,防止OOM另外对于java与jdbc数据库程序设计操作的超时时间也要额外注意设置,防止服务器线程阻塞导致无法提供服务
限制一次或分fetch查询的所有数据量上限 |
底层socket连接的读超时 |
限制事务执行的超时时间 |