批量更新数据之坑——总有遗漏数据没被更新

批量更新数据之坑——总有遗漏数据没被更新

1、业务场景假设:

要求给没有职业的人添加职业

分批处理:分页查询 + 数据处理

最终结果:所有人都有自己的职业

2、代码模拟

设计两个Map模拟数据库数据

dbMap:数据库数据

queryMap:查询数据

(1)数据库数据模拟

public class Person {

    private String name;

    private String gender;

    private Integer age;

    private String occupation;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }

    public Person(){}

    public Person(String name, String gender, Integer age, String occupation) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.occupation = occupation;
    }

    @Override
    public String toString(){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("name : ").append(name);
        stringBuilder.append(",gender : ").append(gender);
        stringBuilder.append(",age : ").append(age);
        stringBuilder.append(",occupation : ").append(occupation);
        return stringBuilder.toString();
    }

}
    private static Map<Integer, Person> dbMap = new HashMap<>();
    private static Map<Integer, Person> queryMap = new HashMap<>();
    private static Person person0 = new Person("大兵0","男",20,"");
    private static Person person1 = new Person("大兵1","男",20,"");
    private static Person person2 = new Person("大兵2","男",30,"");
    private static Person person3 = new Person("大兵3","男",30,"");
    private static Person person4 = new Person("大兵4","男",40,"");
    private static Person person5 = new Person("大兵5","男",40,"");

    /**
     * 模拟创建数据库数据
     *
     * @return
     */
    private void buildPersons() {
        dbMap.put(0, person0);
        dbMap.put(1, person1);
        dbMap.put(2, person2);
        dbMap.put(3, person3);
        dbMap.put(4, person4);
        dbMap.put(5, person5);

    }

(2)模拟数据库分页查询

    /**
     * 模拟数据库分页查询
     *
     * @param currentIndex
     * @param pageSize
     * @return
     */
    private List<Person> pageOccupyIsNull(Integer currentIndex, Integer pageSize) {
        List<Person> list = new ArrayList<>();
        //queryMap只查询职业为空的人
        for (int i = 0, j = 0; i < dbMap.size(); i++) {
            Person person = dbMap.get(i);
            if("".equals(person.getOccupation())){
                queryMap.put(j,person);
                j ++;
            }
        }
        for (int i = currentIndex; i < currentIndex + pageSize; i++) {
            list.add(queryMap.get(i));
        }
        return list;
    }

(3)业务实现模拟

/**
     * 集合中的数据都没有职业,分批次处理,给他们都增加职业信息
     */
    public void testPageAndUpdate() {

        buildPersons();
        dbMap.entrySet().stream().forEach(System.out::println);
        //已知没有职业的数据总数为5
        int count = dbMap.size();

        //分批处理
        for (int i = 0; i < count; i += 2) {
            List<Person> list = pageOccupyIsNull(i, 2);
            if(list == null){
                break;
            }
            list.forEach(person -> {
                if(person != null){
                    person.setOccupation("IT");
                }
            });
        }
        System.out.println("分批处理后:");
        dbMap.entrySet().stream().forEach(System.out::println);
    }

3、问题思考

上面的代码真的可以将所有的人职业都设为IT吗?

答案是:不能!

运行结果:

0=name : 大兵0,gender : 男,age : 20,occupation : 
1=name : 大兵1,gender : 男,age : 20,occupation : 
2=name : 大兵2,gender : 男,age : 30,occupation : 
3=name : 大兵3,gender : 男,age : 30,occupation : 
4=name : 大兵4,gender : 男,age : 40,occupation : 
5=name : 大兵5,gender : 男,age : 40,occupation : 
分批处理后:
0=name : 大兵0,gender : 男,age : 20,occupation : IT
1=name : 大兵1,gender : 男,age : 20,occupation : IT
2=name : 大兵2,gender : 男,age : 30,occupation : 
3=name : 大兵3,gender : 男,age : 30,occupation : 
4=name : 大兵4,gender : 男,age : 40,occupation : IT
5=name : 大兵5,gender : 男,age : 40,occupation : IT

 为什么呢?看图分解:

最后一次虽然sql过滤可以拿到数据,但是在分页查询的时候却漏掉了,大数据级别是万级的时候,一次处理为1000,那么最终也会留有万余的数据没有更新。

4、解决办法 

分页查询每次都从 0 查起

将  List<Person> list = pageOccupyIsNull(1, 2);

改为   List<Person> list = pageOccupyIsNull(0, 2);

/**
     * 集合中的数据都没有职业,分批次处理,给他们都增加职业信息
     */
    public void testPageAndUpdate() {

        buildPersons();
        dbMap.entrySet().stream().forEach(System.out::println);
        //已知没有职业的数据总数为5
        int count = dbMap.size();

        //分批处理
        for (int i = 0; i < count; i += 2) {
            List<Person> list = pageOccupyIsNull(0, 2);
            if(list == null){
                break;
            }
            list.forEach(person -> {
                if(person != null){
                    person.setOccupation("IT");
                }
            });
        }
        System.out.println("分批处理后:");
        dbMap.entrySet().stream().forEach(System.out::println);
    }

运行结果:

0=name : 大兵0,gender : 男,age : 20,occupation : 
1=name : 大兵1,gender : 男,age : 20,occupation : 
2=name : 大兵2,gender : 男,age : 30,occupation : 
3=name : 大兵3,gender : 男,age : 30,occupation : 
4=name : 大兵4,gender : 男,age : 40,occupation : 
5=name : 大兵5,gender : 男,age : 40,occupation : 
分批处理后:
0=name : 大兵0,gender : 男,age : 20,occupation : IT
1=name : 大兵1,gender : 男,age : 20,occupation : IT
2=name : 大兵2,gender : 男,age : 30,occupation : IT
3=name : 大兵3,gender : 男,age : 30,occupation : IT
4=name : 大兵4,gender : 男,age : 40,occupation : IT
5=name : 大兵5,gender : 男,age : 40,occupation : IT

警告:还有一种特殊情况,我这就简单描述一下,不举例说明了。

假如你要处理一批数据,这批数据里面有一部分会由于某种原因一直无法被处理,但是每次查询都会被查出来,如果这个数量级和你每次处理任务的数量一致或者更多的时候,任务就陷入死循环,永远有处理不完的任务在堆积,而真正需要处理的任务却被挤压在后边无法被查询到。

这种情况,就要具体问题具体分析,根据实际情况,通过某种方法把不能被处理的任务过滤掉,才能从根本上解决问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值