项目: 杭钢待办补偿系统
接口: 通过南北补偿机制*-*同步删除待办数据
业务描述:
系统1: 南北待办系统
系统2: 用友系统
南北系统在删除待办任务数据时候,正常情况下在删除自己系统数据时同时会调用用友任务数据删除接口实现数据同步;在服务调用的时候,出现网络阻塞/异常,数据库宕机等情况导致的数据不同步问题,防止用户会看到脏数据,使用定时补偿机制,同步南北系统已删除数据.
流程: 首先查询南北系统的所有数据的key,按照规则转换成用友系统中数据的key,然后查询用友系统所有数据的key,进行比对,找出用友系统数据状态为待办,并且在南北系统数据不存在的数据,进行删除.
难点: 两张表数据量都是万级以上
- 性能问题: MongoDB在处理大规模数据时可能会面临性能问题。大规模数据查询和比对可能会导致较长的查询响应时间,需要通过合适的索引设计、查询优化以及分片技术等手段来提高性能。
- 数据一致性: 由于数据同步是基于定时补偿机制实现的,可能会出现由于网络延迟、数据处理时间等因素导致数据不一致的情况。需要设计有效的机制来确保数据同步的准确性和可靠性,例如使用MongoDB的事务或者乐观锁来确保数据的一致性。
- 并发处理: MongoDB在处理大规模数据时需要考虑并发处理的问题。多个客户端同时对数据库进行读取、比对和删除操作可能导致数据错乱、性能下降等问题。需要采取合适的并发控制机制来确保数据处理的安全性和一致性,例如使用MongoDB的读写锁机制或者乐观并发控制等。
- 资源消耗: 大规模数据处理可能会消耗大量的系统资源,包括内存、CPU、网络带宽等。需要合理规划系统资源,优化MongoDB的配置和部署,以及优化查询和数据处理流程,以降低资源消耗并提高系统的稳定性和性能。
- 错误处理: 在处理大规模数据时,更容易出现各种错误,如数据格式错误、网络异常、数据库连接超时等。需要实现健壮的错误处理机制,及时捕获和处理异常,确保系统在面对异常情况时能够正确地进行错误恢复和异常处理,例如使用MongoDB的异常处理机制或者重试机制来处理异常情况。
解决方案:
-
异步线程的使用
使用CompletableFuture异步查询两个系统的所有数据,将需要处理的数据的Key保存成List,将正确数据的Key保存成HashMap;
-
使用ForkJoin框架对任务的递归拆分处理:
优点1: ForkJoin框架会对大任务进行分治处理,并且是多线程处理.
优点2: ForkJoin框架的工作窃取算法能够更好的“负载均衡”,实现高效的并行计算,同时减少了任务的等待时间。
-
数据结构选型
选用LinkedHashMap存储正确数据的Key, LinedList存储需要进行对比的Key
优点: 减少ArrayList扩容拷贝现象的发生,减少时间复杂度
-
对比过程
在ForkJoin的分治任务中, 我们循环遍历LinkedList,与HashMap进行比对,如果map中不存在key为list该元素,那么就删除,如果有,就添加.
-
数据分片
为了不长时间I/O阻塞数据库,不一次性读取80w行数据,采用分片方式对数据进行加载;对数据的唯一key进行hash分片,每次读取一个分片的数据,保存在List中,然后对其进行对比.
queryWrapper.apply("mod(bus, {0}) = {1}", numberOfShards, i); //这段代码的意思是,使用apply函数插入SQL语句 "mod(bus,分片数量)=i"
bus是主键,对主键和你想分片的数量进行取余操作,如果等于i,就会读取.
select * from tb where mod(bus,8)=1
-
线程池的创建
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), 100, 30, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), new ThreadPoolExecutor.AbortPolicy() );
线程数的确定 : 该方案是以当前cpu核心数为核心线程数
如果需要对cpu利用率有特定要求,我们需要通过计算,以及不断的尝试去确认线程池的具体数量
List<String> sysUserIdQuerys = new LinkedList<>();
List<CompletableFuture<Void>> completableFutureList = sysNameQuerys.stream().map(name -> {
CompletableFuture<Void> completableFuture =
CompletableFuture.runAsync(
String mongoUrl= "https://portal.hzsteel.com"+getTodoListByCodeUrl;
TodoRequestVO todoRequestVO = new TodoRequestVO();
todoRequestVO.setAppId(nbTypeName);//必填标识
todoRequestVO.setDoneStatus(0);
todoRequestVO.setTenantId(ajaxTenantid);
Map<String, String> headers = new HashMap(2);
headers.put("Content-Type", "application/json");
String moStr = HttpUtil.doPost(mongoUrl, net.sf.json.JSONObject.fromObject(JSONObject.parseObject(JSONObject.toJSONString(todoRequestVO))),headers, (String)null);
List<MongodbTodoQueryVO> moList = moList(moStr);
LocalTodoQueryVO.addAll(moList);
);
return completableFuture;
}).collect(Collectors.toList());