java实战场景
2023-05-19 21:24:40 0 举报
AI智能生成
java场景问题总结,数据库,缓存,消息队列,系统架构设计,系统承载能力,系统调优总结;
作者其他创作
大纲/内容
分支主题
两者一样快,两者最终执行的时候语句是一样的。但是比count(列名)快
直接遍历索引来查询;
1、如果是innerdb 数据库引擎
count(列名)只统计不为null的数据,需要遍历整个表
2、myisam数据库引擎
count(*)和count(1) 谁快
数据库
1、在同一个时刻,每个cpu核心只能运行一个线程,如果有多个线程需要执行,cpu只能是通过上线文切花的方式来执行不同的线程
记录线程执行的状态
让处于等待中的线程执行
cpu上下文切换做的事情
过多的上下文切换,占用了过多的cpu资源,导致无法去执行用户进程中的cpu指令,导致用户进程中cpu相应速度过慢
1、文件io操作
2、网络io
3、锁等待
4、线程阻塞
上下文切换过多的场景
多查询一些线程,是否处于阻塞状态中
统一给这些io设置线程池,不让他们占用过多的系统资源
如果是io导致,不管是网络,还是文件,
解决方式
1、cpu上线文切换过多
1、创建了大量线程
2、有线程一直占用cpu资源,死循环
过度消耗原因
2、cpu资源过度消耗
导致cpu使用率飙升的原因
1、通过linux系统的top命令来找到cpu利用率过高的进程
查询进程信息,查看线程
2、ps -mp 进程号
通过jsatck工具根据线程id去下载线程的dump日志,然后定位到具体的代码
cpu消耗过高的是同一个线程
结果1、程序正常,在cpu飙高那一刻,用户访问量增大
做针对性调整
结果2、也有可能程序不正常,线程池的创建相关有问题。
说明线程创建的比较多,拿几个线程id去下载线程的dump日志,
cpu消耗过高的是不同的线程
出现两种情况
2、shirt +H来找到进程中CPU消耗过高的线程
linux系统解决
1、查看docker的cpu占用率:
docker stats
2、进入cpu占用高的docker容器
docker exec -it 容器编号 /bin/bash
查看容器中具体进程cpu占用率,执行top
查看进程中线程cpu占用率:top -H -p 进程号
将异常线程号转化为16进制: printf “%x\” 线程号
查看线程异常的日志信息:jstack 进程号|grep 16进制异常线程号 -A90
docker
各种监控系统辅助我们排查
CPU突然飙升,系统反应慢,怎么排查?
让jvm发生内存溢出的时候自动生成dump文件,并且存放在某个固定路径下面
1、通过设置jvm参数
2、通过执行dump命令来获取当前jvm的内存快照
1、先获取堆内存快照文件
专门用来分析dump文件的
1、mat工具
2、jprpfile工具
dump文件分析工具
2、通过mat工具分析dump文件
3、解析dump文件,得到内存统计信息总览
dump文件中内存占用情况
4、点击dominator Tree
5、获取到内存占用最高的类
根据代码的情况惊醒调整
6、然后再查看这个类中相信的内存占用情况
排查问题方式
线上服务内存溢出如何定位排查
性能调优
1、Unsafe类计算
2、使用<dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-core</artifactId><version>4.0.0</version></dependency> 包下面的 RamUsageEstimator 类种的方法计算
1、通过工具类计算
1、对象头数据,实例数据,对齐数据
1、计算对象实体数据
子主题
计算对象都数据,数组长度,实体数据,对齐数据
2、计算数组数据
1.对象的属性;(定义多少字段,需要分配相应的字段) 如果没有字段,内存分配这个也是没有的;2.对象头;(固定大小的12个字节,这是64位操作系统,32位系统的是4个字节)3.对齐数据(假如一个对象的实际存储空间的数据,不是8位的整数倍, 但是需要填充空位,填充到8 的整数倍,(这个是64位的虚拟机)) 对齐数据 有时候 有,有时候没有
2、通过手动计算
怎么计算一个对象的内存大小?
可以,造成堆内存的线程会被销毁,所以这个线程创建的内存对象也会被回收,其他线程还是可以获取到充足的内存空间来支持程序运行
内存溢出其他线程还能工作吗?
内存
场景类
代码复用优化
业务调优
设计层面优化师优化手段的上层,根据系统的难点和潜在的问题,评估设计出合理的设计方案
缓存使用
常量池
连接池
线程池
对象池设计
IO优化,选择多路复用
消息队列根据具体业务,设置对应的确认机制
使用序列化效率高的组件
零拷贝
io多路复用
具体方式
设计层面
减少对象的创建,减少内存消耗
减少对象的频繁创建
单例设计模式
合理使用设计模式
字符串操作避免内存泄露
切割查找选择最快方式
字符串优化
根据当前业务查询多还是修改多,合理选用对应的容器
局部变量的容器,选择对应的操作快速的线程不安全的容器
容器的初始化长度可以,根据预估的数据量
容器优化
位运算替代除法操作
避免二维数组使用
GC不会回收静态方法占用的元空间数据
减少静态方法
对象需要开辟堆内存,基本类型需不要堆内存
满足业务的情况下使用基本类型替代对象
代码中减少对第三方循环调用,第三方提批量接口
合理多使用switch 语句 替代if else
时间复杂度,空间复杂度
合理的使用空间换时间,或者时间换空间的思想
锁对象使用合理
乐观锁
降低资源竞争
io操作释放对应的资源
代码中可以手动将不再使用的大对象手动回收
同步变成异步
算法优化
针对搜索时间,内存大小设计合理的数据结构
代码层面优化方式
代码层面
可以调整减少执行次数,再进行热点编译,针对一些执行频率很高的代码,可以在前期优化时间;
编译优化
根据对内存,cpu告警信息做适当调整
cpu核数,内存调整
JVM层面
1、合理设置索引
2、合理的提前生成统计数据
3、连接池优化
4、减少关联查询
sql优化
1、合理分库分表
2、合理冗余字段
库表结构优化
内存更大, 磁盘更大, cpu核数更多,运算速度更快
数据硬件优化
数据库日志刷盘策略
缓冲池大小
数据库参数优化
数据库层面
会影响io多路复用
文件最大句柄数量
操作系统层面
调优层面
95线,99线,999线
cpu时间
内存分配
磁盘吞吐量
网络吞吐量
并发量
优化评定指标
明确调优目的,不要为了调优而调优,盲目调优可能适得其反
调优前注意事项
大的分类
调优
信息摘要算法5
概述
单向不可逆,无法解密
产生一个固定长度的散列码
特点
密码存储
信息完成性校验
使用场景
MD5
散列消息鉴别码
HMAC系列
SHA系列
加密方式
不可逆特点
不可逆加密
公钥加密,私钥解密
RSA
DSA/DSS
非对称加密方式
1.比对称加密更加安全可靠;
2.加密解密比对称加密慢很多
HTTPS建立SSL连接中有一个步骤就是,将服务端的公钥加密用于对称加密的密钥;也就是把数据传输的对称加密的密钥发送给服务端,这样客户端和服务端都知道了实际做数据传输的对称密钥了
非对称加密
加密解密都是用的同一个密钥
DES
3DES
AES
对称加密方式
1.加密解密速度快;
2.适用于大量数据加密
3.由于需要把密钥传递给需要用的对接方,在传递的密钥的过程中容易被人抓包,造成数据不安全;
HTTPS建立SSL连接之后,客户端和服务端使用的就是对称加密来传输数据;主要就是利用对称加密解密速度快的优势
场景
对称加密
对称加密性能高于非对称加密
性能
非对称加密安全性比对称加密安全性高
安全性
对称加密,加密解密都是用的同一个密钥;非对称加密,加密解密密钥是分开的;
加密解密方式
对称/非对称加密区别
可逆加密
可逆:可以通过解密的密钥获取到加密之前的数据;不可逆:数据是没有办法做解密操作,无法拿到解密之前的数据;
可逆/不可逆区别
增加数据被破解的难度
让相同的数据在被加密之后显示的数据是不一样的;
作用
加密盐
数据加密算法
查询和修改操作都是幂等;添加和删除不是幂等
分布式环境下怎么保证接口的幂等性
1.用户对于同一操作发起的一次请求或者多次请求结果都是一样。
天然幂等的
查询操作
删除一次和删除多次可能返回的数据不是一样
删除操作
如果把某个字段的值设置为1,那么不管多少次都是幂等
如果把某个字段值加1,那么就不是幂等
更新操作
重复提交会有幂等性问题
添加操作
具体操作
幂等性概述
一般场景:代码中通过代码逻辑判断
支付场景:通过唯一的订单id来判断重复
1.通过代码逻辑判断实现
支付场景:页面跳转的时候获取全局唯一的token,把token缓存起来;等到提交到后台的时候,再校验token是否存在,第一次提交成功就会删除token
使用场景:分布式环境
2.使用token机制实现
分布式环境中,做重复校验,直接用乐观锁实现
3.乐观锁
幂等性解决方案
接口幂等性
通过Redis的key的事件通知机制
实现方式
Redis的事件通知,消息只会发送一次,不管客户端是否有收到(无消息确认机制)
缺点
需要对某些key的改变做监控
redis
RocketMQ设置固定延时时间发送消息
延时的消息时间只能是十几个固定的延时时间
RocketMQ
1.正常队列不设置消费者,给单个消息设置超时时间;
2.设置死信队列和死信队列的消费者,把超时未发送的消息发送到死信队列;
RabbitMQ
时间轮定时任务
kafka
消息队列
先对数据先进行落库,再对数据库进行定时轮询
对数据库压力大
定时任务
实现Java的延时队列,这是固定的延时时间;
java延时队列
延时任务
验证码
NGINX
Tomcat
容器限流
队列
手动控制某些接口访问
gateway
Semaphore类控制并发访问数量
任务线程池控制方式
锁
服务器限流
限流方式
实现
令牌桶
漏斗桶
滑动窗口
计数器
子主题 5
限流算法
子主题 2
子主题 3
流量计算方式
接口限流
缓存
1.读多写少
限流
负载均衡
异步
2.高并发
3.资源访问冲突
秒杀系统的特点及对应解决方式
点击之后,一段时间之后才能再次被点击
按钮控制
需要收到验证码才能点击
验证码控制
控制方式
降低用户秒杀系统的点击频率
控制目的
应用层
CDN
网络层
负载均衡,反向代理,限流
降低服务器访问压力
负载层
缓存,异步,队列,限流,原子操作
降低对数据库的压力
服务层
乐观锁,悲观锁
不同层级对应解决方案
秒杀系统设计
CDN作用
lua脚本为啥可以原子性执行
高并发问题
List集合
banner
三个豆腐块
为你推荐条件
首页
Hash表
骨架星级
注册地
车龄
里程
车辆级别
排放标准
车源属性
燃料类型
筛选条件
场次
拍场模式
所有品牌车系
热门品牌
业务城市数据
查询条件
查询结果集
竞拍大厅
通过zset排序用户出价的品牌车系;每次出价的时候异步去更新redis的记录以及数据库
为你推荐
车源id存放在Zset缓存,从缓存中拿到车源id;再去ES中获取数据;
心愿单的查询条件
心愿单
一个月前未中标
近一个月未中标
交易失败
交易成功
我的出价
String
账户余额
保证金
我的账户
做自增操作
INCR
出价次数
关注次数
数据存入缓存
redis缓存
1.按照不同的业务划分不同的项目
同一业务下的定时任务模块
直接查询属于本业务的库
直接调用本业务的原子微服务
直接调用本服务的非原子微服务
获取本业务数据
1.调用其他原子的微服务
2.调用其他服务的非原子微服务
获取其他业务数据
Job
同一业务下消息队列模块
MQ
同一业务下pc端接口
web
同一业务下对APP接口
App
同一业务下原子的微服务接口
Ms
同一业务下非原子的微服务接口
可以聚合其他业务的原子的微服务接口
Api
2.同一业务下不同的功能划分
模块拆分方式
微服务项目
定义:测试各个逻辑是否正确
更早发现问题
缩短产品周期
更早发现底层问题
目的
接口压测
逐步增加系统租在,测试系统的性能变化,并在最终满足性能指标的情况下,测试系统所能承受的最大负载测试
定义
检测系统运行的最大上限,使系统能够在最大压力下正常运行。获取最大上限数据
目的:
不断地在单位时间内增加请求个数,直到服务器的某一个资源(cpu,内存,网络延迟,等指标)达到饱和或者临界值
方法:
这个是最重要的指标
并发用户数
持续运行时间
数据量
系统负载指标
负载测试
检查系统是否有并发问题导致的内存泄露,线程锁,资源争用问题
确定用户并发数,必须直到系统锁承载的在线用户数,然后在单位时间内 同事发起一定量的请求
方法
login seesion 数量* session 时间 / 考察时间段
平均并发用户数量
平均并发用户数量 + 3(平均并发用户数量 开平方根)
巅峰用户数量
一个oa系统有3000个用户, 平均每天大约有400人访问系统, 一天之内用户从登录到退出系统平均时间4小时,一天时间内,用户只在8个小时使用该系统
400*4/8 = 200
平均并发用户量
200+ 3(200的整数平方根)
demo
确定用户数的方法
并发测试
不断给应用增加并发数量,强制让其在极限情况下运行,观察其运行到何种程度,从而发现其性能缺陷
查看系统能够承受的最大并发量,达到多少的时候系统才会奔溃
以负载测试,并发测试为依据,
接口响应时间
接口并发数量
内存,cpu,等指标
指标
压力测试
1、通过负载,并发,压力测试,可以知道系统的极限,可以更加合理的设置系统的各项告警阈值
2、核心,频繁使用的接口,不做相关的测试,贸然上线,对系统的稳定性是一个极大的考验,并不知道接口,相关的极限值,容易把系统搞崩
内存,cpu数量和接口响应数据,并发数所呈现的函数关系,可以方便调节系统的内存,cpu
3、获取到相关运行数据,可以方便之后的调优
收获
测试
系统在各项指标比如说cpu,内存在极限状态下所能达到最大的QPS,吞吐量,并发量,用户的最小响应耗时
什么是承载能力
通过相同的机器数量可以得到该服务的总的最大并发数量
如何计算系统的承载能力
系统峰值呈现28原则, 百分之80的流量会在20%的时间内到达
峰值qps= PV* 80% /(60*60*24*20%)
pv = page view 页面浏览次数
uv = unique vistor 不同用户访问数量
如果业务方可以给出
根据往年的数据量,做出相应的评估结合当下的数据
访问量数据
机器数量 = 峰值qps / (单台压测得到的极限的qps)
机器数量评估
评估系统的承载能力
服务器台数,服务的硬件,核数,jvm内存设置,磁盘大小,垃圾回收怎么设置
需要单台服务器极限下的qps
当前单台机器的qps
当前水位 = 当前所有机器的总的qps / (单台机器的极限qps *服务器数量 )
系统当前水位评估
正常的使用率0%-75%之间
90%及以上cpu使用过高
cpu使用率
一天有1000w点击量
1000*10000 *80% / (60*60*24*20%) = 450
峰值的qps = 450
二八原则估算
qps峰值估算
qps的估算需要结合接口的99线数据,已经cpu的核数,服务器个数
获取单个接口的平均响应时间,1s除以这个99线,得到1s中单个cpu的qps,乘以cpu的总核数,乘以cpu利用率80%得到单个接口的qps数值
实际情况下,因为线程之间的切换消耗cpu资源还有cpu等待,等等原因,实际的qps数量 并不会像这种线性关系;实际还需要进行压测才行
单个接口的qps
每一个接口调用频率差别很大,每一个接口的相应时间也很大,根据调用频率还有响应时间得到一个综合状态的平均响应时间,然后再通过1s除以这个综合平均响应时间,再乘以80%,得到单个cpu的qps,然后再计算总得qps
实际情况下,还是需要通过压测得到对应的关系曲线
项目整体qps
qps估算
一般情况下 8核16gb的机器,qps可以达到300-400,如果使用了缓存,qps达到1000没有啥问题
qps计算
系统在单位时间内处理请求的数量
TPS
QPS
量化指标
请求对cpu的消耗,外部接口,网络磁盘IO
系统吞吐量要素
吞吐量
QPS*平均响应时间
系统并发数估算
需要结合qps的峰值,以及平均响应时间
qps的峰值2000,平均响应时间50ms
2000/((1000ms/50ms)*80%)/ 4 = 2000/16 = 128核
计算中间需要加上cpu 饱和使用率 80%
实际情况下:128核 服务器,可以拆分成几个32和或者几个16核的服务器;可以做对应的服务器拆分;
demo:
cpu核数估算
是密切相关的,可以根据qps数量,平均响应时间预估得到cpu总的核数,然后再根据核数可以拆分成几台对应的服务器
服务器的台数和cpu核数估算
根据系统的qps,每一个请求会占用多少内存,机器的选型,年轻代应该给多少,老年代给多少,对应进入老年代速度,Full gc的频率
尽量让对象都保留在年轻代,不让进如老年代,减少full gc
预先预估
根据压测环境JVM运行状况,如发现对象过快进入老年代,就需要采用之前介绍的优化思路,合理调整新生代、老年代、Eden、Survivor各个区域的内存大小,保证对象尽量留在年轻代,不要过快进入老年代。当对压测环境下的系统优化好JVM参数之后,观察Young GC和Full GC频率都很低,此时就可部署上线
系统压测jvm优化
天高峰、低峰期观察线上系统的JVM运行是否正常,有无频繁Full GC的问题。有就优化,没有就平时每天都定时或每周都看看
监控jvm内存
jvm内存预估
系统承载能力评估
全文索引的分词,怎么保证分词搜索的准确性;是否有算法参与;
kafka节点挂了
消费者消费速度过慢,生产者生产速度过快
导致原因
如果有,需要针对型处理
1、检查生产者发送消息是否正常,是否有大量重复消息,或者不必要发送的消息;检查消费者消费是否正常,是否有性能瓶颈,
1、开启多线程消费
2、添加消费者服务器节点
3、批量拉取消息
4、消息消费快速入库,快速返回成功,开启后台线程去消费消息;
2、提高消费者消费速度
消息堆积
消费者未能提交正确的消费成功消息,或者服务端没有接受到此消息
代码逻辑中保证幂等性,使用消息id,获取业务id来做幂等性判断;
消费重复消费
生产者自己异常,导致消费发送失败,生产者会有重试机制,重新发送消息
消息重试机制
ACK =0
ACK=1
ACK=all
消息确认等级
设计上是比较折中的在一定程度上能够保证消息的不丢失,也能保证一定的吞吐量
ACK=1(默认设置),只要分区的leader副本接受到了消息,就会给生产者发送消息接受成功(其他副本再回去同步leader的数据)。
缺点:网络宕机的时候,消息会丢失
优点:满足大吞吐量的数据发送;
ACK=0,生产者给服务端发送消息,不等服务端是否有接收到消息,发送完了就认为消息到被服务端接受了;而实际情况是,消息会生产者的缓冲池中待一段时间然后才会被发送到服务端,生产者就不知道消息具体在啥时候发送到服务端;
优点:最大程度上保证服务器节点接受到消息;
缺点:极度影响性能,导致数据的吞吐量低
ACK=all,消息的分区leader还有所有的副本都接受到消息,才会给生产者发送消息已经被接受了。
消息确认相应机制
消息确认机制
RoekctMQ 事物机制来确保消息都能发送到节点上
消息重投机制,消息投递失败,会再次投递
将生产者消息发送设置成同步发送,根据发送结果来判断是否需要做重复发送,最大程度保证消息发送到了服务器
发送端把消息发送个服务端,服务端接收到消息并且把消息持持久化到磁盘就会给发送端一个异步的confirm应答
1、消息确认机制
发送端开启一个事物,再推送消息,如果投递失败;进行事物回滚,然后重新发送消息;如果服务端收到消息,发送端就提交事务。
2、事物消息机制
rabbitMq
1.ACK=all,让所有的服务器节点都获取到数据;
参数设置:min.insync.replica
2.合理的设置从机的个数,设置数据的备份
参数设置:unclean.leader.election.enable=false 本身默认就是flase
3.禁止主机挂掉,选从机作为新的主机
将异步持久化到磁盘,修改成同步持久化到磁盘
RocketMq消息本身默认的持久化盘刷机制是异步刷盘
1、修改节点的消息持久化刷盘机制
确保主机挂了,从机上还有消息备份数据
2、RocketMQ采用主从机构
rocketMq
服务端设置交换机,队列,数据的持久化;服务器宕机后,重启会读取磁盘上的持久化的数据;
问题:由于消息的持久化是一批的持久化,可能宕机了,这一批数据还持久化到磁盘
1、服务端收到生产者发送过来的消息,会做消息的持久化
2、当服务宕机后, 会从磁盘当中读取相应的消息,最大程度上保证消息不在服务节点上丢失
消息的持久化
使用confirm机制,可以让发送者的数据持久化到磁盘,再确认
rabbitMQ
也可以尝试修改刷盘策略
服务端保存消息失败(写入磁盘失败)
设置消息消费提交偏移量为手动提交偏移量,通过代码在finnaly里面手动设置消息异常的那个前一条的偏移量做提交;
需要正确提交自己的消费正常的偏移量
ack机制,设置消息成功消费之后,再通知节点消息已经成功消费
1.消费者在处理消息的时候出现异常了,那么这条消息就是没有被正常的消费;如果不采取措施,那么这个消息就会丢失
丢失原因
消息只有正常消费后,反馈给服务端;服务端才会从队列里面把该条消息删除
ack机制概述
不会发送ack确认消息
不确认
服务端发送完消息就自动认为该消息被成功消费
缺点:由于网络原因,造成数据从服务端发送到消费者消息丢失
自动确认
消费者消费成功之后,显示的给服务端ack信号;服务端只有收到该信号才会把数据从队列里面删除
设置手动ack,尽可能减少消费端的数据丢失问题;正常就是发送ack,异常就记录日志,然后发送nack
手动确认
ack机制三种模式
如果消费者异常没法发ack消息,服务端会认为这些数据都是没有被正常消费;就会堆积在队列当中,造成内存没法回收,内存泄露;
内存泄露
1.设置手动应答,如果异常,捕获异常记录日志,给服务端发送正常消费;
2.设置重试次数(默认是3次,三次不消费成功就会放入到默认的死信队列)
内存泄露解决方案
ack机制弊端
ack机制默认打开,而且是自动确认
ack机制
解决方案
消费者消费消息,都应该向服务器节点正确提交消费的实际情况;
统一概述
消费方消费丢失
消息丢失原因
消费丢失
1、将前后有逻辑的消息合并成一个消息,发送,这样不管消费者是多个,不管消费者有几个线程,消费的时候都能保证消费是顺序被消费
2、同一个topic下,将有先后逻辑顺序的消息发送到同一个队列(rocketMQ,rabbitMQ),或者同一个分区(kafka),且保证消费者只有一个也只有一个线程来消费
通用方式
1、将有前后逻辑顺序的消息发送到同一个分区
1、一个topic只有一个分区,对应的也只有一个消费者
1、将有前后逻辑顺序的消费发送到同一个topic下的同一个队列,且保证消费方只有一个线程去消费
某个Topic下的所有消息都要保证顺序
2、设置发送的消息是全局顺序消息
RocketMq
RabbitMq
消息顺序消费
共性问题及解决思路
java实战
0 条评论
回复 删除
下一页