线上常见问题案例及排查工具

目录

目录

问题背景:

问题摘要:

[CPU 利用率高:](#CPU 利用率高:)

OOM/内存泄露:

[GC 问题,其实也就是内存泄漏导致内存被占满,无法分配内存导致JVM触发GC:](#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博客

缓存一致性问题

缓存最终一致性

问题和解决方案:读数据时,是先读缓存,缓存没有再读数据库,再更新缓存,更新缓存一般有两种方式:先更新数据库,再删除缓存;延迟双删,先删除缓存再更新数据库,延迟一段时间再删除缓存。

缓存强一致性:

解决方案:悲观锁、乐观锁‌和‌分布式事务‌也可以用于保证数据一致性,但实现复杂度较高。

也可以结合业务实现相对强一致性,比如携程的做法。

参考:
干货 | 携程最终一致和强一致性缓存实践

一文讲透数据库缓存一致性问题

分布式缓存与DB秒级一致设计实践(最终一致性)

干货 | 分布式缓存与DB秒级一致设计实践

问题:C端爆款项目需求:高流量、部分商品会成为热卖商品、承担下单职能、让用户尽可能看到最新的信息,即:本地热点key缓存、分布式缓存(按需进行缓存)和DB一致性问题

方案:采用:client和server方式,

1、缓存访问组件client(jar包)提供给各业务方进行按需异步将缓存中需要增、删、改的键值对通过消息传递给缓存更新平台server,让其进行实际的缓存更新操作。

2、对热点key进行本地缓存与更新,避免对某个key的大量请求直接打到缓存导致缓存雪崩。

3、缓存更新都是发生在缓存更新平台server,所以其可以将发生变化的缓存key通过消息队列广播给所有缓存访问组件,组件消费到这条消息后,若key是热点key,则进行本地缓存的更新。

4、本地缓存设置很短的失效时间。

5、计算key的hash值,然后对其取模,可以将相同的key分配到相同的线程处理。

6、旧值覆盖新值的问题,采用缓存版本的概念来解决这个问题,我们认为每条缓存的数据都应该有一个版本号(业务提供,例如可以是修改数据的时间戳,只要满足单调递增即可)。基于此,缓存的增、删、改操作全部基于这个版本号来进行判断是否执行操作,消息版本号必须大于缓存中的版本号才进行增改操作,消息版本号必须大于缓存中的版本号才进行删除操作。

7、缓存访问组件client(jar包)内部会将每条消息记录到业务DB。缓存更新平台server通过业务提供的接口增量轮询该表,确保所有消息都被及时消费掉。

事务问题

插入数据后事务没有立马提交导致更新数据失败:

问题:往任务表中插入任务后,发送消息异步处理任务,并更新任务状态失败。

解决方案:由于插入任务和发送消息操作在一个事务里,消费者收到任务消息后,会立马处理并更新状态,由于事务还没提交,导致更新不到该任务数据,导致更新状态失败;解决办法是在发送消息前先提交插入任务的事务。

详细参考:

知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具

知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具

知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具

如何进行GC调优-CSDN博客

MySQL - 性能优化-CSDN博客

Java服务,CPU100%问题如何快速定位?_java服务,cpu100%问题如何快速定位?-CSDN博客

Java服务,内存OOM问题如何快速定位?_java oom 如何确定sql-CSDN博客


相关推荐
我的运维人生1 天前
Docker实践与应用举例:构建高效开发与部署环境
docker·容器·eureka·运维开发·技术共享
Linux运维老纪1 天前
python文件夹显示在桌面(Python Folder Displayed on Desktop)
linux·服务器·开发语言·网络·python·云计算·运维开发
wuweihao1234562 天前
DataX与DataX-Web安装与使用
数据仓库·运维开发
软考通2 天前
软考高级难度排名,哪个科目容易过?
网络·职场和发展·系统架构·运维开发·集成学习
凌鲨3 天前
DevOps工程师成长路线图
运维·运维开发·devops
kuankeTech5 天前
解读外贸制单软件:功能全面,操作便捷
大数据·产品运营·运维开发·软件开发·erp
学习编程之路5 天前
【Linux】深入理解进程管理与fork系统调用的实现原理
linux·运维·服务器·运维开发·进程
云原生生态圈6 天前
Jenkins 中自定义Build History中显示构建信息
运维·jenkins·运维开发·自动化构建