系统梳理

257 阅读5分钟

大批量增人优化梳理:

优化重点:

尽可能将操作批量化,减少访问数据库的次数,找出大批量增人时需要频繁访问数据库的场景, 将基表数据(险种数据,机构代码数据)缓存起来(缓存到一个map中,或者redis),重复使用。

大批量增人时需要频繁访问数据库的场景:

  1. 解析Excel表格,组装个人投保单数据时,需要频繁访问数据库获取个人投保单相关信息。

  2. 生成个人投保单数据是循环被保人列表来处理,导致循环insert表数据

  3. 保存个人信息是循环调用存储过程保存,而且保存产生的客户号是后面流程必须使用的外键。所以必须要解决这个问

针对频繁访问数据库场景优化方案:

  1. 提前将团单下的主被保人客户查询出来(以团单的维度查询,只需要查询一次),结合Excel表格中的数据,提前构造好主被保人客户号集合。
  2. 将循环处理被保人改为批量处理由saveOnePerson改为saveAllPerson saveAllPerson逻辑:
  •  使用中间表client_information_sig_tp保存被保人基本信息(五项信息),目的是在还没有保存到正式的客户表时,方便取数 (可以按照自己的维度去查找数据【比如通过团体投保单号,查询所有被保人信息】,记录中间产生的各个表的主键和业务号(地址序号,联系信息序号等等)) 和批量操作(连表),为了保证client_information_sig_tp中的查询效率,每次批量导入被保人任务时, 都会根据团体投保单号去清除,改团单下的临时被保人信息(防止表数据膨胀和脏数据)。
    保存被保人信息时,使用批量保存操作(ibatis SqlMapExecutor startBatch 使用批量提交功能,这个功能会将sql拼接,再发送给数据库执行) 更新被保人信息。
    通过优化sql实现批量更新(merge into *** use ***),按照团单的维度去批量更新信息,这个时候可以和客户表连接起来,判断这个被保人是否为新客户 ,如果为信息客户,则在这个阶段可以给他生成客户号,达到访问一次数据库,批量更新客户号的效果,不用每次申请客户号都要访问数据库。

  •  对于不使用的对象和集合,及时置空,避免在jvm垃圾回收时不能及时回收。

  •  大量使用merge into *** use *** 语法和ibatis SqlMapExecutor的批量提交功能去保存个人投保单相关表数据

  • 大量使用线程池执行耗时操作(黑名单校验,标准保费计算,客户信息校验等等)

  • 使用CountDownLatch来保证,有上下文依赖的流程正确执行,设置超时时间(黑名单校验,标准保费计算,客户信息校验等等),防止系统阻塞。

超大团单自动核保优化梳理:

 优化重点:

超大团下由于包含的被保险人数过多(小榄镇团单18万被保人),原有逻辑是先获取全部个单信息和组装规则引擎接口报文(使用线程池),再执行规则引擎(使用线程池), 但是这两个操作是同步的,在被保人数过多时, 可以优化成并行的,组装好一个个单信息就可以执行规则引擎了(生产者消费者模式)。

原有组装规则引擎接口报文中的个单信息的逻辑是,循环个单号数组,根据个单号查询数据库,使用线程池(15个核心线程,阻塞队列大小为size-15)来执行, 但团单数量过多时比如(18万),虽然使用了线程池,但是这个效率还是 比较慢的,还有规则引擎一次处理那么多人,对规则引擎会产生压力,影响其他业务的进行,并且查询个单信息过程会持续长时间频繁访问数据库。所以这块可以改为分页批量(一次性查询多个)操作(提高效率和减少访问数据库次数),分页组装报文,分批去访问规则引擎。

解耦优化方案:

  1. 使用一个线程池充当生产者线程池,每个线程分页处理个单号List,分页(200条)向个单报文队列添加数据(为什么数量选择200,主要是出现生产效率的考虑,如果数量过大,查询时间会增加,生产效率会下降,效率下降就会导致生产线程效率下降,可能导致线程池满队,触发拒绝策略。还有报文对象是一个大对象,不宜加载过多数量的报文,会导致内存吃紧。)

  2.  队列采用ArrayBlockingQueue阻塞对列,当对列中的数据满了或者空的时候阻塞线程,消费者线程在生产者对列为空时,会阻塞当前线程 。

  3. 使用CountDownLatch来保证,所有消费线程池中的线程消费完数据后,才执行下一步流程,这里要注意的是,主线程每次循环使用线程池组装了一个分页的数据的报文对象到生产者队列中,然后使用消费者线程池每次取 出一个报文的数据,去调用规则引擎。这里要注意下生产速度和消费速度,因为多表查询,连表数据量比较多,规则引擎200一次的数据量下,速度还是比较快,所以生产效率<消费效率,这时候的场景是:消费者线程池会有
    一些线程阻塞在获取数据那里。

  4. 使用ThreadLocal来保存函数直接共用的变量(分页数,生产者对列,执行结果队列,异常队列),减少参数在函数之间的传递,这里不能使用全局变量,会被其他执行核保的线程影响,产生脏数据。

  5. 使用原子包装类AtomicInteger,来保存分页信息,使用cas的方式去控制分页的计算,防止多线程的情况下分页数据计算出错