请教大佬, springboot+mongodb 如何效率的更新整个实体类 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
请不要在回答技术问题时复制粘贴 AI 生成的内容
NoKey
This topic created in 2441 days ago, the information mentioned may be changed or developed.
基于 springboot+mongodb 做一个系统
有一个场景,保存了全公司的员工信息( 3 万+)
定时会从 mysql 库同步员工信息,如果员工信息有改动(有一个版本号可以比较),mongodb 里就需要更新
但是。。。但是。。。我不知道到底哪个信息修改了
员工信息字段有几十个
如果要一个一个的比较,要写好多判断语句
然后,我找了 MongoTemplate 又一个方法 findAndReplace
但是,效率很低,测试一下,更新 1 万多人,需要 5 分钟左右(测试服务器),这段时间,用 robo 3T 工具去打开数据表,会卡住
哪位大佬知道,有没有更好更快速的方法呢?
谢谢
23 replies    2020-03-18 21:42:39 +08:00
kkkkkrua
    1
kkkkkrua  
   Aug 21, 2019 via iPhone
我曾经也考虑过类似的问题
如何系统的考虑修改字段的新值和旧值的记录,后面考虑了下,可以把记录做副本保存,再加上时间戳,对比的话只用对比最近的两个记录就行
缺点是数据比较大,算法如何实现,我还没落地。
Takamine
    2
Takamine  
   Aug 21, 2019 via Android
直接更新这个员工的全部数据快,还是找一遍更新某几个字段快。_(:з」∠)_
如果时效性要求不高,感觉可以丢到队列慢慢消费。
429463267
    3
429463267  
   Aug 21, 2019   1
监控 mysql 执行日志,mysql 集群主从复制就是这个原理,读取 mysql 执行日志以后,转换成 mongodb 语法到 mongo 执行
luckylo
    4
luckylo  
   Aug 21, 2019 via Android
换个方式。你这是定时同步。假设你更新员工信息,就往消息队列里丢一个消息,mongodb 这个服务接收消息并更新员工信息,这样或许好一丢丢。
NoKey
    5
NoKey  
OP
   Aug 21, 2019
@luckylo 谢谢回复。实际问题,就是无法这样,如果能这样,就没有我的问题了,现在的状况就是,只能在一定时间之后获取一次,而不能在每次有修改的时候,同步一下。这个一定时间之后,不可控,或许来一个较大的部门变动,就会涉及很多人。。。
wdmx007
    6
wdmx007  
   Aug 21, 2019
用反射去对比字段可以少写判断 /手动滑稽
DsuineGP
    7
DsuineGP  
   Aug 21, 2019
javers 了解下
notreami
    8
notreami  
   Aug 21, 2019
为啥要定时呢?准实时不行嘛?
mysql 数据有变动就发一个变更消息出来,直接更新 mongodb 不就可以了?
Kaiv2
    9
Kaiv2  
   Aug 21, 2019 via Android
时效性要求不高是不是可以弄个定时任务跑下
NoKey
    10
NoKey  
OP
   Aug 21, 2019
@notreami 原来的服务已经写好了,不能动,所以无法再修改信息的时候给个消息过来
NoKey
    11
NoKey  
OP
   Aug 21, 2019
@Kaiv2 就是定时任务,但是感觉更新的很慢
energetic
    12
energetic  
   Aug 21, 2019
看起来你似乎用的是逐个更新的方法,建议试试先查出所有需要更新的员工信息,不用比较哪些字段变化,用 bulkOps 方法批量整体更新
liuhuansir
    13
liuhuansir  
   Aug 21, 2019
半夜跑定时任务,影响很小吧,慢点就慢点,5 分钟也还好吧?
NoKey
    14
NoKey  
OP
   Aug 21, 2019
@liuhuansir 关键是为了更新效率性,又不能设置为半夜来跑,定时 2-3 小时跑一次,
NoKey
    15
NoKey  
OP
   Aug 21, 2019
@energetic 谢谢回复,我看了一下 bulkOps 需要自己写一个 update,但是人员信息太多,得一个一个的过一遍才写的多 update,这个是很痛苦的。。。
Kaiv2
    16
Kaiv2  
   Aug 21, 2019 via Android
把数据分片处理下
br00k
    17
br00k  
   Aug 21, 2019 via iPhone
这么点数据。实时性要求不高就按数据 update 时间定时同步就行了。实时性要求高就用 MQ。
artikle
    18
artikle  
   Aug 21, 2019
定时任务+时间断点。
每 5 分钟或者 10 分钟跑一次脚本,去数据库拉取更新时间为,上一次时间断点到当前时间这一批次的所有数据 批量更新到 MongoDB,再将断点时间更新为当前时间。
ShellMings
    19
ShellMings  
   Aug 21, 2019 via iPhone
其实你可以在 mysql 里新建一张和员工表一样结构的表,每次修改员工信息时那张表只存员工 id 和他修改过的字段,当然真正的员工表里的信息也要改,再加一个 lastEditTime 在新表里这样你就知道谁在什么时候改了些什么。
Juszoe
    20
Juszoe  
   Aug 21, 2019
mysql 做个触发器,把更新的员工 id 放到新表中,记录更新时间,然后从这个表得知哪些被修改了,这样可以不?
TestCode
    21
TestCode  
   Aug 21, 2019
我觉得这样比较好,数据库那头用一个触发器,一旦有员工信息修改便将修改相关信息放入消息队列,另一端消费。
LeeSeoung
    22
LeeSeoung  
   Aug 22, 2019
"员工信息字段有几十个
如果要一个一个的比较,要写好多判断语句"

mysql 跟 mongodb 分别取回来的实体类 自己写一个反射遍历所有成员进行比对,很容易得到差异点吧,可以写成通用的,并不需要一个一个写。
wxb2dyj
    23
wxb2dyj  
   Mar 18, 2020
以下是我的方法

//CommonUtil.java
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
....
public static void copyNonNullProperties(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}

private static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
String propertyName = pd.getName();
Object srcValue = src.getPropertyValue(propertyName);
if (srcValue == null) {
emptyNames.add(pd.getName());
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}

//获取新 /旧员工信息,newEmployee 是新的员工信息,originalEmployee 是旧的员工信息
Employee originalEmployee = readFromYourMongoDB();
Employee newEmployee = readFromYourMySQL();
//该方法将会更新 originalEntity,而且只更新相对 newEntity 中变化的字段
CommonUtil.copyNonNullProperties(newEmployee , originalEmployee );
//重新保存 originalEmployee 到 MongoDB
ObjectId objectId = new ObjectId();
Date date = objectId.getDate();
//保存更新时间
originalEmployee.setUpdateTime(objectId.getDate());
employeeRepository.save(originalEmployee);
About     Help     Advertise     Blog     API     FAQ     Solana     2996 Online   Highest 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 80ms UTC 15:10 PVG 23:10 LAX 08:10 JFK 11:10
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86