阅读 391

Mybatis-plus大数据量流式查询

一、在需要使用流式查询的mapper文件中,定义流式查询方法

package com.unionpay.dao.db2;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.unionpay.entity.TblMallOrder;import org.apache.ibatis.annotations.*;import org.apache.ibatis.mapping.ResultSetType;import org.apache.ibatis.session.ResultHandler;/**
 * (TblMallOrder)表数据库访问层
 *
 * @author liudong
 * @since 2020-09-15 17:07:13
 */@Mapperpublic interface TblMallOrderDao extends BaseMapper<TblMallOrder> {
    @Select("${sql}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    @ResultType(TblMallOrder.class)
    void dynamicSelectLargeData1(@Param("sql") String sql, ResultHandler<TblMallOrder> handler);

    @Select("${sql}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    @ResultType(Map.class)
    void dynamicSelectLargeData2(@Param("sql") String sql, ResultHandler<Map> handler);}
二、使用示例

@RestControllerpublic class TestSearchLargeData {
    // 这是每批处理的大小
    private final static int BATCH_SIZE = 1000;
    private int size;
    // 存储每批数据的临时容器
    private List<TblMallOrder> mallOrders;

    @Autowired
    private TblMallOrderDao tblMallOrderDao;

    @GetMapping("/getLargeData1")
    public void getLargeData1() {
        String sql = "select * from t_mall_order";
        tblMallOrderDao.dynamicSelectLargeData1(sql, new ResultHandler<TblMallOrder>() {
            @Override
            public void handleResult(ResultContext<? extends TblMallOrder> resultContext) {
                TblMallOrder tblMallOrder = resultContext.getResultObject();
                System.out.println(tblMallOrder);
            }
        });
    }

    @GetMapping("/getLargeData2")
    public void getLargeData2() {
        String sql = "select * from t_mall_order";
        tblMallOrderDao.dynamicSelectLargeData1(sql, new ResultHandler<TblMallOrder>() {
            @Override
            public void handleResult(ResultContext<? extends TblMallOrder> resultContext) {
                TblMallOrder tblMallOrder = resultContext.getResultObject();
                System.out.println(tblMallOrder);
                // 你可以看自己的项目需要分批进行处理或者单个处理,这里以分批处理为例
                mallOrders.add(tblMallOrder);
                size++;
                if (size == BATCH_SIZE) {
                    handle();
                }
            }
        });
        //用来完成最后一批数据处理
        handle();
    }
    /**
     * 数据处理
     */
    private void handle(){
        try{
            // 在这里可以对你获取到的批量结果数据进行需要的业务处理
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 处理完每批数据后后将临时清空
            size = 0;
            mallOrders.clear();
        }
    }}
三、总结
  1. Oracle和DB2,当我们执行一个SQL查询语句的时候,需要在客户端和服务器端都打开一个游标,并且分别申请一块内存空间,作为存放查询的数据的一个缓冲区。这块内存区,存放多少条数据就由fetchsize来决定,同时每次网络包会传送fetchsize条记录到客户端。应该很容易理解,如果fetchsize设置为20,当我们从服务器端查询数据往客户端传送时,每次可以传送20条数据,但是两端分别需要20条数据的内存空闲来保存这些数据。fetchsize决定了每批次可以传输的记录条数,但同时,也决定了内存的大小。这块内存,在oracle服务器端是动态分配的。而在客户端,PS对象会存在一个缓冲中(LRU链表),也就是说,这块内存是事先配好的,应用端内存的分配在conn.prepareStatement(sql)或都conn.CreateStatement(sql)的时候完成。

  2. MySQL本身并没有FetchSize方法, 它是使用CS阻塞的方式,通过网络流控制服务端,MySQL在执行ResultSet.next()方法时,会通过数据库连接一条一条的返回。MySQL按照自己的节奏不断的把buffer写回网络中。flush buffer的过程是阻塞式的,也就是说如果网络中发生了拥塞,send buffer被填满,会导致buffer一直flush不出去,那MySQL的处理线程会阻塞,从而避免数据把客户端内存撑爆。这样带来的问题:如果使用了流式查询,一个MySQL数据库连接同一时间只能为一个ResultSet对象服务,并且如果该ResultSet对象没有关闭,势必会影响其他查询对数据库连接的使用!此为大坑,所以sharding-sphere费劲心思要提供两种数据库连接模式,如果应用对数据库连接的消耗要求严苛,那么流式查询就不再适合。

7人点赞

MybatisPlus专题



作者:会飞的鱼_ld
链接:https://www.jianshu.com/p/c539b6d3ed90


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐