美文网首页开源框架-Mybatis系列
Mybatis-plus大数据量流式查询

Mybatis-plus大数据量流式查询

作者: 爪哇驿站 | 来源:发表于2020-10-27 16:52 被阅读0次
    一、在需要使用流式查询的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
     */
    @Mapper
    public 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);
    }
    
    二、使用示例
    @RestController
    public 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费劲心思要提供两种数据库连接模式,如果应用对数据库连接的消耗要求严苛,那么流式查询就不再适合。

    相关文章

      网友评论

        本文标题:Mybatis-plus大数据量流式查询

        本文链接:https://www.haomeiwen.com/subject/zawsuktx.html