这是一篇对最近做centos服务器运维和java应用性能优化的总结。
一,压测
无测试,不优化。优化之前要先对应用和服务器进行压力测试,
分析出影响性能的瓶颈在哪,然后进行针对性的优化,才能达到想要的结果。
工具:
1:jmeter:压力测试工具。
2:zabbix:服务器资源监控工具。
分析:
在压测过程中主要分析服务器的以下指标:
1:cpu
2:内存
3:网络带宽
4:磁盘io
以上因素是影响服务器性能的关键因素。(centos中ulimit -a 里面的配置也很重要)
在测试过程中要观察各项指标的表现并做好记录,然后调整不同大小的并发数,看各项指标的变化,即可分析出性能瓶颈卡在那个指标。
比如:cpu在80%时,网络带宽能达到200M,cpu在100%时,网路带宽只能到150M左右,那就说明瓶颈在cpu。
如果架构是分布式的,也可以测试过每个服务器的性能和能承载的最大压力,这样就能做到心里有谱:如果想要达到什么样的性能,需要加什么样的资源配置。
二,优化
2.1 如果瓶颈在cpu
一般是我们的架构或者程序上的问题。
可以输入top命令查看是哪个资源在占用cpu。
如果是数据库:1,优化sql。
2,优化索引。
3,考虑缓存(redis)。
如果是程序:那就要好好分析分析代码 是哪里浪费了时间,在进行优化。
1,检查一下是否有无线循环?
2,大循环里用“+”拼接字符串?
3,在大循环里又查询数据库?
4,是否有比较复杂的计算?
5,用jvisualvm看一下是否在频繁的进行gc。
如果是第三方组件:
1,单独测试一下组件的性能。
2,换一个性能更好的组件。
3,改成分布式集群模式。
2.2 如果瓶颈在内存
也可使用top命令查看谁在占用内存。
2.3如果瓶颈在网络带宽
那就增加带宽吧。
2.4 如果瓶颈在磁盘io
看是不是考虑换个固态?
三,缓存
缓存一般是提高应用及服务器性能的一大利器。
前端可以利用nginx做动静分离。
后端一般就用redis做缓存。
3.1哪些数据适合缓存?
1:及时性,数据一致性要求不高
2:访问量大且更新频率不高的数据(读多,写少)
3.2 本地缓存
1,什么是本地缓存
缓存组件(Map)跟代码在同一个进程,同一个jvm里.
2,本地缓存的问题
2.1 如果是单体应用,就没什么问题。
2.2 如果是分布式系统下 请求被负载均衡到不同的节点里 那么都会自己在 查一遍数据库,然后更新自己的缓存。
2.3 如果数据被修改,会造成数据不一致性。
3.3 分布式缓存
每个服务器都去同一个地方去数据,更新数据也是更新同一个地方
3.4 缓存失效时间?
如果不定义缓存失效时间 需要考虑redis中的数据和数据库数据的一致性。
缓存数据一致性(高并发下会出现问题):
1:双写模式:修改数据时,也要修改缓存。
2:失效模式:修改数据之后把缓存删掉。等待下次主动查询,更新。
如果定义缓存失效时间 需要考虑高并发下缓存失效的问题。
缓存穿透:
指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是
数据库也无此记录,我们没有将这次查询的null写入缓存,这将导致这个不
存在的数据每次请求都要到存储层去查询,失去了缓存的意义
风险:
利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃
解决:
把结果为null的也加入缓存,并加入短暂过期时间(几分钟)
缓存雪崩
缓存雪崩是指在我们设置缓存时key采用了相同的过期时间,
导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时
压力过重雪崩。
解决:
原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这
样每一个缓存的过期时间的重复率就会降低,就很难引发集体
失效的事件。
缓存击穿
缓存穿透:
• 对于一些设置了过期时间的key,如果这些key可能会在某些
时间点被超高并发地访问,是一种非常“热点”的数据。
• 如果这个key在大量请求同时进来前正好失效,那么所有对
这个key的数据查询都落到db,我们称为缓存击穿。
解决:
加锁(分布式锁)
大量并发只让一个去查,其他人等待,查到以后释放锁,其他
人获取到锁,先查缓存,就会有数据,不用去db
可以引入SpringCache做分布式缓存。
也可以用Canal:canal 是一个组件,角色似于数据的从数据库(主-从模式的从)
spring-cache的不足:
1:读模式:
缓存穿透:查询一个null数据。解决:缓存空数据:spring.cache.redis.cache-null-values=true
缓存击穿:大量的并发进来同时查询一个正好过期的数据。解决:加锁。spring-cache默认无加锁,想要加锁给@Cacheable
设置参数:sync = true
缓存雪崩:大量的key同时过期。解决:加过期时间。spring.cache.redis.time-to-live=60000
2:写模式(缓存与数据库一致)
读写加锁。
引入canal:感知到mysql的更新,去更新数据库。
读多写少:直接去数据库查询就行。
总结:
常规数据:读多写少,及时性,一致性要求不高的数据;完全可以使用spring-cache。(写模式:有过期时间就足够了)
特殊数据:特殊设计。
原理:缓存管理器-->缓存组件-->管理缓存
四,jvm内存浅析
可以使用jdk1.8自带的jvm监控工具jvisualvm
在cmd窗口输入:jvisualvm
堆:
存储程序运行过程中创建的对象。
Metaspace:元空间,直接操作物理内存。
Eden: 新生代区。
S0+S1:幸存者区。
Old:老年区。
流程:
新创建的对象会放到Eden区:
放得下:就放。
放不下:会对Eden区进行一次小GC,没被回收掉的会被放到幸存者区。
GC之后: 放得下:就放。
放不下:放到老年区。
Old:放得下:就放。
放不下:大GC(很耗时)
大GC之后Old:
放得下:就放
放不下:内存溢出
S0+S1幸存者区:存储Eden区进行gc之后存活的对象,
如果多次gc都存活的对象会被搬到Old区。
总结:jvm内存调优要尽量减少GC的执行次数,
频繁的进行GC是很耗时的(很占用cpu资源)需要优化代码或者适当的增加堆内存的空间。
-Xmx 最大占用内存
-Xms 初始内存
-Xmn Eden+S0+S1区(新生代区加幸存者区)