一次简单的系统优化

一次简单的系统优化

系统开发设计主要就是围绕3高:高性能、高并发、高可用。介绍这些优化方案的大牛很多,而且高并发和高可用的设计需要足够的技术积累和行业经验积累,我这里主要是记一些初级开发也能使用的方案优化系统性能。

前因

年初由于一个部门领导带走了手底下几乎所有的员工,我被临时抽调过去暂时接手其中6套系统。在没有招齐人手的这段时间,我主要是熟悉项目以及维护和优化系统性能。

数据库相关优化

数据库往往是一个系统最大的性能瓶颈,服务器简单的加机器就能很大程度的解决问题,但是数据库加机器就比较麻烦而且数据库机器一般性能高,价格贵,所以这里主从设置和分表分库就不介绍了,而且我也没有实际的分表分库经验。

熟悉业务流程后简单的看了下数据库的一些主要业务表,基本都只有一个主键,但是索引也不是简单的新增即可,毕竟会占用存储空间也会影响数据新增删除的性能,需要按照实际的使用情况处理,所以先引入druid监控数据库的执行情况。 通过druid可以看到url执行的sql条数、sql执行时间,可以选择时间长的sql通过explain查看执行计划做对应优化,主要分为请求了不需要的数据或者扫描了额外的记录,以后有时间了再介绍。

数据库资源是一种奢侈的资源,通过url监控发现有些接口一次执行请求了60多次数据库,看源码发现是先取出一部分订单然后在循环里面查询每条订单的一些其他表的数据。这种情况最大的问题并不是说订单数据整合效率低,而是会人为增加数据库并发量,导致数据库负荷。这种情况是可以先批量将这部分的数据查出然后在服务端程序里面做聚合处理。

合理利用并发

常常说到并发,主要就是因为在某些情况下线性会比并发慢很多。比如有前线业务反馈订单详情页面加载缓慢影响作业效率,这个作为主要功能肯定是放在第一优先序列。 针对业务说的加载缓慢情况,首先是去确认偶发还是大面积,结果没想到全部都是这样的情况。订单详情页加载的时候会有好几个请求,我们服务器也没有完善的监控,所以选择了最原始的方案:浏览器打开控制台查看每一个请求的花费。一看吓一跳,其中加载订单详情的接口稳定需要6秒以上(我也不知道他们是怎么忍受到现在才反馈的)。看了下源码,逻辑比较简单,就是加载12个模块的数据返回给前端,问题主要分为以下三个:

  1. sql执行效率太低,没有合理使用索引直接扫全表,可能是以前数据量少所以业务没有感觉到慢。
  2. 执行sql太多,部分sql在多个模块加载数据的时候都会执行,其实只是简单的想要一个客户编号(可能是其它接口需要写了一个通过订单号加载部分数据的方法,这里就直接复用了,导致同一sql在不同方法重复执行)。这个解决也简单,重写方法,先将公共数据查询出来再查各个模块的数据即可。
  3. 12个模块数据加载是线性执行但实际是无关联的。其实这个是加载慢最主要的原因,即使每个模块数据加载需要300毫秒,12模块也需要3600毫秒了,所以我将其改成了异步加载,打散数据加载久的模块,三个模块一起通过一个线程加载,这样加载时间就取决于最慢的一个线程。(有些场景是可以做成前端分模块加载的,比如商品详情页面加载不出来库存情况也不影响正常使用,默认有库存就行) 最终成功将接口请求时间缩减到1.6秒左右。
该用缓存的时候使用缓存

浏览器会有缓存,网络请求会有cdn缓存,请求到了nginx也可以查缓存直接返回、通过nginx到后端服务器了还有利用缓存,如果后端服务到mysql是select也会有缓存可用,缓存的使用从前到后无处不在,cpu都还有个一二三级缓存呢。

数据库资源是有限的,sql执行的时候也需要解析sql、优化器优化再查询并返回结果,性能相对缓存来说还是差了不少,所以要合理利用缓存。网上有很多大牛对redis进行了详细介绍,比如部署相关的主从、哨兵、分片,而且我自己也只是了解过,没有看过redis相关的源码也没有足够的体量经历过一些复杂的意外情况,照搬一遍没有意义,这里主要是介绍一些简单使用场景(非redis各种数据结构的使用场景)。

一般很多公司都会有各种编码,比如省市区、产品类型等等,业务数据主要是保存编码值,需要的时候再通过编码值查询对应的中文名称。比如上面说到的订单详情加载,其中就有用户所在省市区,项目以前的实现方案就是拿编码值去省市区表查询,甚至还有在循环里面查询的,这效率懂的都懂。像省市区这种数据设置好了以后基本一年不动,甚至有些公司都不会每年同步最新数据,所以完全可以使用redis缓存,而且如果服务器内存足够还可以放一份本地缓存。

除了这种一年半载不动的编码值,一些业务数据也是可以放缓存的,比如上面说到到订单详情加载会重复执行sql查客户编号。除了客户编号,一些订单基本数据也可以放缓存,比如客户手机号、身份证号码等等。这些数据改动的可能性很低甚至不可修改,查询频率又非常高,放缓存可以显著提升效率,但是要注意设置过期时间。这些redis的简单使用不会有太复杂的技术要求,甚至都不要双写,数据有改动直接清空缓存,下次读的时候没有数据从数据库取出来放入redis缓存就行。(要根据实际情况考虑是否需要处理缓存穿透,有些公司情况不同直接网络就限制了可访问用户所以不需要实现缓存穿透处理,但是了解下没有坏处)。

修改使用不合理的Transactional注解

首先并不是所有更新sql都需要在事务环境下执行,其次Transactional注解也要注意加的技巧。 spring里面事务的实现是通过aop实现的,所以同一个类里面的方法调方法是不会起事务的,比如:

public void methodA(){
    .....
    methodB();
    .....
}
@Transactional
public void methodB(){
    .....
}
复制代码

这时候其它类调用方法methodA是不会起事务的。除了这些还有默认情况下方法必须是public的啊、可回滚的异常类型之类的,都需要了解下。 而且个人认为spring里面事务也是一种锁,只不过是锁的数据库资源,只要你把资源锁了,其它请求进来就没得用的了。所以就像使用锁时需要注意锁的粒度一样,Transactional也需要注意粒度,比如下面这种情况:我给一个方法加了Transactional注解,调用这个方法的时候会创建一个数据库连接,如果这个方法里面我有个网络请求花了3000毫秒,然后更新数据库花了100毫秒,也就是这次请求霸占了这个连接3100毫秒,如果数据库连接池只有1个连接,那后面进来的请求就得等你3100毫秒,这3000毫秒完全浪费了。 所以,尽量不要在事务里面执行网络请求之类的耗时操作。 关于spring事务,我在我的另一篇文章做了简单分析,感兴趣的可以自己去看看,这里就不贴链接了,毕竟不感兴趣了贴了没用,感兴趣的不贴也能自己找过去。

期望实现灰度发布

目前这些项目都还是暴力发布,虽然当前相关业务只有公司业务使用或者客户在业务引导下使用,晚上下班了发版影响不大,但是考虑到后面的公司规划和一些其它情况,还是要实现灰度发布更好。不过运维的同事这段时间全忙着服务器搬迁的事,没办法,只能自己动手丰衣足食。 其实这个问题主要有一下几个关键点:

  1. 确定好当金丝雀的机器了在下线的时候要处理完已经进来的请求,并且不能接新的请求。
  2. 流量分发的时候不能分发到下线到机器上。
  3. 项目发布好后要可以根据特定账号分发流量到充当金丝雀的机器上。

关于第一点比较简单,实现spring项目的优雅停机就可以了。

第二点控制流量分发其实也还好。目前这6套系统形成了一个闭环,而且作为整个业务的上游,只有对外调用的接口,没有其它接口主动调用它们,和下游系统的数据交互也是通过mq,所以管好自己几个系统之间的调用就行。这里使用的方案是通过nacos+ribbon实现(选nacos主要就是因为文档全面而且我们还有一些比较老的项目,又可以同时作为配置中心)。所有项目注册到注册中心nacos服务上,发布的时候先将充当金丝雀的机器下线,ribbon负载均衡的时候去注册中心拿到对应项目机器列表,根据负载算法将请求路由到非下线项目。但是这样只解决了几个后端项目之间的调用,前端进来的请求还是走的域名,不知道哪台机器下线了,所以这时候就需要引入网关。我们使用的是zuul(目前文档全面的又是java开发的主要就zuul、spring cloud gateway,后者又有版本限制),所有前端请求统一到zuul,再由zuul将请求路由到后端服务器,当然,网关不只是能做路由功能而已。

第三点 会比较麻烦,目前的设想是自定义ribbon的路由规则,如果是测试账号的请求就分发到金丝雀机器上。 目前nacos+ribbon这个已经上了,zuul应该在下周会上(第一期上只有简单的路由功能),第三点还只是一种想法,还需要调研下有没有更好的方案(各位有好的建议也可以介绍下)。

随便聊聊

上面这些东西,其实都挺简单,并没有用到太复杂的技术以及一些三方框架或者中间件的复杂应用,写这些只是给其他同为初级的研发分享下经验,系统优化并不一定非得动架构、加机器、买cdn缓存、资源静态化、数据异构什么的我们目前还玩不转的甚至用不到的,从平时自己敲的业务代码入手就行,谁都是这样过来的,每天比前一天犯更少的错也是一种进步。17年12月我才从android转后端时还乱加Transactional注解,根本没有事务效果还不知道原因,上个月我却通过分析spring源码解决了我们项目中主从设置存在的隐患。初级开发咋了,写curd又咋了,curd业务也不是随随便便就能写好的,所以与其去各种贴吧论坛抱怨curd没意思,还不如想想怎么才能写出没问题的高性能的curd代码。 这个月31号就离职了,感觉在现在这技术成长到了一个瓶颈,想换个地方接触下更复杂的业务场景接触更多的牛人,见识下别人的设计。这段时间会比较空闲,有空就会写写我这一年半后端开发工作的收获,想到啥就写啥,可能是对一些三方框架或者中间件的理解、使用经验,也可能是一些工作经验,主要是为了自己以后回顾自己使用过的技术,也缕缕自己的思路。当然,和那些大牛的分析没有可比性,甚至可能会存在错误的理解和使用,欢迎各位指教。

转载于:https://juejin.im/post/5cdeb04f6fb9a07ea8039963

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值