文档章节

从代码层面优化系统性能的解决方案

九州暮云
 九州暮云
发布于 2017/09/07 13:41
字数 2745
阅读 34
收藏 1

写在前面

上一篇 我们主要介绍了所遇到问题的五点,那么今天接下来讨论剩下的问题,我们先再回顾一下之前讨论的问题:

  1. 单台 40TPS,加到 4 台服务器能到 60TPS,扩展性几乎没有。
  2. 在实际生产环境中,经常出现数据库死锁导致整个服务中断不可用。
  3. 数据库事务乱用,导致事务占用时间太长。
  4. 在实际生产环境中,服务器经常出现内存溢出和 CPU 时间被占满。
  5. 程序开发的过程中,考虑不全面,容错很差,经常因为一个小 bug 而导致服务不可用。
  6. 程序中没有打印关键日志,或者打印了日志,信息却是无用信息没有任何参考价值。
  7. 配置信息和变动不大的信息依然会从数据库中频繁读取,导致数据库 IO 很大。
  8. 项目拆分不彻底,一个 tomcat 中会布署多个项目 WAR 包。
  9. 因为基础平台的 bug,或者功能缺陷导致程序可用性降低。
  10. 程序接口中没有限流策略,导致很多 vip 商户直接拿我们的生产环境进行压测,直接影响真正的服务可用性。
  11. 没有故障降级策略,项目出了问题后解决的时间较长,或者直接粗暴的回滚项目,但是不一定能解决问题。
  12. 没有合适的监控系统,不能准实时或者提前发现项目瓶颈。

优化解决方案

缓存优化方案

针对配置信息和变动不大的信息可以放到缓存中,提高并发能力也能够降低 IO 缓存,具体缓存优化策略可以参考我之前写的:

http://www.jianshu.com/p/d96906140199

程序容错优化方案

在这一块我要先举一个程序的例子说明一下什么才是容错,先看程序:

输入图片说明

注:

那么如果 service 层的方法调用 dao 层的方法,一旦数据插入失败,那么这种异常处理的方式是容错吗?

把异常给吃掉了,在 service 层调用的时候,虽然没有打印报错信息,但是这能是容错吗?

所谓容错是指在故障存在的情况下计算机系统不失效,仍然能够正常工作的特性。

我们拿使用缓存来作为一个案例讲解,先看一个图:

输入图片说明

这是一个最简单的图,应用服务定期从 redis 中获取配置信息,可能会有朋友认为这样已经很稳定了,但是如果 Redis 出现问题呢?可能会有朋友说,Redis 会是集群,分片或者主从,确保不会出现问题。其实我是这样的认为的,虽然应用服务程序尽量的保持轻量级是不错的,但是不能因此而把希望全部寄托在中间组件上面,换句话说,如果此时的 Redis 是单点,那么后果会是什么样的,那么随着大量的并发请求到来的时候,程序中会报大量的错误,同时正常的流程也不能进行下去了业务也可能由此而中断。

那么在此种场景下我的解决方案是,要把缓存的使用分级别,有的缓存同步要求时效性非常高,比如支付限额配置,在后台修改完成以后前台立刻就能够获得感知,并且能够成功切换,这种情况只能实时的从 Redis 中获取最新数据,但是每次获取完最新的数据后都可以同步更新本地缓存,当单点的 Redis 挂掉后,应用程序至少还能从本地读取信息而不至于服务瞬间挂掉。有的缓存对时效性要求不高,允许有一定延迟,那么在这种情况下我采用的方案是,利用本地缓存和远程缓存相结合的方式,如下图所示:

方案一:

输入图片说明

这种方式通过应用服务器的 Ehcache 定时轮询 Redis 缓存服务器更同步更新本地缓存,缺点是因为每台服务器定时 Ehcache 的时间不一样,那么不同服务器刷新最新缓存的时间也不一样,会产生数据不一致问题,对一致性要求不高可以使用。

方案二:

输入图片说明

通过引入了 MQ 队列,使每台应用服务器的 Ehcache 同步侦听 MQ 消息,这样在一定程度上可以达到 准同步 更新数据,通过 MQ 推送或者拉取的方式,但是因为不同服务器之间的网络速度的原因,所以也不能完全达到强一致性。基于此原理使用 Zookeeper 等分布式协调通知组件也是如此。

部分项目拆分不彻底

拆分前

输入图片说明

注:

一个 Tomcat 中布署多个应用 war 包,彼此之间互相牵制在并发量非常大的情况下性能降低非常明显。

拆分后

输入图片说明

注:

拆分前的这种情况其实还是挺普遍,之前我一直认为项目中不会存在这种情况但是事实上还是存在了。解决的方法很简单,每一个应用 war 只布在一个 tomcat 中,这样应用程序之间就不会存在资源和连接数的竞争情况,性能和并发能力提交较为明显。

因基础平台组件功能不完善导致性能下降

先看一段代码:

输入图片说明

注:

首先我们先不说这段代码的格式如何如何,先看功能实现,使用 Future 来做超时控制,这是为何呢?原因其实是在我们调用的 Dubbo 接口上面,因为是 Dubbo 已经经过二次封装,结果把自带的 timeout 给淹没了,程序员只能通过这种方式来控制超时,可以看到这种用法非常差劲,对程序性能造成一定的影响。

如何快速定位程序性能瓶颈

我相信在定位程序性能问题的时候,大家有很多种办法,比如用 jdk 自带的命令,如 Jcmd,Jstack,jmap,jhat,jstat,iostat,vmstat 等等命令,还可以用 VisualVM,MAT,JRockit 等可视化工具,我今天想说的是利用一个最简单的命令就能够定位到哪段程序可能存在性能问题,请看下面介绍:

一般我们会通过 top 命令查看各个进程的 cpu 和内存占用情况,获得到了我们的进程 id,然后我们将会通过 pstack 命令查看里边的各个线程 id 以及对应的线程现在正在做什么事情,分析多组数据就可以获得哪些线程里有慢操作影响了服务器的性能,从而得到解决方案。示例如下:

输入图片说明

由此可以判断出来在 LWP 30222 这个线程产生了性能问题,执行时间长达 31.4 毫秒的时间,再观察无非就是下面的几个语句出现的问题,只需要简单排查就知道了问题瓶颈。

输入图片说明

关于索引的优化

组合索引的原则是偏左原则,所以在使用的时候需要多加注意;

索引的数量不需要过多的添加,在添加的时候要考虑聚集索引和辅助索引,这二者的性能是有区别的;

索引不会包含有 NULL 值的列:只要列中包含有 NULL 值都将不会被 包含在索引中,复合索引中只要有一列含有 NULL 值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为 NULL。

MySQL 索引排序:MySQL 查询只使用一个索引,因此如果 where 子句中已经使用了索引的话,那么 order by 中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

使用索引的注意事项

以下操作符可以应用索引:

  • 大于等于
  • Between
  • IN
  • LIKE 不以 % 开头

以下操作符不能应用索引:

  • NOT IN
  • LIKE %_ 开头

索引技巧

同样是 1234567890,数值类型存储远比字符串节约存储空间。 节约存储就是节约 IO,减少 IO 就是提升性能 通常对数字的索引和检索要比对字符串的索引和检索效率更高。

使用 Redis 需要注意的一些点

  • 在增加 key 的时候尽量设置过期时间,不然 Redis Server 的内存使用会达到系统物理内存的最大值,导致 Redis 使用 VM 降低系统性能
  • Redis Key 设计时应该尽可能短,Value 尽量不要使用复杂对象。
  • 将对象转换成 JSON 对象(利用现成的 JSON 库)后存入 Redis,
  • 将对象转换成 Google 开源二进制协议对象(Google Protobuf,和 JSON 数据格式类似,但是因为是二进制表现,所以性能效率以及空间占用都比 JSON 要小;缺点是 Protobuf 的学习曲线比 JSON 大得多)
  • Redis 使用完以后一定要释放连接,如下图示例:

输入图片说明

不管是返回到连接池中还是直接释放掉,总之就是要将连接还回去。

关于长耗时方法的拆分

我们拆分长耗时方法的一般技巧是:

  • 寻找业务的冗余点,代码中有很多重复性的代码,可以适当简化。
  • 检查库表索引是否合理加入。
  • 利用单元测试或者压力测试长耗时的操作进行算法级别优化,比如从库中大批量读取数据,或者长时间循环操作,或者死循环操作等等。
  • 寻找业务的拆分点,根据业务需求拆分同步操作为异步,比如可以使用消息队列或者多线程异步化。

经过以上几个分析后如果方法执行时间仍然非常的长,这样可能就是业务方面的需求使然,如下图:

输入图片说明

那么我们是否可以考虑将一个长耗时方法进行拆分,拆分为多个短耗时方法由发起端分别调用,这样在高并发的情况下不会造成某一个方法的长时间阻塞,在一定程度上能够提高并发能力,如下图:

输入图片说明

转自:从代码层面优化系统性能的解决方案

本文转载自:

共有 人打赏支持
九州暮云
粉丝 56
博文 142
码字总数 81160
作品 0
海淀
高级程序员
私信 提问
有了CPU平台才做得了AI转型?英特尔认为既有IT基础一样能行!

     亟待人工智能转型的企业,往往首先面临在技术硬件上的选择:   实际应用中,特别是在解决复杂问题时,不少人认为,需要先建立CPU平台,在此硬件基础上来实现路径的融合搭配,这是...

大数据文摘
06/29
0
0
银行科技部门质量建设规划交流之一

银行科技部门质量建设规划交流 浙江出差回来已一周多,整天忙着编写该客户的质量规划建设方案,以便两周后在跟客户交流,如何根据上周与客户领导交流咨询挖掘客户痛点进行质量管理规划建设。...

泊涯
2017/06/25
0
0
阿里七层流量入口 Tengine硬件加速探索之路

Tengine在软件层面已经有了深度的调试和优化经验,但是在硬件层面,通用处理器(CPU)已经进入了摩尔定律,有了瓶颈。而在业务量突飞猛进的当下,如何利用硬件来提升性能,承载双11等大型活动...

樰篱
05/29
0
0
架构师必备技能之Netty 高并发 UTS 项目实战

一、Netty Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基...

A尚学堂Nancy老师
08/27
0
0
大龄程序员之困

今天花费了整整一天时间来看paxos协议,发现还是看不明白,感觉分布式数据处理这一块已经无望了。 脑袋混乱之极。到处都是乱乱的。 年龄大了是一个原因,根本原因还是感觉自己的智商不够,逻...

大胡桃夹子
2017/11/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

腾讯与Github的魔幻会面背后的故事…

10月22日,腾讯开源管理办公室有幸邀请到Github新晋CEO Nat Friedman,前来鹅厂参观交流。目前腾讯已经有近70个项目在Github上开源,共获得17w stars,世界排名11位。Github是腾讯开源的主阵...

腾讯开源
33分钟前
1
0
单例模式

单例模式(Singleton pattern)属于创建型设计模式。 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对...

NinjaFrog
38分钟前
1
0
TypeScript基础入门之装饰器(三)

转载 TypeScript基础入门之装饰器(三) 继续上篇文章[TypeScript基础入门之装饰器(二)] 访问器装饰器 Accessor Decorator在访问器声明之前声明。 访问器装饰器应用于访问器的属性描述符,可用...

durban
55分钟前
2
0
spring5调研学习(转载)

Spring框架的新功能 这一章主要提供Spring框架新的功能和变更。 升级到新版本的框架可以参考。Spring git。 内容列表 Spring 5.x框架新的功能 Spring 4.x框架新的功能 Spring 3.x框架新的功能...

小海bug
今天
2
0
为何Spring框架能这么流行?

想要学习更多关于Spring框架在Java开发者中如此流行?看这篇文章可以学到更多! Spring框架特性 Spring是用于应用开发中的一款强大,轻量级框架。更广泛地说,你可以认为Spring框架是一个定义...

java知识分子
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部