很早以前,做管理系统,对性能体会并不是特别明显。因为一些用户非常聪明,会通过调整自己的使用方式来适应系统的处理能力。现在想起来,有环境的原因也有能力的原因,没有做好性能的事情,觉得有些好笑也有些遗憾。
现在做的程序,对响应速度、处理能力都有一定的要求,而且这些指标直接和效益挂钩。这个时候,性能问题就随着系统的运行不断显现出来,并在运行过程中左右一项重要任务不断改进、调优。
性能优化的过程是一种辛苦又有趣的挑战。
改进总是在事关厉害的时候才会有推动力。无关痛痒,不做又何妨?
来自业务上的简化
很多时候,自己是一个理想主义者,公司的其他一些也是,所有在系统设计时:总试图考虑周全,试图具备很强的扩展性,试图一劳永逸。事实是,在对业务又有成竹于胸、对设计有深刻立即的时候,很难做到。所以往往过度设计、过度开发。
多做事情,不是好事情,在一些关键流程上影响严重,尤其是那些高频执行的代码。多一个环节,都是意味着多消耗计算资源。
很早前,我们做的程序在结算上,要求对交易账号三方进行资金划拨(一方减少,另外一方增加,第三方佣金增加),要记录资金变动明细,要有事务保护。这业务,在交易量不大的情况下,没有问题;小幅度的性能提升也可以通过提升硬件配置来解决。但是对于我们,运营一段时间后,真的很难在提升:1、业务逻辑写得很复杂,改动风险很大,2、所有瓶颈在数据库上,事务经常失败
经过很长时间的观察,我们发现,我们并不需要三方划账,只需要单方扣款就可以了;我们也发现,资金做好变动日志就可以了,事物也可以不需要。这个业务以确定下来,砍掉了这个模块6成的代码,单台的服务大幅提高。
事后感叹,真的想多了,业务没有想的复杂。业务上的简化,比其他方面的优化,我认为更加有效,也更加乐意去做:
1. 可以删掉很多无用的东西,维护简单了,
2. 以前不爽的代码终于可以写了
部署上的优化
业务总是在上升的,单台服务器,总会有一个上限。这是常理,但在开始做程序的时候,只是想过,并有做预案。那时的想法是先完成业务,在考虑性能。这个策略,仁者见仁,智者见智。
部署上的优化,我的理解是有两个维度:
1. 从业务流程上进行拆解,通过拆解,可以降低整体的复杂度,方便维护和部署;整个流程可以部署到不同的服务器上,可以达到分摊压力的效果;
2. 单个环境多套部署,将单个环节部署到不同服务器上,做负载均衡
就现在的情况看,增加硬件的成本,还是远低于开发、维护的人力成本的,所有这个方式的性能优化,是运维喜欢干的。
来自数据库上优化
数据库的优化空间,还是挺大的,主要表现在表索引的合理使用、表字段设计、表关联查询、事务。
个人认为:
1、对于性能要求的程序,尽量避免使用事务、和表关联。
2、好的索引能够很大程度上提高速度
3、如果数据量大,要考虑表分区
一些编码的人,对数据库了解不多,没有连主键、索引的都没有,也时常发生。所以优化这个,是程序员,尤其懂数据库的程序员喜欢干的事情。时常听到一些牛人将一些耗时巨大的数据库计算,瞬间提升数百、数千倍。
来自程序实现上优化
一般公司的程序,只是完成某一些业务而且,没有什么大不了的技术攻关,所有也谈不上什么高深的技术。而我们写的程序,经常会因为各种各样的原因,多做一些没用的事情:
1、没有用的for循环
2、可以在一个循环里面搞定的,做了多个循环
3、一个事情反复做,通常是代码相互调用(因为计算结果没有共享或传递)
这个事情,程序员会比较喜欢做。程序员每隔一段时间都会回头看自己的过去,每每发现以前的幼稚表现,都有冲动去修正一下。
来自硬件的提升
比较简单
一些小经验
IIS行吗
在开发现在这个程序的时候,有人提醒我们IIS容易崩溃,不稳定,性能不行。
在系运行之初,我们的确也是碰到这两个问题,也怀疑IIS是否真的不行。遇到内存消耗居高不下、线程消耗居高不下、CPU满负荷,总是间歇性崩溃。
随着问题的逐步解决,我们发现,其实大部分原因是代码编写不当,一小部分是不了解IIS的运行机制。
内存问题
.Net有垃圾回收机制,但也不能滥用,少New一点也许会好点
.Net的垃圾回收,并不是一个对象释放了,就马上会回收的,内存占用高,不代表就是真的消耗了那么多
线程消耗
多线程是解决一些性能问题的良方,但是线程是有成本的,线程太多,CPU会把时间浪费在线切换上
对于IO密集型的,最好采用异步的方式。同步方式意味着一个线程大部分时间消耗在等待上。
对于cpu密集性的,处理升级增加硬件投入,好像没有办法。
不要把ThreadPool中的线程耗光了,不然IIS就不响应了:1、asp.net也用这个线程池;2、每秒只会创建2个新线程;
CPU满负荷
多数情况下,是死循环了
还有就是做了一些多余的事情
MsSql之Nolock
通常,在MSSQL中,有Nolock的提示符,表示允许脏读,这种情况下,避免使用锁,非常有效。多数情况下,我们的一些数据并没有那么严格要求,即便是脏读,也没有什么关系。
MsSql之with(rowlock)
使用Update语句,一些时候是明确只更新单行的,尤其是状态转换的,使用rowlock,会好一些(感觉,没有测试确认过)。