分析一次秒杀活动堆内存,通过jvm优化让服务抗过一分钟

前言

在之前的公司参与过一次秒杀xxxx活动设计与开发过程,涉及到了jvm优化问题,在这里记一下。

一、背景:

一个秒杀活动,秒杀时间是60s,TPS为3000,有三台机器-4核8g。

1.机器的jvm内存大小分析

一台8g内存的机器,大概分多少g给堆?

答:极限情况下6g。2g是os固有的;如果要用TProfiler、Arthas等监控工具,最多只能有4g给堆,推导出来监控工具大约要占1/4;
一般情况下4g差不多。

2.新老年代默认内存大小分析

新老生代默认是1:2,那么新生代:1333M。应当为:1333*0.9*0.8=960M
极限使用率90%,因为有s区,它要空着;
也不可能让eden区完全满着,大约不能超过80%的使用率。

3.峰值分析

如果峰值每秒3000笔,每台机器按理要达到1000笔,但我们要做一个20%-50%的预留,也就是每秒能抗住1200-1500笔。

4.订单对象分析、full GC原因分析

一个order对象大小(对象头、实例数据、对齐填充):不超过1k;
根据一些工具分析,一条链路会产生多少个对象,比方说20个:1k*20=20k;
假设单台要抗住1200笔:20k*1200=24M;
也就是每s产生24M的对象,年轻代堆内存可用大小是960M,能抗多少秒呢?
——960M/24M=40s; 只能抗40s

只能抗40,没有压测的必要了。
并且,这个服务不可能只有促销在跑,还有其他的业务也再跑。

也就是40s后,young区不够用,会触发young GC。young GC有可能把对象以到老年代,促使老年代也达到GC条件,触发了full GC,full GC会STW,会导致服务卡死。

old区内存-2666M,促销的时候,old肯定不是空的,比如原来有2000M,26s后young GC后,有的对象晋升到了old区,比方晋升的所有对象的大小为600M,也就是old区一共2600M,那么会触发full GC。

46.对象的内存布局(对象头、实例数据、对齐填充)和对象的访问定位(句柄访问和直接指针)_simpleGq的博客-CSDN博客

5.在不加机器的情况下,怎么解决问题?

——给年轻代和老年代比例换一下,把1:2改成2:1。

  • 年轻代:2666M
  • 老年代:1333M

这种情况下,年轻代能抗80s了,抗过1分钟了,解决了问题。

但请各位注意

G1和ZGC不能这么调,这两个GC不能改变新老年代比例;

改了之后,自适应策略、担保机制会失效。
先抗过这60s,如果秒杀不常用,晚上再把参数改回来。 

6.图示

 二、拓展

1.内存泄漏是在请求并不大的时候,通常造成OOM的主要原因是什么?

内存泄漏是在请求并不大的时候,通常造成OOM的主要原因;
形象的说,就是拿了一块内存不还了。

查看堆内存情况:

当程序比较卡的时候,排查问题原因的思路:

  1. top ,查看当前cpu有没有高不高
  2. jstack ,查看线程有没有发生死锁,或者锁由于某些原因未释放的问题
  3. jmap,分析堆内存情况

2.堆内存设置了4g,发现有不正常的full GC,此时查看堆内存才2g,为什么?

——方法区。启动项目的时候,元数据空间不够用,会抢占堆内存。这种情况jdk1.7根据突出,因为jdk1.7方法区占用了堆内存。

 三、G1垃圾回收器

G1垃圾收集器使用前提:

 如果达到了这些要求,可以直接用G1,可以不用调优了,因为你再调优,也达不到他的性能。

四、jvm排查问题方式以及解决方案:

<think>嗯,用户问的是Redis作为一级缓存,JVM作为二级缓存的介绍。首先,我需要确定这两个概念在缓存系统中的作用和位置。通常,一级缓存(L1)是离应用最近的,速度最快,但容量较小;二级缓存(L2)则更大但稍慢一些。不过这里用户提到的是一级用Redis,二级用JVM,这似乎和常见的JVM一级、Redis二级的结构相反,可能用户有特殊需求或者配置,我需要先确认这点是否正确。 接下来,我需要解释这两个缓存的角色。Redis作为内存数据库,通常作为分布式缓存使用,适合做二级缓存,而JVM缓存如Ehcache或Caffeine作为一级,因为它们在应用进程内,访问更快。但用户的情况反过来,可能需要说明这种架构的适用场景,比如可能考虑到网络延迟或其他因素,将Redis作为更接近数据源的一级,而JVM作为二级补充? 然后要分析两者的优缺点。Redis作为一级,优点是分布式、一致性好,但网络延迟可能影响速度。JVM作为二级,访问速度快,但容量有限,数据不一致风险。需要比较两者的读写策略,比如如何保持同步,常见的策略如写穿透、写回等。 还要考虑数据同步机制,比如如何确保Redis和JVM缓存的一致性,可能使用消息队列或者发布订阅机制来通知缓存失效。此外,适用场景方面,这种架构可能用于需要快速访问但又能容忍一定程度数据延迟的应用,或者作为多级缓存的优化策略。 需要注意用户可能的混淆,是否将一级和二级的顺序搞反了,但按照问题描述,还是按用户说的来回答。同时,检查是否有误,比如Redis作为一级是否合理,或者是否有其他考虑因素,比如网络架构使得Redis更接近应用,成为一级缓存。 最后,总结这种架构的优缺点和使用场景,帮助用户理解何时选择这样的方案,以及需要注意的问题,比如数据一致性、缓存击穿、雪崩等常见问题,以及如何通过TTL、熔断机制等来缓解。</think>好的,我将分步骤为您解释“Redis一级缓存 + JVM二级缓存”的架构设计及其工作原理: 1. **基础概念** - **一级缓存(Redis)**:位于应用层外部,基于分布式内存数据库的缓存,所有服务实例共享同一份缓存数据。 - **二级缓存(JVM)**:位于应用进程内部(如堆内存),通过HashMap、Ehcache、Caffeine等实现,仅当前实例可见。 2. **数据访问流程** ``` 请求 → 检查JVM缓存 → 命中则返回 ↓ 未命中 检查Redis缓存 → 命中则返回并写入JVM ↓ 未命中 查询数据库 → 写入Redis → 写入JVM → 返回 ``` 3. **核心优势对比** | 特性 | Redis一级缓存 | JVM二级缓存 | |---------------|----------------------------------|----------------------------| | 数据一致性 | 全局一致 | 单机一致,集群可能不一致 | | 访问速度 | 微秒级(网络IO开销) | 纳秒级(内存直接访问) | | 容量 | 十GB~百GB级 | MB~GB级(受堆内存限制) | | 数据共享 | 跨进程、跨机器共享 | 仅进程内有效 | 4. **同步策略设计** - **写操作处理**: ```java public void updateData(Data newData) { // 先更新数据库 db.update(newData); // 删除两级缓存(保证后续读取加载最新值) redis.delete(key); localCache.invalidate(key); } ``` - **过期控制**: - Redis设置TTL(如30分钟) - JVM设置更短的TTL(如10分钟)避免长期脏数据 5. **性能优化技巧** - **JVM缓存预热**:服务启动时批量加载热点数据 - **Redis布隆过滤器**:防止缓存穿透 - **分级TTL策略**: $$ TTL_{JVM} = \alpha \cdot TTL_{Redis} \quad (0 < \alpha < 1) $$ - 异步刷新:后台线程主动更新临近过期的热点数据 6. **典型应用场景** - 高并发读(>10k QPS)且数据变更频率较低的场景 - 电商商品详情页 - 新闻类内容展示 - 地理位置信息查询 7. **风险控制措施** - **熔断机制**:Redis故障时自动降级为纯JVM缓存 - **限流策略**:使用令牌桶控制数据库访问量 - **监控指标**: ```mermaid graph TD A[缓存命中率] --> B(Redis命中率>85%) A --> C(JVM命中率>95%) D[延迟分布] --> E(Redis P99<2ms) D --> F(JVM P99<100μs) ``` 8. **数据一致性保障** - 最终一致性模型: 1. 数据库更新成功后 2. 通过Redis Pub/Sub广播失效指令 3. 各节点监听通道并清除本地JVM缓存 $$ \text{不一致时间窗口} \leq T_{pub/sub} + T_{network} $$ 这种多级缓存架构需要根据业务特征进行参数调优,建议通过压力测试确定最佳TTL值和缓存容量比例。当数据更新频繁(如库存秒杀场景)时,需谨慎评估使用此模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值