用户组用户角色应用关系绑定,先取最终用户需要绑定角色的交并集,然后再求用户组用户角色的笛卡尔积,最多可产生(用户数应用数角色数)条数据,多线程操作,从几小时到几十分钟,增加一个查询(open_role_user表的user_id)索引,变成6分钟,改成批处理插入,变成秒级处理,之前是因为jpa在多线程环境下有问题(使用threadlocal维护本身context,默认使用open-view-intercepter来维护线程级别的context),后来通过jdbctemplate重写,自定义事务范围

后来做了减法,因为最终生成的就是用户和角色的关联关系,是一张中间表,就一个user_id和role_id的联合主键,之前是批量插入,jdbctemplate的批量插入是直接按list的size进行设置,不过批量数据5000多个用户的用户组多绑定几个角色的话就达到12w+级别,即使批量插入(批量插入这步操作单线程了,所以cpu看监控都是比较少的80%左右的利用率,前面计算交并集的时候程序和mysql都能达到500%左右的利用率)也需要耗时

所以这里我尝试进行了优化,因为mysql的插入,对于设置了主键且主键不一样的情况下,不会产生冲突(只会生成一个IX表锁和REC_NOT_GAP当前记录的行锁),所以我改成使用多线程插入

但是需要面临的一个问题就是jdbctemplate的事务传播问题,因为spring的事务传播原理底层是使用的threadlocal,单线程环境下事务没有问题,而多线程使用并行流或者线程池就读不到上下文了,所以这里我需要做下调整,可以先切分数据list为多份然后循环使用最原生的thread执行批量操作(这里需要自己通过InheritableThreadLocal来绑定子线程的connection,spring默认使用的是ThreadLocal,不支持子线程上下文共享),再通过CountDownLatch来实现线程执行等待效果

结果发现多线程插入和单线程插入同一张表花费的时间几乎差不多,都是60s左右,具体原因可能是因为即使我多线程批量插入,但是我为了保持一个事务,所以只能共用一个connection,所以最终发送到mysql的还是一样的效果

后面我又找了一下有没有提升批量插入速度的方案,今天又发现一个jdbc批处理的参数rewriteBatchedStatements,会改批量sql(insert into xxx value(xxx))为拼接sql(insert into xxx values(xx),(xxx)),效率极大提升,基本执行12w+的数据插入只要2s,提升一个数量级


我不是码农
3 声望1 粉丝

java开发码农