1
xuanbg 2019-10-16 13:13:09 +08:00
拆分服务。。。。。。
|
2
IamNotShady 2019-10-16 13:14:33 +08:00
一个请求 900M ? 换成 G1 试试
|
3
chendy 2019-10-16 13:21:07 +08:00 1
一个请求 900M…要么极端需求要么设计不合理…
|
4
jimrok 2019-10-16 13:23:59 +08:00
把数据拆出来,放在 redis 或者 memcached 里面。否则堆会太大,一旦需要扫描堆就会比较耗时。
|
5
reus 2019-10-16 13:24:15 +08:00
换最新版 jvm 和最先进的 GC 算法
不换就不要奢求太多。 |
6
coolcfan 2019-10-16 14:45:38 +08:00
也许可以从生命周期的角度检查下请求处理过程中消耗的内存。
|
7
lihongjie0209 2019-10-16 14:54:22 +08:00
那也就是说你的这个服务的并发数量 = 10 ??
|
8
summer7 OP @lihongjie0209 是的,这个项目基本等于没有并发。 10 并发也到不了。
|
9
summer7 OP @chendy 需求大概是这样的:从数据库查出 30000 条数据,数据部分字段会包含长文本,单条数据约 200+字段,查出后需要对数据解析,据观察内存涨幅约等于 900m
|
10
summer7 OP @jimrok 数据流程大概是这样,第一步 jdbc 查库,第二步:对每条数据做解析,第三步:入 redis。 目前问题主要集中在解析这个过程 32 核机器跑满也需要 2-4s,稍微来个并发(不到 10 个),10g 的堆因为 gc 导致解析时间会上升到 20s+
|
11
yidinghe 2019-10-16 16:15:57 +08:00
1、确认有没有内存泄漏,也就是多次 GC 后程序还是能正常运行并且长期运行;
2、能够对老年代进行非 STW 回收的垃圾收集器只有 G1。建议换 G1 ; 3、进一步对内存使用进行剖析,减少不必要的对象持有; 4、如果缓存数据占大头,那么换用 Redis/memcached 等独立进程的缓存方案。 |
12
yidinghe 2019-10-16 16:17:05 +08:00
“从数据库查出 30000 条数据,数据部分字段会包含长文本” 这时候应该改为读取一条处理一条的方式。
|
14
misaka19000 2019-10-16 16:21:07 +08:00
用 C 语言重写
|
15
learnshare 2019-10-16 16:22:44 +08:00
这需求或实现逻辑并不合理吧
|
16
Immortal 2019-10-16 16:24:46 +08:00
单条数据约 200+字段= = 猛老哥
|
17
u823tg 2019-10-16 16:26:13 +08:00
换语言? 我胡说的
|
18
memedahui 2019-10-16 16:28:35 +08:00
@IamNotShady 我记得 java8 默认就是 G1 吧...还是我记错了
|
19
lihongjie0209 2019-10-16 16:31:46 +08:00
@misaka19000 #14 你用 c 语言管理 10G 内存试试, 没啥区别的。
|
20
lihongjie0209 2019-10-16 16:32:20 +08:00
@summer7 #9 一次查 3000 条试试?
|
21
hikikomorimori 2019-10-16 17:51:36 +08:00
考虑 Nosql?
|
22
lihongjie0209 2019-10-16 18:03:35 +08:00
@hikikomorimori #21 不管底层存储怎么样, 加载 30000 条数据就需要这么多的内存
|
23
babyvox5th 2019-10-16 18:09:27 +08:00
补充一下技术优化之外的,SSD 上 3000MB.
|
24
ipwx 2019-10-16 18:11:40 +08:00 via Android
不该想办法维护中间结果的表,降低每次请求计算量么
|
25
bk201 2019-10-16 18:17:06 +08:00
取少点不行吗?
|
26
bobuick 2019-10-16 18:21:56 +08:00
假设你其他措施都做了, 然后单次要是 900m 是必要的数据. 然后也无法重新设计, 然后请求完数据后内存里这些数据就不用保存了的话, 不是应该在 young 区被回收么. 把 young 设的足够大一些. 老年代应该保持一天都不到一次的水平
|
27
summer7 OP @ipwx 嗯,以前 hbase 做存储时已经是结构化的数据了,但是后来换了数据库必须要调用方去解析这些数据。其实整体数据流程很简单,jdbc 查询,查完解析入库。
|
28
bookit 2019-10-16 18:39:57 +08:00
用 C 写一遍,最简单的那种,看需要多少内存
|
29
sadfQED2 2019-10-16 18:40:58 +08:00 via Android
考虑 nosql?另外必须实时吗,不需要实时的话定时脚本计算,然后存缓存呢。最后,如果都不行就升级机器? 80 核以上,500+G 内存那种?
|
30
sadfQED2 2019-10-16 18:42:18 +08:00 via Android
@sadfQED2 楼上的换语言没什么意义啊,数据已经那么大了,你用什么语言加载到内存都那么大,除非改算法
|
31
BBCCBB 2019-10-16 18:45:21 +08:00
先用 G1 试试,然后增大堆内存再试试
|
32
l8g 2019-10-16 18:48:15 +08:00
1. 一次查询 3W 条记录,可以拆分一下
2. 调整一下 Young 和 Old |
33
summer7 OP @bobuick 感谢回复。young 设置大一点我会验证试一下的。这是第一次遇到这种 GC 问题,不知像互联网那些高并发项目,Full GC 频率的合理范围是多少呢?还请指教
|
34
summer7 OP @babyvox5th 数据 JDBC 查询完,就是内存操作了目前。
|
35
summer7 OP @l8g 感谢回复。楼上也有大佬提到修改 young 和 old,我会试一下的。 说起来,拆分查询也是一个头疼的事情,目前底层存储库不支持这种比如 limit 这种拆分查询
|
37
summer7 OP @bobuick 目前我单纯的本地写个 for 循环去触发查询接口,基本上每调用 9 次就会触发一次 Full GC,之后内存迅速下降,基本等于刚启动时候的内存。 改改参数看吧,也是第一次遇到这种问题
|
39
summer7 OP @yidinghe 感谢回复。
1、确认后没有内存泄漏,一次 FullGC 之后内存基本和刚启动时一样.但是一次 10 并发,基本就要 Full GC 一次,楼下也有大佬提出修改 young 和 old,我试一下看看,将 young 设置大一点。 2、对于取一条数据解析一条,嗯。。其实一直有个疑惑,while(resultSet.next ())的时候,这个 resultSet.next 是不是就相当于一个游标,其实数据还是存在于数据库端的?还是说不同数据库有不同的实现方式? |
40
shakoon 2019-10-16 19:08:41 +08:00
如果能在数据库上实现,用视图把表拆小、用存储过程进行你所说的解析,就试一下看看
|
41
semut 2019-10-16 19:28:17 +08:00
一次请求 30000 条数据,设计不太合理,可以简单说说这个任务的目的,看下有没有其他方案实现
|
42
0NF09LJPS51k57uH 2019-10-16 20:07:42 +08:00
长文本存 elasticsearch,其他 200 字段该拆表拆表,将 30000 条数据水平分割到不同 机器 /进程 流式处理,全部处理完毕再汇聚。
|
43
Jonz 2019-10-16 20:15:58 +08:00
关注下进展
|
44
uyhyygyug1234 2019-10-16 20:54:28 +08:00
|
45
af463419014 2019-10-16 21:02:13 +08:00
用堆外内存
处理的数据单独放在堆外内存,用完手动释放,可以跟 JVM 的 GC 分开 JVM 堆内存就不需要 10G 这么大了 |
46
pangliang 2019-10-16 21:08:52 +08:00
3 万条数据为啥要一次性读入内存? 用了 orm 吧? 查完库只能返回一个 3 万的 list, 然后遍历 list?
直接 jdbc 里查, 完了用 jdbc 的 result.next 去循环直接处理, 不要拼到 list 再遍历; 这样就不会有 3 万行在内存了 如果还是不行, 检查下 jdbc 返回数据的方式; 可以设置, next 一次返回一行 php 都有, java 不可能没有 |
47
SoloCompany 2019-10-16 21:14:27 +08:00 via iPad
加并发控制,限制 slow operation 的并发数,预留 30%以上的空闲 heap
|
48
mxalbert1996 2019-10-16 21:15:54 +08:00 via Android
@sadfQED2 换语言的意义在于减少 GC 的时间啊
|
49
summer7 OP @phantomzz 其实在之前用 hbase 存储是分表的,但是每个表也是 200+字段的,之后底层数据库弃用了 hbase,把所有表数据汇聚在一张超大表中,数据量相当的大。
感觉“合久必分”这句话太适合描述这种存储方式变化了 |
51
summer7 OP @pangliang 感谢老哥的回复。源于对 JDBC 理解的不是很深,目前我的做法是 next 遍历完,30000 条数据存入 list 再交给具体的解析方法解析。 也有其他大佬和老哥你一样说,可以 next 遍历时一条一条解析,或许这个就是解决问题的办法吧,待我一试。
|
52
neoblackcap 2019-10-16 23:04:29 +08:00
为什么需要一次读 3W 条数据?流式处理嘛,你业务逻辑不改,换 JVM 也未必有成效。
毕竟假如全部都是新生代对象,但是在 GC 被触发的时候,这些对象很有可能还是活的,有其他业务代码在使用这它们。你这个整体并发一样上不去。 这个东西还是得结合你的业务逻辑进行分析才行 |
53
Buffer2Disk 2019-10-17 00:01:22 +08:00
改架构吧,这业务设计就不合理。。。
|
54
swulling 2019-10-17 01:24:13 +08:00 via iPhone
每次请求结束后都主动触发一次 GC 吧😄
|
55
v2orz 2019-10-17 08:31:32 +08:00
换语言和堆外内存方案建议别搞
换语言作用不大 堆外内存,既然你们都设计成这样了,想想堆外内存管理估计也很难做好,不建议去踩 一次少查点数据或者流式处理就差不多了,GC 换 G1 |
56
softtwilight 2019-10-17 09:01:13 +08:00
把 resultSet 包装为流,一条一条解析,十分省内存;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Record>( Long.MAX_VALUE,Spliterator.ORDERED) { @Override public boolean tryAdvance(Consumer<? super Record> action) { try { if(!resultSet.next()) return false; action.accept(createRecord(resultSet)); return true; } catch(SQLException ex) { throw new RuntimeException(ex); } } }, false).onClose(() -> closeConnectionAnd...()) |
57
haochih 2019-10-17 09:20:16 +08:00
jdbc 默认情况下的读会把 sql 语句的结果全部加载到内存中,这种大数据量的读可以考虑采用 stream read。详情参见 https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-implementation-notes.html 中 ResultSet 一节。
|
58
tairan2006 2019-10-17 11:12:36 +08:00
这架构有问题,数据拆开,或者改成流处理
|
59
sorra 2019-10-17 12:10:09 +08:00
JDBC ResultSet 有 vendor 差异性,默认情况下:
- MySQL 和 PostgreSQL 会一次加载所有行到 JVM - Oracle 每次只加载 10 行到 JVM,DB 这边维持一个会话和偏移量,JVM 可以不停地推进偏移量,直到读完所有行 这个行为可配置(ResultSet 可以 setFetchSize),详情见 vendor 的文档 如果 vendor 支持 setFetchSize,你可以流式处理数据,不要都堆到内存里才全部处理 如果 vendor 不支持 setFetchSize 和 LIMIT,也可以想想能不能在 SQL 语句上想想办法 |
60
sorra 2019-10-17 12:15:03 +08:00
Statement 也可以 setFetchSize,为了防止来不及,可以在 Statement 就设上
|
61
jimrok 2019-10-17 13:41:22 +08:00
你的数据里有大文本是不太能降低内存的,大文本被装在进 java 要转换成 String 的对象,这些对象再进行解析,肯定要再产生若干小对象,gc 的负担肯定重。你这种还是提前做好,例如你要从小说里面找某个情节,那你把整篇小说读进来再查找肯定耗费内存。最后提前做好索引,这样根据索引,直接找到段落,返回段落信息就不需要消耗很多内存了。
|
62
lazyfighter 2019-10-17 14:21:05 +08:00
可以写个定时任务建立中间表,甚至最终数据表,这样你的并发也能上来,把数据处理拆出来
|
63
TJT 2019-10-17 16:52:14 +08:00
流处理。
|