V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NoKey
V2EX  ›  程序员

请教大佬, springboot+mongodb 如何效率的更新整个实体类

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

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

    //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);
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2779 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 12:07 · PVG 20:07 · LAX 05:07 · JFK 08:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.