V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Sponsored by
LinkedIn
不坐班的神仙工作 · 去任何你想去的地方远程,赚一线城市的工资
2000 个不用出门 Social 的全球远程工作,帮助 V2EX 的小伙伴开启全新的工作方式。
Promoted by LinkedIn
frank1256
V2EX  ›  Java

Java ,大量对象内存中计算, oom 怎么处理哇。

  •  
  •   frank1256 · 194 天前 · 4844 次点击
    这是一个创建于 194 天前的主题,其中的信息可能已经有所发展或是发生改变。

    rt ,

    遇到个问题,需要查询 100 条记录查到内存中,然后进行计算。我内存很小,总是 oom 。就是单纯的内存不够。有啥好办法吗?

    mat 工具打开 dump ,里面全是 mysql 的 jdbc 对象。。。

    最高峰 300w 条,也没啥精力再去弄大数据啥的了。老板只给了 8g 。

    小弟能想到的就是,分批查询,一批一批计算。靠谱吗?

    有什么好办法吗,大佬们

    第 1 条附言  ·  194 天前
    我傻逼了,写错了是 100w ,不是 100 条。漏了个 w ,人傻了
    57 条回复    2022-03-22 15:10:10 +08:00
    soulzz
        1
    soulzz  
       194 天前
    加机器 swap 慢就慢
    不会崩
    frank1256
        2
    frank1256  
    OP
       194 天前 via iPhone
    @soulzz 加机器?我要读到内存里计算哇?加机器有什么用
    cabing
        3
    cabing  
       194 天前
    @frank1256 为啥要把所有的数据都加载到内存中计算?

    300w 行。。

    找个基于磁盘的 nosql ?
    heyjei
        4
    heyjei  
       194 天前
    用存储过程把计算推到数据库实现,不要用 Java 计算。
    jetyang
        5
    jetyang  
       194 天前
    分批计算当然靠谱
    RedBeanIce
        6
    RedBeanIce  
       194 天前
    关键词,代码中的大事务问题
    CEBBCAT
        7
    CEBBCAT  
       194 天前
    @frank1256 他少说了个“的”字,“加机器的 swap”

    另外,8G 也不算小了吧,我觉得甚至还挺大,一次一百条,平均下来一条数据能拥有 80M 内存。我想问问你,你说的内存里全是 JDBC 是什么意思?我离开 Java 好久了,JDBC 不是连接数据库的吗?你没用连接池吗?或者没有对连接池做单例吗?

    你可以讲讲你处理数据的方式吗?我怀疑是不是你程序写得有问题
    hidemyself
        8
    hidemyself  
       194 天前   ❤️ 1
    ResultSet.TYPE_FORWARD_ONLY
    ResultSet.CONCUR_READ_ONLY

    流式查询 /游标查询 不知道满不满足你的要求
    miao1007
        9
    miao1007  
       194 天前
    写一个 flink 的 jar,单独部署
    janus77
        10
    janus77  
       194 天前
    分批就分批呗,慢点来就行了,老板问你就说我电脑太差了没办法
    sagaxu
        11
    sagaxu  
       194 天前
    100 条跟 300w 也不搭啊
    DonaldY
        12
    DonaldY  
       194 天前
    1.先计算一次加载容量,100 条 大对象,大概多大。
    2.看 GC 算法,一般调大 老年代的容量,计算出 GC ( STW )的时间
    3. 任务进队列,每次执行 x 个任务。
    frank1256
        13
    frank1256  
    OP
       194 天前 via iPhone
    @cabing 单纯的统计业务
    frank1256
        14
    frank1256  
    OP
       194 天前 via iPhone
    @CEBBCAT 目前就是直接读了数据库,然后一口气查了 100 多万条,然后做计算。要是有几个线程一起查,就 oom 了
    frank1256
        15
    frank1256  
    OP
       194 天前 via iPhone
    @hidemyself 这个我没试过,我来看看
    frank1256
        16
    frank1256  
    OP
       194 天前 via iPhone
    @sagaxu sor ,漏了个 w ,人傻了
    liprais
        17
    liprais  
       194 天前 via iPhone
    写个 sql 算完事
    misaka19000
        18
    misaka19000  
       194 天前
    信息太少了,不好判断

    拆分计算是个办法
    CEBBCAT
        19
    CEBBCAT  
       194 天前
    @frank1256 一百万就是 1M ,8G/1M=8k 。可以跟我分享一下你是怎么计算负载的吗?
    Junzhou
        20
    Junzhou  
       194 天前
    100w 条数据之间有强关联性吗? 如果不是,就分批吧。
    leaves615
        21
    leaves615  
       194 天前
    文件缓存、流计算
    leaves615
        22
    leaves615  
       194 天前
    只读取用于计算的字段。
    matrix67
        23
    matrix67  
       194 天前
    这是挟泰山以超北海么😥
    frank1256
        24
    frank1256  
    OP
       194 天前 via iPhone
    @CEBBCAT 大佬我不太明白你说的负载是啥。就是现在的情况是,物理机 8g ,我给的堆内存 7g ,我有个需求,统计单表 30 天的记录的维度指标。比如 30 天内的,top10 ,平均值之类的。需求的某些字段还会在其他的表或者缓存里。有些还要计算公式,用 sql 太麻烦了。就打算读到内存里去计算。但是数量特别大的时候就会内存不够。实际情况里,程序还有其他地方用到缓存,或者还有其他的 query ,也就是说我这个统计能用的缓存很少。
    frank1256
        25
    frank1256  
    OP
       194 天前 via iPhone
    @leaves615 这个想到了,在优化
    night98
        26
    night98  
       194 天前
    10 个线程每个线程查 100 条,然后更新原子变量,完事。一次性加载一百万条,只能加钱。
    Brian1900
        27
    Brian1900  
       194 天前
    LIMIT 分批就行了吧,想要一次性读就得加机器呗
    msg7086
        28
    msg7086  
       194 天前
    如果对性能要求高,就加内存。内存才多少钱,一台 384G 内存的服务器也就几百刀就能买到。
    如果要省钱,那就先分批读出来写入单独的数据库中(相当于做数据快照),然后再慢慢分批处理算结果。
    lsk569937453
        29
    lsk569937453  
       194 天前
    这种百万级别数据的统计直接大数据计算平台(hardoop/spark)搞起啊
    mingl0280
        30
    mingl0280  
       194 天前 via Android   ❤️ 1
    为啥要一次读 100 万再计算?这是什么神奇的操作?
    mingl0280
        31
    mingl0280  
       194 天前 via Android   ❤️ 2
    @frank1256 据我所知常见的统计学指标没有任何一个需要加载 100 万数据才能计算的
    xuanbg
        32
    xuanbg  
       194 天前
    为啥不写 sql 去统计?
    gam2046
        33
    gam2046  
       194 天前
    一次业务操作需要读取百万条记录显然是不合理的。尝试业务流程上的优化。如果只是一些统计汇总的任务,完全没必要把所有记录加载到内存后处理。流式的处理并不需要什么内存空间。甚至还不如直接在数据库中汇总后再返回。
    sagaxu
        34
    sagaxu  
       194 天前 via Android
    top10 ,平均值是不需要一次全部读入内存的,可以流式处理
    liangkang1436
        35
    liangkang1436  
       194 天前 via Android
    楼主之所以提出这样的问题很有可能就只是一个单独的需求,为了谁一个需求去部署一个大数据框架很不经济,所以常规的做法应该就是每天定时统计,最后前台展示的时候从每天定时统计的表里面去取数据
    summerLast
        36
    summerLast  
       194 天前
    32 楼给了思路,还有另一种方式 就是 能否 100w 分批查 与分批计算,否的话就让对象尽量的小 而非有过多冗余信息
    X0ray
        37
    X0ray  
       194 天前
    可以参考下 aggregate function 的思路
    limbo0
        38
    limbo0  
       194 天前
    典型的 mapreduce
    wqhui
        39
    wqhui  
       194 天前
    楼上的流式计算 OR 分批汇总计算正解,或者尝试减少单条记录的大小
    frank1256
        40
    frank1256  
    OP
       194 天前
    @liangkang1436
    @sagaxu
    @summerLast
    @X0ray
    @wqhui
    感谢回答,有一些思路了
    clf
        41
    clf  
       194 天前
    利用磁盘去做缓存。

    POI 在操作大数据导出 excel 的时候就是类似的处理方式,一次性只处理 N 行,其余的数据行会在磁盘里利用文件缓存。
    leonme
        42
    leonme  
       194 天前
    @heyjei #4 这是认真的吗?如果是 OLTP 呢?
    liuxingdeyu
        43
    liuxingdeyu  
       194 天前
    我觉得分批查靠谱,再就是活用 mapreduce
    cheng6563
        44
    cheng6563  
       194 天前   ❤️ 1
    楼上这么多人就没个说增加 jvm 内存容量限制的吗?
    jvm 默认给的堆内存大小为物理内存的 1/4 ,所以啥不管直接加物理内存的收益也只有 1/4
    启动命令用 -Xmx6g 参数可以分配 6g 内存给堆。
    堆内存一般占 jvm 进程内存的大多数但不是 100%,所以最好再开些 swap 避免占满内存。
    ragnaroks
        45
    ragnaroks  
       193 天前
    @cheng6563 一般默认这种基础都会,而且楼主说了给了 7G

    @frank1256 如果时效性不高就猛拉 swap ,不过都用 java 了还抠硬件资源你们老板也是过分
    kingfalse
        46
    kingfalse  
       193 天前
    自己花钱,再加 8 个 G /狗头
    feitxue
        47
    feitxue  
       193 天前
    也给几个方向,不一定靠谱.要根据你的实际需求来.
    第一种,就是你有提到一个月才跑一次的任务的话,那就每天跑一次,生成你要的日数据,月底的时候汇总这一个月每天的数据.或者安排的没人用系统的时候,凌晨,和其他任务错开执行.
    第二种,还是你之前代码,不动,月底的时候申请按量计费的机器,跑完就销毁.不确定你们允不允许这样玩儿,不过这种方式可能对 java 不太友好.或者用云函数,现在有支持 java 的,只是改造成本确实有点大.
    其他的 sql 优化的楼上都提到了,我就没啥补充了.
    vacuitym
        48
    vacuitym  
       193 天前
    我觉得这个应该定期汇总。数据不多的话每天做日统计,多的话做几小时的统计。然后做月统计。最后只要根据统计的数据再做计算就可以了,我们一般这样做
    fuchaofather
        49
    fuchaofather  
       193 天前 via Android
    信息太少了。第一感觉没有必要查 100w 条数据到内存,都是真正需要的吗?
    timsims
        50
    timsims  
       193 天前
    xxx 量级数据找 top10 这种不就是经典的面试题。。
    CaptainD
        51
    CaptainD  
       193 天前
    必须要所有数据同时在内存么,如果不需要是否可以分批计算
    sampeng
        52
    sampeng  
       193 天前
    300w 条记录求平均值,top10.。不都是标准面试算法么。。面试会,实现需求就不会了?
    300w 就要上 hadoop 了? 300w 就要大数据了? 300w 哪怕上个 es 也比大数据强啊。。当然,这个量级都不需要。直接硬算酒可以了

    1 。分而治之当然靠谱,把数据写入 n 个小文件,再对小文件统计,最后汇总。这一个做法缺点是对数据库压力这个时候会相对较大,如果你的服务器和数据库之间的带宽就 1000M 。会瞬间打满,所以可能会影响业务。
    2.limit 控制条目数,开线程内存里去算了然后汇总。坏处是代码写起来复杂,维护相对较麻烦。过了半年可能看不懂自己写的代码。好处就是你可以控制间隔时间,想快就快想慢就慢。

    还一个方式。不要侵占你的业务代码,分析是分析,业务时业务。这是完全两个不同的领域。所以:
    写个 python 脚本把数据弄出来。。numpy+pandas 。这个数据量,算起来是慢点,但也没有接受不了的程度。
    giiiiiithub
        53
    giiiiiithub  
       193 天前
    可以考虑下预计算(当然,复杂的话不一定可以
    FYFX
        54
    FYFX  
       193 天前
    统计 30 天记录的维度指标,你这个看着像离线场景啊,用 spark 跑完之后把结果同步到 mysql 或者缓存吧。
    Brentwans
        55
    Brentwans  
       193 天前
    100w 数据就把 8G 搞 oom 了,看下数据自身情况吧,是字符串很多,还是数值都是存成对象了。百万级别的数据不算多,8G 还是可以放下的。
    如果想省事,直接 spark 单机跑吧,肯定没问题吧。
    312ybj
        56
    312ybj  
       193 天前
    1. 分批+多线程处理
    这个速度绝对比单线程快,而且资源稳定。
    2. 交给大数据
    这玩意就是大数据该做的事,Java 不适合大批量数据处理!!!!!
    zhangleshiye
        57
    zhangleshiye  
       192 天前
    如果想在代码里处理的话 ,可以弄张同步中间表,
    100w 数据 好比是未经过处理的数据 ->转到中间表(根据各种数据维度 比如时间 一个月处理一个月处理 把这些增量放到中间表里) ,就加一张配置表 比如记录到更新到某某某月了, ->结果就 sum 一下就好了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1946 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 50ms · UTC 05:19 · PVG 13:19 · LAX 22:19 · JFK 01:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.