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

请教下微服务间大批量数据获取一般是如何处理的

  •  
  •   gibber · 37 天前 · 4423 次点击
    这是一个创建于 37 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如 a 服务需要从 b 服务获取几十万的数据处理后生成自己的业务数据,如果 b 服务直接从数据库中一次性查出来返回,对内存的压力就很大。
    现在的方案是使用分页,每次最多 1 万条记录,获取一批处理一批,把整个业务处理的时间拉长了。
    想知道还有没有更好的办法
    46 条回复    2024-11-06 16:57:26 +08:00
    ZGame
        1
    ZGame  
       37 天前
    1.内存压力大?一个作业才几十万数据。。 如果怕影响 a 库业务性能,直接给 a 库做一个从库,从从库里拉数据。
    2.走 cdc 那种从日志里读取,这种时效性会好点。我是感觉没必要
    csys
        2
    csys  
       37 天前 via Android
    1.
    b 服务把数据保存成文件
    a 服务下载文件后进行处理

    2. kafka/cdc
    securityCoding
        3
    securityCoding  
       37 天前
    单独落离线表,明令禁止直接从线上业务表捞数据
    ymz
        4
    ymz  
       37 天前
    kafka
    m2276699
        5
    m2276699  
       37 天前   ❤️ 1
    数据源之间冗余
    xiaohupro
        6
    xiaohupro  
       37 天前
    时间线拉长应该是由于同步导致的吧,查一万处理一万。可以把查处来的数据立马丢给 Kafka 或者 Rabbit MQ 这类消息队列,A 服务监听队列,只要有数据就一直处理,这样应该会分批同步处理快一些。
    sagaxu
        7
    sagaxu  
       37 天前
    这是两个步骤

    1. b 服务从 db 获取几十万条数据
    2. a 服务从 b 服务获取完整数据

    第二个步骤在分页之后,从 1 次 rpc 变成几十次,内网 rpc 的开销是毫秒级的,几十次 rpc 增加几十毫秒,不会显著拉长处理时间。

    那问题就出在第一步,db 端分页之后,几十次小量查询,开销远大于单次全量。这种情况就不建议分页,而是分批,b 服务一次查询分批读取,写入文件或者消息队列等暂存设施,返回给 a 的是数据的指向,a 自己再分批读取
    ymmud
        8
    ymmud  
       37 天前
    才几十万条,服务之间类似于流式处理直接拉过去就行了
    SmartTom
        9
    SmartTom  
       37 天前
    a 服务直接做多数据源直连 b 服务数据库/doge
    povsister
        10
    povsister  
       37 天前 via iPhone
    你这种 case 如果数据量持续上升,应该用 spark 这种离线作业,或者压根不应该拆分服务。
    Wh1t3zZ
        11
    Wh1t3zZ  
       37 天前
    流式数据处理
    Plutooo
        12
    Plutooo  
       37 天前
    把 B 服务当成直接从数据库查不也是存在一样的问题么,还是说担心 B 服务的内存占用
    landerwong99
        13
    landerwong99  
       37 天前
    要么就离线近源处理,来个服务直接调 B 库的只读库,
    要么就流式处理,使用 kafka 之类的。
    ZZ74
        14
    ZZ74  
       37 天前
    搞那么麻烦干啥,导出文件写入共享目录,调用接口通知 喂 数据我放到 xx 目录下的 x 文件里了
    lifei6671
        15
    lifei6671  
       37 天前
    一般情况下是通过下面方式实现的:
    1 、建立只读线下备库,通过从库的方式从线上库实时同步数据,不能用于线上系统读,只能用于线下业务大批量读。
    2 、建立只读从库,和主库实时同步,只能进行线上系统只读。
    3 、通过 binlog 实时建立分析宽表,一般用来汇总各个业务方数据,建立大宽表,支持线下业务分析已经大批量查询等。
    kaf
        16
    kaf  
       37 天前
    流格式数据
    8355
        17
    8355  
       37 天前
    有 id 能排序的话 传起始 id 过来就行了 where id > xx limit 10000 order by id asc
    8355
        18
    8355  
       37 天前
    其实数据没这么大,我的的业务天天导入 300m 的 csv ,200w 左右。
    只要不是一两百个字段带 text 的宽表数据 不会特别大的。
    fengpan567
        19
    fengpan567  
       37 天前
    没条件搞数据同步服务的,直接让对方生成一个 csv 上传到 oss ,你每天去捞当天的文件同步就行了
    print1024
        20
    print1024  
       37 天前
    如果数据库 id 是有序的话可以先排序,然后切分数据,如 1000 条一次,多线程处理,也就这样了,用中间件其实没太大必要
    cccssss
        21
    cccssss  
       37 天前
    直接读 b 库
    不让读的话只能说又想马儿跑又不给马儿吃草
    InkAndBanner
        22
    InkAndBanner  
       37 天前
    oss or 离线数仓,如果在线去拉的话 就算可行,ab 服务的 io 会不会被占满导致其他接口、服务不可用?
    bthulu
        23
    bthulu  
       37 天前
    获取一批处理一批, 怎么就把业务处理时间拉长了?
    你一次获取, 不还是要处理这么多?
    newaccount
        24
    newaccount  
       37 天前
    要么时间换空间,要么空间换时间
    你这又嫌内存占用大又嫌处理时间长的
    就算让 a 直接读 b 库,那内存占用无非是从 b 服务器转移到 a 服务器
    masterclock
        25
    masterclock  
       37 天前
    看看能不能让 a 不依赖 b ,数据分别进 a 、b 服务
    如果 a 强依赖 b ,那就别微服务了,把 a 整合进 b ,或者 a 的这一部分功能整合到 b
    kchenzhi
        26
    kchenzhi  
       37 天前
    这事我有经验。
    1 、不要在 responseBody 里返回, 那样内存一定会爆。
    2 、不要分页查询,两个原因:①不同分页的查询不在一个事务中,会有数据一致性的问题。②当查询到靠后的分页时,耗时直线上升,性能太差。
    kchenzhi
        27
    kchenzhi  
       37 天前
    3 、如果能让 a 直接读库,那是一种解决方案。但如果 b 里有些处理逻辑比较复杂,那你得在 a 中重新实现一遍,重复工作量且代码冗余,不合适。

    我们最终采取的方案是:访问数据源时使用游标,一行行读取数据后,通过 http outputstream ,用流式返回。
    GeekGao
        28
    GeekGao  
       37 天前
    时间换空间:小批次分批执行
    空间换时间:增加内存,大批量执行
    中间方案:放在共享存储(例如 nfs ),mmap 读文件,增加消费者进程消费
    clf
        29
    clf  
       37 天前
    数据表做数据冗余吧。
    gibber
        30
    gibber  
    OP
       37 天前
    @xiaohupro 就是一次查询改为多次查询后比较耗时,不太想引入额外的中间件来处理
    gibber
        31
    gibber  
    OP
       37 天前
    @kchenzhi a 服务本地数据源的话是会使用流式查询的,倒是不清楚微服务调用也能使用流式处理的方式,感觉可以参考,谢谢
    molicloud
        32
    molicloud  
       37 天前
    直接在 b 服务处理数据,再通知或调用 a 服务
    vacuitym
        33
    vacuitym  
       37 天前
    定时生成文件给下载地址(这感觉很像对账单)
    asAnotherJack
        34
    asAnotherJack  
       37 天前
    @kchenzhi #26 `当查询到靠后的分页时,耗时直线上升,性能太差。`
    盲猜是不是用的 limit offset 做的分页
    gibber
        35
    gibber  
    OP
       37 天前
    @molicloud b 服务只负责从数据库查询数据,不处理具体业务
    notwaste
        36
    notwaste  
       36 天前
    b 服务查出来往 kafka 里面丢,a 服务消费处理
    macttt
        37
    macttt  
       36 天前
    A 服务提交一个任务给 B 服务,B 服务收到任务后推送数据给 A 服务。两个服务之间的数据完备性检查,你可以使用类似于 TCP 传输的形式。A 服务不用管 B 服务怎么实现的,只需要接收数据就行了,B 服务则需要让 A 服务记录数据完整性的元数据。
    snickers
        38
    snickers  
       36 天前
    不建议走接口 ,ETL 转换调度
    molicloud
        39
    molicloud  
       36 天前
    @gibber #35 在新建一个 b-analysis 服务,也连和 b 相同的数据库
    siweipancc
        40
    siweipancc  
       34 天前 via iPhone
    不要分页,用游标依次写到队列里
    kchenzhi
        41
    kchenzhi  
       32 天前
    @asAnotherJack
    请问是这种方式吗:LIMIT row_count OFFSET offset
    我们就是用这种, 仍然是直线上升哦,
    每页 5000 行,翻到 1000 页后,对比起第一页的查询速度已经差了好几个数量级了。

    请问是有什么优化技巧我没用上么?
    asAnotherJack
        42
    asAnotherJack  
       32 天前
    @kchenzhi #41 用 lastId + pageSize 的方式,where id > lastId order by id limit pageSize
    kchenzhi
        43
    kchenzhi  
       31 天前
    @asAnotherJack 这个方案我们也用过, 可以是可以,但是有两个问题:
    1 、对调用方有了一些入侵。
    2 、多分页拉取的数据可能会跨事务,导致数据一致性被破坏。

    所以最后选择的是游标查询加流式传输,一次查询解决问题。
    suolong00
        44
    suolong00  
       30 天前
    没人说用消息中间件吗,b 服务器使用多线程查询几十万的数据,写到消息队列中,a 去消费就行了,可以多线程插入
    gibber
        45
    gibber  
    OP
       30 天前
    @suolong00 主要不想为这一个功能去引入一个中间件
    Plutooo
        46
    Plutooo  
       16 天前
    @kchenzhi 你好,可以请教一下 http outputstream 流式返回主要是通过什么实现吗,是 rpc 有类似的实现么
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2901 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 08:22 · PVG 16:22 · LAX 00:22 · JFK 03:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.