线上常见问题案例及排查工具
目录
目录
问题背景:
问题摘要:
CPU 利用率高:
OOM/内存泄露:
GC 问题,其实也就是内存泄漏导致内存被占满,无法分配内存导致JVM触发GC:
MySQL死锁问题:
慢SQL、慢查询
消息队列消息积压
分布式锁使⽤不当导致超卖:
缓存雪崩,缓存穿透、缓存击穿,热key,大key
缓存一致性问题
详细参考:
问题背景:
你在做这个项⽬的时候遇到了什么问题?( OOM 问题、 GC 问题等等)你⽤过哪些分析定位 Java 故障 / 性能的⼯具?( JDK ⾃带⼯具、 MAT 、 Arthas 等等)如果项⽬遇到了 OOM 问题,你会如何排查?(常⽤ MAT )有什么办法可以监控到 JVM 的实时运⾏状态?( Arthas )⽣产环境有⼀个接⼝很慢,如何排查?(Arthas、Cat、 Pinpoint、Skyworking )CPU100% ,你是怎么处理的?( jstack 或者 Arthas )你是如何定位线上问题的?(说说⾃⼰了解的⼯具,然后根据⾯试官提示继续深⼊聊即可)项⽬中遇到了哪些线上故障,你是如何解决的?
问题摘要:
CPU 利用率高:
1、找到最耗CPU的进程(top -c),找到最耗CPU的线程(top -Hp 进程ID),jstack查看最耗CPU的堆栈信息(jstack 10765 | grep '0x2a34' -C5 --color)2.、 阿⾥开源的全能的故障诊断⼯具 Arthas :dashboard 命令查看 TOP N 线程, thread 命令查看堆栈信息。内部集成的⽕焰图⼯具 async-profiler
OOM/内存泄露:
1. JDK ⾃带的 VisualVM :使⽤ jmap 命令⽣成堆转储快照也就是 dump ⽂件,然后通过VisualVM 分析 dump ⽂件。2. MAT (Memory Analyzer Tool) : MAT 也可以分析 dump ⽂件。
GC 问题,其实也就是内存泄漏导致内存被占满,无法分配内存导致JVM触发GC:
1、Full GC问题 :公司的监控系统:大部分公司都会有,可全方位监控JVM的各项指标。
JDK的自带工具,包括jmap、jstat等常用命令:
#查看堆内存各区域的使用率以及GC情况
jstat-gcutil-h20pid 1000
#查看堆内存中的存活对象,并按空间排序
jmap-histo pid head-n20
#dump堆内存文件
jmap-dump:format=b.file=heap pid
可视化的堆内存分析工具,JVisualVM、MAT等2、Young GC问题排查(YGC频率过快、YGC持续耗时过长)
YGC问题其实比较难排查。相比FGC或者OOM,YGC的日志很简单,只知道新生代内存的变化和耗时,同时dump出来的堆内存必须要仔细排查才行。
排查思路也是:查看监控、确认JVM配置、查看代码、对dump的堆内存文件进行分析,排查大对象,正常大对象YGC多次后会晋升到老年代,不应该是YGC持续耗时过长的原因,不断的往静态变量List中添加新对象,从而引发YGC持续耗时过长的问题。
使⽤ guava cache 的时候,没有设置最⼤缓存数量和弱引⽤,导致频繁触发 Young GC对于⼀个查询和排序分⻚的 SQL ,同时这个 SQL 需要 join 多张表,在分库分 表下,直接调⽤ SQL 性能很差。于是,查单表,再在内存排序分⻚,⽤了⼀个 List 来保存数据,⽽有些数据量⼤,造成了Young GC和Old GC都非常频繁 。接口线程池满导致会在同一时间Full GC,重启后,线程数量恢复正常水位。3、JVM性能优化
通过观察 GC 频率和停顿时间,来进⾏ JVM 内存空间调整,使其达到最合理的状态。调整过程记得⼩步快跑,避免内存剧烈波动影响线上服务。 这其实是最为简单的⼀种 JVM 性能调优⽅式了,可以算是粗调吧。
MySQL死锁问题:
解决 MySQL 死锁问题的思路:1. 使⽤ show engine innodb status; 查看死锁⽇志,然后分析死锁⽇志。2. 如果第⼀种⽅式⽆法解决死锁问题,可以通过 binlog ⽇志获取锁事务所执⾏的全部SQL 。有了具体 SQL 语句,就能进⾏具体的锁冲突分析了。
慢SQL、慢查询
解决 MySQL 慢SQL 问题的思路:1. explain字段分析, 比较重要的字段有:select_type : 查询类型,有简单查询、联合查询、子查询等
key : 使用的索引
rows : 扫描的行数
type :访问类型排列结果值从最好到最坏:
system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all
一般来说,得保证查询至少达到range级别,最好能到达ref
system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现
const:表示通过索引一次就能够找到
eq_ref:唯一性索引扫描,对于每个索引键,表示只有一条记录与之匹配,常见于主键或唯一索引扫描
ref:非唯一性索引扫描,返回匹配某个单独值的所有行
range:只检索给定范围的行,使用一个索引来选择行,一般就是在where语句中出现了between、<、>、in等的查询
index:index比all快,因为index是从索引中读取,all是从硬盘中读取
all:遍历全表才能找到
慢SQL案例:
in中参数太多(分批处理);
返回的查询结果过多(分页处理);
强制指定了使用orderId索引但where条件中并没有orderId;
忘写了join的on条件导致全表扫描;
where条件下的多个条件,在不确定哪种索引是最优时,可以尝试建立不同的索引,观察语句在不同索引情况下的执行情况进行权衡;
MySQL优化器选错索引、强制走主键索引;
尽量要避免隐式转换,因为⼀旦发⽣隐式转换除了会降低性能外, 还有很⼤可能会出现不期望的结果;慢查询优化是一个长期的过程,长期有耐心!
消息队列消息积压
1、Kafka:
问题:项⽬中某 Kafka 消息组消费特别慢,导致在设置的时间内没有提交offset,kafka对这个消费组的消费者进行再平衡,一直循环往复导致消息积压,有时候在 kafka-manager 控制台看到有些消费者已被踢出消费组。该消费组在短时间内重平衡了 600 多次。
解决办法:根据业务逻辑调整 max.poll.records (一次最大拉取消息条数)与 max.poll.interval.ms (消费者处理消息逻辑的最大时间)之间的平衡点,避免出现消费者被频繁踢出消费组导致重平衡。
2、RabbitMQ:
消息延迟 : TTL + 死信队列(⽐较麻烦)、 RabbitMQ 延迟队列插件(更简单,相关阅读: RabbitMQ 延迟插件的使⽤ )消息堆积解决:增加消费者、多线程、扩⼤队列的容量、惰性队列(更灵活但消息的时效性降低,接收到消息后直接存⼊磁盘⽽⾮内存,⽀持百万级消息的存储)3、消息队列(MQ)消息堆积问题排查与解决思路-CSDN博客
- Redis
分布式锁使⽤不当导致超卖:
问题:因 Redis 分布式锁,项目中飞天茅台抢购活动超卖的重大事故。原因是用户服务响应延迟致锁失效被覆盖,非原子性库存校验。
解决方案:包括实现相对安全的分布式锁和安全的库存校验,基于LUA脚本实现原子性的get and compare,可以利用Redisson的分布式锁。redis的incr原子操作会返回操作之后的结果。
缓存雪崩,缓存穿透、缓存击穿,热key,大key
简述:Redis 线程模型、Redis 的核心数据结构的使用场景、各种缓存高并发的使用场景:缓存雪崩,缓存穿透、缓存击穿,热key,大key等_redis线程模型及使用场景-CSDN博客
缓存一致性问题
缓存最终一致性:
问题和解决方案:读数据时,是先读缓存,缓存没有再读数据库,再更新缓存,更新缓存一般有两种方式:先更新数据库,再删除缓存;延迟双删,先删除缓存再更新数据库,延迟一段时间再删除缓存。
缓存强一致性:
解决方案:悲观锁、乐观锁和分布式事务也可以用于保证数据一致性,但实现复杂度较高。
参考:
干货 | 携程最终一致和强一致性缓存实践一文讲透数据库缓存一致性问题
详细参考:
知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具
知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具
知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具
如何进行GC调优-CSDN博客
MySQL - 性能优化-CSDN博客
Java服务,CPU100%问题如何快速定位?_java服务,cpu100%问题如何快速定位?-CSDN博客
Java服务,内存OOM问题如何快速定位?_java oom 如何确定sql-CSDN博客