您的位置:首页 > 理论基础 > 计算机网络

高性能软件系统设计中应该考虑的问题

2011-07-28 18:21 351 查看
高性能软件系统设计中应该考虑的问题

高性能软件系统都是应用于大用户量,超大数据量处理的情况下,这样的软件系统并不好设计,这里主要讲述一下对于高性能软件系统在设计过程中要注意考虑哪些问题。

1. 内存的使用
栈:
在高性能软件设计时,由于处的的数据量大且复杂。所以会大量的使用多线程技术。一个进程的线程数有可能达到几千或是上万。而每个线程都会有自己的线程线来保存局部变量和函数调用的信息。由于线程数之多,而总的内存空间又是有限的。平均下来每个线程的栈空间就少的可怜了。所以在使用栈空间时就要考虑如何合理的使用了,不然就会发生栈数据越界的严重问题。在此时使用局部变量时就要谨慎些了。1.尽量避免使用超大的局部数据变量。2.要注意函数调用的形参数据长度和数量。3.要注意函数调用的深度,因为涵数调用的越深,压栈的数据也就越多。4.要谨慎使用递归函数(递归算法),控制好递归的深度,或是用别的算法来代替递归算法。

堆:
堆空间是通过new(malloc)和delete(free)方法进行申请和释放的。堆空间可以用来存放比较大的数据信息。但在多线程的程序中,堆空间的管理是间很大的麻烦事。分配到的堆空间什么时候用,有哪些线程会在什么时候用到,在什么时候用完,什么时候才能释放。这些问题不解决好,会出现很多问题。比如其中一个线程过早释放,而另外的线程又在使用,或是用完了没有释放(内存泄漏)等严重问题。针对这些问题,最好是能设计一个内存管理器来专门管理堆空间的内存分配与回收。其它所有的模块或线程要得到堆空间时都通过内存管理器获取,也由内存管理器决定什么时候进行释放。这样即可以解决堆空间管理混乱的问题,还能提高程序效率,因为可以通过内存管理器重复使用同一块堆空间,而不必频繁的分配和释放内存,还有一点也非常得要,就是发生内存操作错误或是内存泄漏以后也可以通过内存管理器的一些信息快速的找到问题的所在。最简单的一种内存管理器就是采用引用记数的方法。就是当通过内存管理器分配到一块资源后对该内存块资源的合用标记加一,以后每当一个模块使用一次该资源就加一,用完后就减一,当减到为零时,由内存管理器去决定重复利用还是释放。通过个简单的方法可以实现一个相对简单且实用的内存管理器。内存管理器的形式多种多样,具体要怎么设计要看具体的情况。

2. 多线程管理
创建线程池管理器:
一个进程当中有各种功能的线程,每一种功能的线程也许有很多个。线程的种类和数量之多,这对线程的管理与监控来说是件麻烦的事情。在这种情况下应当设计线程池管理器,专门来创建分配线程资源,回收释放线程资源,监控管理线程,控制线程数量等方面的工作,也同时多次复用线程资源,避免重复的创建和释放线程,提高系统整体效率。对不同功能类的线程应当分配一个独立的线程池。这样方便不同功能类型的线程个自特性的管理。

如何决定线程的数量:
一个进程当中并不是线程越多越好,因为线程越多消耗的资源也越多。而且加上线程间频繁的切换会影响效率。那怎样来确定线程数量多少合适呢?一般来说每个CPU核分配两个线程比较合适,但这种方法不太科学,比较片面。最好的方法是要根据不同功能的线程来进行考虑。比如一个线程专门用于网络操作的,那么该类型线程的数量就决定网络带宽,和网卡IO的情况了,这时每个线程发出一个网络动作请求后,由于网络的廷迟和网络设备IO的限制需要等待比较长的时间,此时会进入睡眠状态。那么由于这种线程在工作时大部分时间处于睡眠状态,所以可以多开几个线程,以更好的利用CPU和其它计算机设备的资源。再比如现在是一种纯数据运算的线程,它只应用到CPU和内存的资料,没有其它IO操作。这种类型的线程数量主要考虑CPU和内存的参数性能。可能不太适合过多数量的线程,因为过多的线程会造成频繁的切换,反而效率下降。再比如一个专门用于操作数据库的线程,那就要考虑你所用的数据库系统了,如果只有一个数据库,那么就在同一时间最多分配一个线程比较合适了,因为太多的线程同时去操作同一个数据库,由于数据库本身需要不断切换连接用户和数据库内部的一些操作的切换,反而影响了数据库工作的性能。所以说不同功能的线程要看具体这个线程完成什么工作,要用到哪些其它资源,再来决定该类线程的数量。

3. 资源的分配与管理
在多线程程序中有个很急手的问题,就是资源访问控制。如果没处理好资源访闾控制,那就会造成死锁或饿死的现象。在普通的多线程程序设计中可以通过互斥体,信号量,自旋锁等方法可以控制,但在高性能程序设计,由于线程数量之多,关系复杂,如果只靠简单这些方法可能很难解决资源访问控制问题。特别有些线程在自己的深度死锁问题(深度死锁就是一个线程得到一个资源后对该资源进行了上锁,但在后续的操作当中,由于逻辑复杂,函数调用太深,在某处再次对该资源获取操作权,但在前面他自己把这个资源上了锁还没解锁,再次尝试获取操作权时造成失败的问题)。在针对这些情况下,就需要设计一个资源分配管理器来进行资源的分配和管理了。当一个线程需要访问某个资源时都需要通过个资源分配器来获得操作权。资源分配器记录着哪些资源现在是哪个线程在访问,有哪些线程需要访问哪些线程,由资源分配管理器来决定这些线程谁将先获得哪些资源的访问权。资源分配管理器还记录着资源被哪些线程所使用过的时间有多长,通过合理的调配,让优先级比较低的线程也能得到资源的访问权,不至于饿死。

4. 数据的存储
数据存储问题是高性能系统设计中所避免不了的。如果这里没做好,会严重影响系统的整体性能。很多数据的存储都是传统的关系型数据库。所以在设计数据库时考虑很多的问题,比如表关系设计是否设计合理,对数据量大的表进行分表或表分区存储,对表数据建立合理的索引,通过多种索引联合检索的方式来提高查询效率,考虑表锁和表行锁的影响,存储过程设计的合理问题,还有就是开启数据库的cache功能,数据库集群,分布式数据库都是需要考虑的问题。
除了这些,你最重要的一点就是还要考虑数据的关系。现在的数据库系统是表关系型数据,如果你的数据是树关系或是图关系的,那你得考虑传统数据库时否适合了,因为表关系型数据库对保存树关系或是图关系这些的数据是相当难的,会靠成数据大量冗余或是检索起来相当难。在这个时候你就不能依赖于现有的数据库系统了,你得根据数据关系的实际情况来设计适合的存储结构模块和数据检索模块了。

5. 缓存的应用
很多情况下,数据存储的性能瓶颈并不是在数据存储系统上,而是在存储设备上,大家都知道外存储器(硬盘,FLASH)的速度比CPU和内存的速度可不是几倍或是几十倍的差距,那是好几个数量级的差距。在这时候就会出现数据存储和数据检索上的性能瓶颈了,特别是数据检索。在这种情况下我们就得在数据操作层和数据存储层之间加一个缓存层了。通过合理的设计把数据索引和访问频率高的数据常驻内存。这样就能大量提高数据存储和数据检索的性能。缓存层的设计最好是能达到透明,对上下两层的工作不会有影响。

6. 集群,分布式与负载均衡
高性能系统都是处理超大数据量信息的,而单台计算机的性能毕竟是有限的。所以在做高性能系统设计的时候就不得不考虑通过集群,分布式与负责均衡的方法来提高整体的处理能力。整个系统有各种不同功能的模块组成。针对不同的功能模块可以设计成一个独立的进程,比如一个进程专门用于网络的操作,另一个专门负责数据的运算或专门用于数据的存储,而同一功能的进程又可以分布在多台的机器上运行,把这些计算机通过网络连在一起形成一个统一的系统。为什么要这样设计呢?很简单,因为每个模块进程的功能不同,对设备资源的要求也不同,如操作网络的模块进程主要是对网络的要求要高,这样的话就让这种进程运行成一组网络配置比较高的计算机上,而另外一个专门用于数据运算的模块进程则对CPU 和内存的要求比较高,那我们就可以让它运行在一组CPU和内存配置比较高的计算机上。这样即能节省成本又能提高系统整体的性能,而且可以针对某个功能模块实际需要的运算量来增加和减少相关的计算机设备。在这种设计当中,我们就要考虑同一功能模块间多个进程的任务分配(这里还要考虑负载均衡等问题),工作协调等问题,在这时可以通过集中控制或是进程间相互间自我协调的方式进行控制。在不同功能模块进程间的工作协调也要进行集中和相互间自我的控制。这也增加了系统整体的复杂度,这里最好是设计一个专门的监控与控制管理器来管理整个系统的工作。

7. 可靠性与安全性
这种高性能系统都需要长时间稳定的对外提供服务。所以在设计中可靠性和安全性是必须考虑的问题。在这里需要考虑整个系统中当一个进程或机器出现问题不能工作时,其它相同的进程或机器能不能及时接管其没有完成的工作,出现问题的进程或机器能不能快速的恢复。当遇到一些不可预知的问题或外部攻击时,原始数据被破坏时,能不能快速的恢复。这些都是可靠性的表现。这里可以通过功能模块冗余设计,模块或进程实时的监控来快速发现问题,通过让其它进程及时的接管工作或让其快速的自我恢复。还有就是要设计数据镜像的实时备份等功能来增强系统的可靠性。
整个系统是运行在多台计算机上,当中通过网络过行数据的交换和控制。当系统遭到恶意攻击时可能让系统无法正常的工作,或是窃取系统中重要的信息实施其它的恶意行为。这些在设计时也要考虑。我们可以通过对系统网络中的交换的数据进行加密,对系统重要的配置信息进行加密后再保存。在系统内部数据交换或是系统控制等协议设计上要严谨,以免出现严重的漏洞让人恶意利用。通过这些一系统的方法来提高系统的安全性。

8. 后续的优化和重构
系统开发完成后放在机器上运行就算大功告成了吗?其实还差很远。像这样的高性能系统很难做到一步设计到位的。很多问题在运行后才会暴露出来,即使你很有经验也一样。比如某些算法的设计是否合理,达到了性能要求。某些数据的处理方法是不是正确的。某些功能的设计是否合理。这些都可能在系统运行后才能发现。当一个算法达不到要求时,我们就得优先它,重写算法。某些模块对数据的处理流程并不合理,我们要重构它。因为某些需求有所改动或功能设计的不合理,我们要对该模块进行重新设计。这些工作都是需要的。为了解决这些问题,我们在设计系统时就要做好准备,尽量做到系统的可扩展性好,模块可重构性高。因为后期的算法优化,模块重构或增加新的功能可能会对系统影响很大。所以设计时需要注意一些问题。尽量做到模块间耦合度低,相互依赖小,模块间接口明确。可以考虑模块插件式的设计方法,这样在后期对模块进行更换,增加时不会影响到相领的模块。在详细设计时应用好设计模式(设计模式千变万化,不能按书的硬套),提高代码的复用率,减少代码冗余,代码功能扩展性好。考虑算法与数据结构分,这样当更换算法时不用改变数据的结构。这些方法都可以增加系统的灵活性和方便后期的优化。还有一点也非常重要,那就是文档的管理,架构文档,算法文档,接口文档等一系列的设计文档。这些也是在后期进行优化和重构的重要信息。

在设计这些复杂性高的高性能软件系统还有很多东西要考虑,这里只讲到了一小部分的介绍,要设计好这样的系统还需要更进一步的学习和总结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息