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

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

  •  
  •   frank1256 · 2022-03-20 19:48:24 +08:00 · 5980 次点击
    这是一个创建于 1013 天前的主题,其中的信息可能已经有所发展或是发生改变。

    rt ,

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

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

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

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

    有什么好办法吗,大佬们

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

    300w 行。。

    找个基于磁盘的 nosql ?
    heyjei
        4
    heyjei  
       2022-03-20 20:21:17 +08:00
    用存储过程把计算推到数据库实现,不要用 Java 计算。
    jetyang
        5
    jetyang  
       2022-03-20 21:09:52 +08:00
    分批计算当然靠谱
    RedBeanIce
        6
    RedBeanIce  
       2022-03-20 21:10:49 +08:00
    关键词,代码中的大事务问题
    CEBBCAT
        7
    CEBBCAT  
       2022-03-20 21:18:22 +08:00
    @frank1256 他少说了个“的”字,“加机器的 swap”

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

    你可以讲讲你处理数据的方式吗?我怀疑是不是你程序写得有问题
    hidemyself
        8
    hidemyself  
       2022-03-20 21:19:09 +08:00   ❤️ 1
    ResultSet.TYPE_FORWARD_ONLY
    ResultSet.CONCUR_READ_ONLY

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

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

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

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

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

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