IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 开发技巧2:学会通过while、do-while或for循环进行业务分批处理 -> 正文阅读

[大数据]开发技巧2:学会通过while、do-while或for循环进行业务分批处理


??这个专栏之前定下的计划是每周会发布一篇,但现在距离上一篇文章的发布已经过去了3个多月的时间了。希望自己从这周开始接下来坚持每周至少输出一篇文章!欢迎各位同学朋友们积极监督!

??好,废话少说!现在开始进入正题。这次教给大家的开发技巧就是学会使用while或do-while这两种循环结构进行分页查询,这个分页查询的方式主要包括了查询数据库和调用接口这两种方式。可能这么说大家未必明白,没关系,接下来听我详细讲解。

??比如有个业务需求是这样的:将系统A中所有用户的信息上报到B系统中进行数据分析。假设系统A的用户表为t_user(主键字段:id),系统B提供了一个名为analyzeUserInfo(List users)这么一个接口(UserInfo类的属性信息跟t_user表中的字段一一对应)。那么你是如何做的呢?有些同学可能想到了一个直接粗暴的方法也就是把t_user表中的数据全部查出来,然后在代码中进行处理。这种方法也不是不可以,但是如果t_user这张表的数据量是千万级别的话,你依然选择把全部数据放在内存中处理吗?答案当然不是这样。因为这么多的数据量放在内存中操作的话大概率会引发OOM异常的。所以接下来我就讲解下我是怎么处理的。
  1. 分批处理。 首先我们可以针对t_user进行分页,不建议使用limit ?,?这种方式进行分页。为什么呢?因为阿里巴巴开发手册中已经说得很明白:

MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。

所以得换种分页的方式,我们可以这样进行分页:select * from t_user where id > #{maxId,jdbcType=BIGINT} order by id asc limit #{size,jdbcType=BIGINT} 。通过order by id从小到大进行分页查询,首先maxId的值初始化为0,然后第二次循环的maxid = 0 + size, 接着以此类推,直到查询出的表t_user信息为空或者查询记录数小于每页的大小size时就跳出循环。

2.如果以上还是看不懂,没关系!接下来我直接上个伪代码,大家可以细品下,如下所示:
第一种方法:使用while循环

// 处理用户信息
public void handleUserInfo() {
	// 偏移量初始化为0
	int maxId = 0;
	// 每页大小初始化为500
	int size = 500;
	
	while(true) {
	    // 里面执行的就是select * from t_user where id > #{maxId,jdbcType=BIGINT} order by id asc limit #{size,jdbcType=BIGINT}这个sql
		List<User> userList = userService.getUsers(maxId, size);
		// (1)判断userList是否为空,如果为空则跳出循环
		if(userList == null || userList .isEmpty()) {
			break;
		}
		// 业务逻辑处理 - 数据分析
		analyzeUserInfo(userList)// (2)判断userList的大小是否小于每页大小,如果小于则跳出循环
		if(userList.size() < size) {
			break;	
		}
		// 更新偏移量:因为sql中已经有order by id asc,所以只需取列表中最后一项的id即可
	    maxId = userList.get(userList.size() - 1).getId();
}

第二种方法:使用do-while循环

// 处理用户信息
public void handleUserInfo() {
	// 偏移量初始化为0
	int maxId = 0;
	// 每页大小初始化为500
	int size = 500;
	List<User> userList;
	
	do {
	    // 里面执行的就是select * from t_user where id > #{maxId,jdbcType=BIGINT} order by id asc limit #{size,jdbcType=BIGINT}这个sql
		userList = userService.getUsers(maxId, size);
		if(userList != null && !userList.isEmpty()) {
			// 业务逻辑处理 - 数据分析
			analyzeUserInfo(userList)// 更新偏移量:因为sql中已经有order by id asc,所以只需取列表中最后一项的id即可
	   	    maxId = userList.get(userList.size() - 1).getId();
		}	
    } while(userList != null && !userList.isEmpty() && userList.size() == size);
}

??因为代码中已经写得比较清楚啦,所以我就不做过多的文字解释了。相信认真读过上面代码的同学已经知道我想表达的的意思啦。当然如果再细致点的话,我们可以对analyzeUserInfo(userList)做个try-catch处理,或者在analyzeUserInfo方法内部做处理也没问题,这样做的目的就是避免一次循环的报错导致的整个循环的退出。

??今天下午跟一位同事讨论时他指出我这种while或do-while的写法可能会有死循环的风险,但我觉得只要我们控制好偏移量maxId和每页大小size,就不会出现死循环的问题。同时他还指出了如下的写法,也就是先计算出userList的总页数,然后使用for循环进行处理,伪代码如下所示:

第三种方法:使用for循环

  // 获取总页数
  public static int getPage(Integer size, int step) {
        int page = size / step;
        page += size % step > 0 ? 1 : 0;
        return page;
   }
   
// 处理用户信息
public void handleUserInfo() {
	  // 偏移量初始化为0
	  int maxId = 0;
	  // 每页大小初始化为500
	  int size = 500;
	  Integer totalSize = userService.count();
	  Integer totalPage = getPage(totalSize, 500);
	  for(int i = 0; i < totalPage; i++) {
		    // 里面执行的就是select * from t_user where id > #{maxId,jdbcType=BIGINT} order by id asc limit #{size,jdbcType=BIGINT}这个sql
			List<User> userList = userService.getUsers(maxId, size);
			// 业务逻辑处理 - 数据分析
			analyzeUserInfo(userList)// 更新偏移量:因为sql中已经有order by id asc,所以只需取列表中最后一项的id即可
		    maxId = userList.get(userList.size() - 1).getId();
	   }
 }

??本人感觉这个方法也是不错的!哈哈,看来多跟别人讨论还是对自己非常有帮助的!以上的三个方法中,本人推荐使用第一种方法:使用while循环,因为个人感觉这种方法会更直观并更好理解点。

??好,完毕!

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:11:15  更:2022-02-16 13:12:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:11:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码