发布新日志

  • (LR)判断应用程序是否存在处理器瓶颈的方法

    2009-04-29 13:49:30

    判断应用程序是否存在处理器瓶颈的方法:如果Processor Queue Length 显示的队列长度保持不变(>=2)个并且处理器的利用率%Processor Time 超过90%,那么很有可能存在处理器瓶颈。
    如果发现Processor Queue Length 显示的队列长度超过2,而 处理器的利用率却一直很低,那么或许更应该去解决处理器阻塞问题,这里处理器一般不是瓶颈。
    如果系统由于应用程序代码效率低下或者系统结构设计有缺陷而导致大量的上下文切换(Context Switches/sec 显示的上下文切换次数比较大),那么就会占用大量的系统资源。
    如果系统的吞吐量降低并且CPU 的使用率很高,并且此现象发生时切换水平在15000 以上,那么意味着上下文切换次数过高同时还可以比较Context Switches/sec 和%Privileged Time 来判断上下文切换是否过量。 
    如果后者的值超过40%,且上下文切换的速率也很高,那么应该检查为什么会产生这样高的上下文切换。
  • LoadRunner性能指标分析(转)

    2009-04-29 11:49:47

    Memory:

    内存使用情况可能是系统性能中最重要的因素。如果系统“页交换”频繁,说明内存不足。“页交换”是使用称为“页面”的单位,将固定大小的代码和数据块从 RAM 移动到磁盘的过程,其目的是为了释放内存空间。尽管某些页交换使 Windows 2000 能够使用比实际更多的内存,也是可以接受的,但频繁的页交换将降低系统性能。减少页交换将显著提高系统响应速度。要监视内存不足的状况,请从以下的对象计数器开始:
    Available Mbytes:可用物理内存数. 如果Available Mbytes的值很小(4 MB 或更小),则说明计算机上总的内存可能不足,或某程序没有释放内存。参考值:至少要有10% 的物理内存值。


    page/sec: 表明由于硬件页面错误而从磁盘取出的页面数,或由于页面错误而写入磁盘以释放工作集空间的页面数。一般如果pages/sec持续高于几百,那么您应该进一步研究页交换活动。有可能需要增加内存,以减少换页的需求(你可以把这个数字乘以4k就得到由此引起的硬盘数据流量)。Pages/sec 的值很大不一定表明内存有问题,而可能是运行使用内存映射文件的程序所致。Page/sec 推荐00-20(如果服务器没有足够的内存处理其工作负荷,此数值将一直很高。如果大于80,表示有问题)。这些计数器的值比较低,说明Web 服务器响应请求比较快,否则可能是服务器系统内存短缺引起( 也可能是缓存太大,导致系统内存太少)。PageInput/sec 的值可以衡量出硬错误页发生的速率,通常它的值会大于或者等于PageReads/sec。

    page read/sec:页的硬故障,page/sec的子集,为了解析对内存的引用,必须读取页文件的次数。阈值为>5. 越低越好。大数值表示磁盘读而不是缓存读。

    由于过多的页交换要使用大量的硬盘空间,因此有可能将导致将页交换内存不足与导致页交换的磁盘瓶径混淆。因此,在研究内存不足不太明显的页交换的原因时,您必须跟踪如下的磁盘使用情况计数器和内存计数器:
    Physical Disk\ % Disk Time
    Physical Disk\ Avg.Disk Queue Length
    例如,包括 Page Reads/sec 和 % Disk Time 及 Avg.Disk Queue Length。如果页面读取操作速率很低,同时 % Disk Time 和 Avg.Disk Queue Length的值很高,则可能有磁盘瓶径。但是,如果队列长度增加的同时页面读取速率并未降低,则内存不足。


    要确定过多的页交换对磁盘活动的影响,请将 Physical Disk\ Avg.Disk sec/Transfer 和 Memory\ Pages/sec 计数器的值增大数倍。如果这些计数器的计数结果超过了 0.1,那么页交换将花费百分之十以上的磁盘访问时间。如果长时间发生这种情况,那么您可能需要更多的内存。


    Page Faults/sec:每秒软性页面失效的数目(包括有些可以直接在内存中满足而有些需要从硬盘读取)较page/sec只表明数据不能在内存的指定工作集中立即使用。


    Cache Bytes:文件系统缓存(File System Cache),默认情况下为50%的可用物理内存。如IIS5.0 运行内存不够时,它会自动整理缓存。需要关注该计数器的趋势变化


    如果您怀疑有内存泄露,请监视 Memory\ Available Bytes 和 Memory\ Committed Bytes,以观察内存行为,并监视您认为可能在泄露内存的进程的 Process\Private Bytes、Process\Working Set 和Process\Handle Count。如果您怀疑是内核模式进程导致了泄露,则还应该监视 Memory\Pool Nonpaged Bytes、Memory\ Pool Nonpaged Allocs 和 Process(process_name)\ Pool Nonpaged Bytes。如果发生了内存泄漏,Process\Private Bytes计数器和Process\Working Set 计数器的值往往会升高,同时Available Bytes 的值会降低。内存泄漏应该通过一个长时间的,用来研究分析当所有内存都耗尽时,应用程序反应情况的测试来检验。


    Pages per second :每秒钟检索的页数。该数字应少于每秒一页。

    Process:
    %Processor Time: 被处理器消耗的处理器时间数量。如果服务器专用于sql server,可接受的最大上限是80-85%。参考值:小于75%。排除内存因素,如果该计数器的值比较大,而同时网卡和硬盘的值比较低,那么可以确定CPU 瓶颈。



    %User Time:表示耗费CPU的数据库操作,如排序,执行aggregate functions等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。


    %Privileged Time:(CPU内核时间)是在特权模式下处理线程执行代码所花时间的百分比。如果该参数值和"Physical Disk"参数值一直很高,表明I/O有问题。可考虑更换更快的硬盘系统。另外设置Tempdb in RAM,减低"max async IO","max lazy writer IO"等措施都会降低该值。
    此外,跟踪计算机的服务器工作队列当前长度的 Server Work Queues\ Queue Length 计数器会显示出处理器瓶颈。队列长度持续大于 4 则表示可能出现处理器拥塞。此计数器是特定时间的值,而不是一段时间的平均值。
    % DPC Time:越低越好。在多处理器系统中,如果这个值大于50%并且Processor:% Processor Time非常高,加入一个网卡可能会提高性能,提供的网络已经不饱和。

    Thread
    ContextSwitches/sec: (实例化inetinfo 和dllhost 进程) 如果你决定要增加线程字节池的大小,你应该监视这三个计数器(包括上面的一个)。增加线程数可能会增加上下文切换次数,这样性能不会上升反而会下降。如果十个实例的上下文切换值非常高,就应该减小线程字节池的大小。

    Physical Disk:
    %Disk Time %:指所选磁盘驱动器忙于为读或写入请求提供服务所用的时间的百分比。如果三个计数器都比较大,那么硬盘不是瓶颈。如果只有%Disk Time比较大,另外两个都比较适中,硬盘可能会是瓶颈。在记录该计数器之前,请在Windows 2000 的命令行窗口中运行diskperf -yD。若数值持续超过80%,则可能是内存泄漏。


    Avg.Disk Queue Length:指读取和写入请求(为所选磁盘在实例间隔中列队的)的平均数。该值应不超过磁盘数的1.5~2 倍。要提高性能,可增加磁盘。注意:一个Raid Disk实际有多个磁盘。


    Average Disk Read/Write Queue Length:指读取(写入)请求(列队)的平均数。


    Disk Reads(Writes)/s: 物理磁盘上每秒钟磁盘读、写的次数。两者相加,应小于磁盘设备最大容量。


    Average Disksec/Read: 指以秒计算的在此盘上读取数据的所需平均时间。


    Average Disk sec/Transfer:指以秒计算的在此盘上写入数据的所需平均时间。


    Network Interface:
    Bytes Total/sec :为发送和接收字节的速率,包括帧字符在内。判断网络连接速度是否是瓶颈,可以用该计数器的值和目前网络的带宽比较

    SQLServer性能计数器:
    Access Methods(访问方法) 用于监视访问数据库中的逻辑页的方法。
    . Full Scans/sec(全表扫描/秒) 每秒不受限的完全扫描数。可以是基本表扫描或全索引扫描。如果这个计数器显示的值比1或2高,应该分析你的查询以确定是否确实需要全表扫描,以及S Q L查询是否可以被优化。
    . Page splits/sec(页分割/秒)由于数据更新操作引起的每秒页分割的数量。


    Buffer Manager(缓冲器管理器):监视 Microsoft® SQL Server™ 如何使用: 内存存储数据页、内部数据结构和过程高速缓存;计数器在 SQL Server 从磁盘读取数据库页和将数据库页写入磁盘时监视物理 I/O。 监视 SQL Server 所使用的内存和计数器有助于确定: 是否由于缺少可用物理内存存储高速缓存中经常访问的数据而导致瓶颈存在。如果是这样,SQL Server 必须从磁盘检索数据。 是否可通过添加更多内存或使更多内存可用于数据高速缓存或 SQL Server 内部结构来提高查询性能。
    SQL Server 需要从磁盘读取数据的频率。与其它操作相比,例如内存访问,物理 I/O 会耗费大量时间。尽可能减少物理 I/O 可以提高查询性能。


    .Page Reads/sec:每秒发出的物理数据库页读取数。这一统计信息显示的是在所有数据库间的物理页读取总数。由于物理 I/O 的开销大,可以通过使用更大的数据高速缓存、智能索引、更高效的查询或者改变数据库设计等方法,使开销减到最小。


    .Page Writes/sec (.写的页/秒) 每秒执行的物理数据库写的页数。
    .Buffer Cache Hit Ratio. 在“缓冲池”(Buffer Cache/Buffer Pool)中没有被读过的页占整个缓冲池中所有页的比率。可在高速缓存中找到而不需要从磁盘中读取的页的百分比。这一比率是高速缓存命中总数除以自 SQL Server 实例启动后对高速缓存的查找总数。经过很长时间后,这一比率的变化很小。由于从高速缓存中读数据比从磁盘中读数据的开销要小得多,一般希望这一数值高一些。通常,可以通过增加 SQL Server 可用的内存数量来提高高速缓存命中率。计数器值依应用程序而定,但比率最好为90% 或更高。增加内存直到这一数值持续高于90%,表示90% 以上的数据请求可以从数据缓冲区中获得所需数据。


    . Lazy Writes/sec(惰性写/秒)惰性写进程每秒写的缓冲区的数量。值最好为0。


    Cache Manager(高速缓存管理器) 对象提供计数器,用于监视 Microsoft® SQL Server™ 如何使用内存存储对象,如存储过程、特殊和准备好的 Transact-SQL 语句以及触发器。


    . Cache Hit Ratio(高速缓存命中率,所有Cache”的命中率。在SQL Server中,Cache可以包括Log Cache,Buffer Cache以及Procedure Cache,是一个总体的比率。) 高速缓存命中次数和查找次数的比率。对于查看SQL Server高速缓存对于你的系统如何有效,这是一个非常好的计数器。如果这个值很低,持续低于80%,就需要增加更多的内存。


    Latches(闩) 用于监视称为闩锁的内部 SQL Server 资源锁。监视闩锁以明确用户活动和资源使用情况,有助于查明性能瓶颈。


    . Average Latch Wait Ti m e ( m s ) (平均闩等待时间(毫秒)) 一个SQL Server线程必须等待一个闩的平均时间,以毫秒为单位。如果这个值很高,你可能正经历严重的竞争问题。


    . Latch Waits/sec (闩等待/秒) 在闩上每秒的等待数量。如果这个值很高,表明你正经历对资源的大量竞争。


    Locks(锁) 提供有关个别资源类型上的 SQL Server 锁的信息。锁加在 SQL Server 资源上(如在一个事务中进行的行读取或修改),以防止多个事务并发使用资源。例如,如果一个排它 (X) 锁被一个事务加在某一表的某一行上,在这个锁被释放前,其它事务都不可以修改这一行。尽可能少使用锁可提高并发性,从而改善性能。可以同时监视 Locks 对象的多个实例,每个实例代表一个资源类型上的一个锁。


    . Number of Deadlocks/sec(死锁的数量/秒) 导致死锁的锁请求的数量
    . Average Wait Time(ms) (平均等待时间(毫秒)) 线程等待某种类型的锁的平均等待时间
    . Lock Requests/sec(锁请求/秒) 每秒钟某种类型的锁请求的数量。
    Memory manager:用于监视总体的服务器内存使用情况,以估计用户活动和资源使用,有助于查明性能瓶颈。监视 SQL Server 实例所使用的内存有助于确定:
    是否由于缺少可用物理内存存储高速缓存中经常访问的数据而导致瓶颈存在。如果是这样,SQL Server 必须从磁盘检索数据。
    是否可以通过添加更多内存或使更多内存可用于数据高速缓存或 SQL Server 内部结构来提高查询性能。
    Lock blocks:服务器上锁定块的数量,锁是在页、行或者表这样的资源上。不希望看到一个增长的值。
    Total server memory:sql server服务器当前正在使用的动态内存总量.

    监视IIS需要的一些计数器
    Internet Information Services Global:
    File Cache Hits %、File CacheFlushes、File Cache Hits
    File Cache Hits %是全部缓存请求中缓存命中次数所占的比例,反映了IIS 的文件缓存设置的工作情况。对于一个大部分是静态网页组成的网站,该值应该保持在80%左右。而File Cache Hits是文件缓存命中的具体值,File CacheFlushes 是自服务器启动之后文件缓存刷新次数,如果刷新太慢,会浪费内存;如果刷新太快,缓存中的对象会太频繁的丢弃生成,起不到缓存的作用。通过比较File Cache Hits 和File Cache Flushes 可得出缓存命中率对缓存清空率的比率。通过观察它两个的值,可以得到一个适当的刷新值(参考IIS 的设置ObjectTTL 、MemCacheSize 、MaxCacheFileSize)
    Web Service:
    Bytes Total/sec:显示Web服务器发送和接受的总字节数。低数值表明该IIS正在以较低的速度进行数据传输。
    Connection Refused:数值越低越好。高数值表明网络适配器或处理器存在瓶颈。
    Not Found Errors:显示由于被请求文件无法找到而无法由服务器满足的请求数(HTTP状态代码404)

    Web 应用程序
    Request/Sec,Request Executing:每秒执行的请求数,当前执行的请求数。如果Request/Sec的值比较小,你的Web 程序可能是瓶颈。
     
    Request Wait Time,Request Executing Time,Request Queued:最近的请求在队列中等待的毫秒数;执行最近的请求所用的毫秒数;等候处理的请求数;该计数器应保持接近 0。超过 IIS 队列长度会出现“服务器太忙”错误。参考值:Request Wait Time 和Request Queued 在理想状况下应该接近0,如果这两个值太大,那么需要重写代码提高性能。
  • (LR)优化Controller 和Load Generators 计算机

    2009-04-29 11:36:08

    如果控制机(Controller machine)和Load Generators 计算机运行的都是Windows2000,
    那么下面两个简单的技巧可以提高性能
    l 在Load Generators 计算机上,依次进入“控制面板”——“系统”——选择“高级”
    标签页,点“性能选项”按钮,选择优化“后台服务”选项,这样可以提高性能,从而
    可以在每个Load Generators 上运行更多的虚拟用户
    l 在Controller 计算机上,按照以上的步骤,进入“性能选项”窗口,不过这里选择优化
    “应用程序”
  • LR函数

    2009-04-29 11:09:12

    VuGen 中可以使用C 语言中比较标准的函数和数据类型,语法和C 语言相同。下面简
    单介绍一下比较常用的函数和数据类型。
    1. 控制脚本流程
    if { } else { }
    for{ }
    while{ }
    ……………
    总之 C 语言的控制流程的语句这里都可以直接使用
    2. 字符串函数
    由于在VuGen 脚本中使用最多的还是字符串,所以字符串函数在脚本中使用非常
    频繁。具体的语法请参考帮助说明。
    strcmp 比较两个字符串
    strcat 连接两个字符串
    strcpy 拷贝字符串
    ……………..
    注意:在VuGen 中,以char*声明的字符串是只读的,如果试图给char*类型的字
    符串赋值的话,编译会通过,但在运行时会产生“Access Violation”的错误。解决
    这类问题,就是把字符串声明为字符数组,比如char[100]。
    3. 输出函数
    输出函数在调试脚本时非常有用。
    lr_output_message 输出一条消息
    ………………..
    4. LoadRunner 提供的标准函数
    lr_eval_string 该函数功能是得到参数(参数化输入中)当前的值
    exg: lr_output_message("temp = %s", lr_eval_string("{WCSParam2}"));
    lr_save_string 该函数功能是把一个字符串保存到参数中
    exg: lr_save_string("439","WCSParam3");
  • 用例图(use case diagrams)

    2009-04-16 10:29:41

    1. 用例图(use case diagrams)简述
        描述角色和用例之间的关系,着重展示系统必须实现的功能,用于在需求分析阶段分析客户需求。
    2. 主要元素
        用例(use case),系统为角色提供可见结果的一系列动作(简单理解为角色可见的系统功能),使用椭圆表示。
        角色(actor),在与系统的一次或者多次交互中起作用的人,组织或者其他系统(即本系统的用户或者使用本系统的其他外部系统),使用小人图形表示。
        关系(association),角色和用例的交互,使用带箭头或者不带箭头的实线表示,箭头表示调用关系。
        系统边界(system boundary boxes),可选元素,用于划定系统范围,使用包围用例和角色的长方形表示,很少用。
        包(package),可选元素,用于组织各种UML图,使之容易管理和浏览(类似java中的包),可以包括类图和用例图,使用文件夹的形式表示。
    3. 分类
       分为业务用例(business use case)和系统用例(system use case),一般来说,业务用例描述的系统功能比较粗糙和概括,业务人员更容易理解;系统用例更详细的描述系统所能提供的系统功能。
       对于一般系统而言,使用系统用例即可满足需求。
    4. 优缺点
        优点:方便系统分析设计人员和业务人员沟通,方便系统分析人员对系统范围和规模有大概认识,方便构建测试用例,方便分析人员明确系统功能,方便接口设计人员尽早介入设计开发过程。
        缺点:不适合描述没有交互或者交互很少的系统,不同的业务人员对于用例可能有不同的解读,不能清晰定义用户界面,主要适用于面向对象的系统。
    5. 注意要点
        将系统视为黑盒,从使用者的角度看系统,确定系统必须实现的功能。
        角色描述的是系统中涉及的用户,现实生活中不同人可能拥有多个的角色。
        所有的交互都发生在角色和用例之间,再没有其他可能发生的交互。
        一般情况下一个用例只有一个actor拥有,如果有多个actor共用一个用例,就要考虑是否要增加新的角色,或者分拆用例。

  • 转->用例建模指南

    2009-04-16 10:14:38

    用例(Use Case)是一种描述系统需求的方法,使用用例的方法来描述系统需求的过程就是用例建模。用例方法最早是由Iva Jackboson博士提出的,后来被综合到UML规范之中,成为一种标准化的需求表述体系。用例的使用在RUP中被推崇备至,整个RUP流程都被称作是"用例驱动"(Use-Case Driven)的,各种类型的开发活动包括项目管理、分析设计、测试、实现等都是以系统用例为主要输入工件,用例模型奠定了整个系统软件开发的基础。

    1. 什么是用例?

    在介始用例方法之前,我们首先来看一下传统的需求表述方式-"软件需求规约"(Software Requirement Specification)。传统的软件需求规约基本上采用的是功能分解的方式来描述系统功能,在这种表述方式中,系统功能被分解到各个系统功能模块中,我们通过描述细分的系统模块的功能来达到描述整个系统功能的目的。一个典型的软件需求规约可能具有以下形式:


    采用这种方法来描述系统需求,非常容易混淆需求和设计的界限,这样的表述实际上已经包含了部分的设计在内。由此常常导致这样的迷惑:系统需求应该详细到何种程度?一个极端就是需求可以详细到概要设计,因为这样的需求表述既包含了外部需求也包含了内部设计。在有些公司的开发流程中,这种需求被称为"内部需求",而对应于用户的原始要求则被称之为"外部需求"。

    功能分解方法的另一个缺点是这种方法分割了各项系统功能的应用环境,从各项功能项入手,你很难了解到这些功能项是如何相互关联来实现一个完成的系统服务的。所以在传统的SRS文档中,我们往往需要另外一些章节来描述系统的整体结构及各部分之间的相互关联,这些内容使得SRS需求更象是一个设计文档。

    1.1 参与者和用例

    从用户的角度来看,他们并不想了解系统的内部结构和设计,他们所关心的是系统所能提供的服务,也就是被开发出来的系统将是如何被使用的,这就用例方法的基本思想。用例模型主要由以下模型元素构成:

    • 参与者(Actor)
      参与者是指存在于被定义系统外部并与该系统发生交互的人或其他系统,他们代表的是系统的使用者或使用环境。
    • 用例(Use Case)
      用例用于表示系统所提供的服务,它定义了系统是如何被参与者所使用的,它描述的是参与者为了使用系统所提供的某一完整功能而与系统之间发生的一段对话。
    • 通讯关联(Communication Association)
      通讯关联用于表示参与者和用例之间的对应关系,它表示参与者使用了系统中的哪些服务(用例),或者说系统所提供的服务(用例)是被哪些参与者所使用的。

    这大三种模型元素在UML中的表述如下图所示。


    以银行自动提款机(ATM)为例,它的主要功能可以由下面的用例图来表示。ATM的主要使用者是银行客户,客户主要使用自动提款机来进行银行帐户的查询、提款和转帐交易。


    通讯关联表示的是参与者和用例之间的关系,箭头表示在这一关系中哪一方是对话的主动发起者,箭头所指方是对话的被动接受者;如果你不想强调对话中的主动与被动关系,可以使用不带箭头的关联实线。在参与者和用例之间的信息流不是由通讯关联来表示的,该信息流是缺省存在的(用例本身描述的就是参与者和系统之间的对话),并且信息流向是双向的,它与通讯关联箭头所指的方向亳无关系。

    1.2 用例的内容

    用例图使我们对系统的功能有了一个整体的认知,我们可以知道有哪些参与者会与系统发生交互,每一个参与者需要系统为它提供什么样的服务。用例描述的是参与者与系统之间的对话,但是这个对话的细节并没有在用例图中表述出来,针对每一个用例我们可以用事件流来描述这一对话的细节内容。如在ATM系统中的"提款"用例可以用事件流表述如下:

    提款-基本事件流

    1. 用户插入信用卡

    2. 输入密码

    3. 输入提款金额

    4. 提取现金

    5. 退出系统,取回信用卡

    但是这只描述了提款用例中最顺利的一种情况,作为一个实用的系统,我们还必须考虑可能发生的各种其他情况,如信用卡无效、输入密码错、用户帐号中的现金余额不够等,所有这些可能发生的各种情况(包括正常的和异常的)被称之为用例的场景(Scenario),场景也被称作是用例的实例(Instance)。在用例的各种场景中,最常见的场景是用基本流(Basic Flow)来描述的,其他的场景则是用备选流(Alternative Flow)来描述。对于ATM系统中的"提款"用例,我们可以得到如下一些备选流:

    提款-备选事件流

    备选流一:用户可以在基本流中的任何一步选择退出,转至基本流步骤5。

    备选流二:在基本流步骤1中,用户插入无效信用卡,系统显示错误并退出信用卡,用例结束。

    备选流三:在基本流步骤2中,用户输入错误密码,系统显示错误并提示用户重新输入密码,重新回到基本流步骤2;三次输入密码错误后,信用卡被系统没收,用例结束。

    通过基本流与备选流的组合,就可以将用例所有可能发生的各种场景全部描述清楚。我们在描述用例的事件流的时候,就是要尽可能地将所有可能的场景都描述出来,以保证需求的完备性。

    1.3 用例方法的优点

    用例方法完全是站在用户的角度上(从系统的外部)来描述系统的功能的。在用例方法中,我们把被定义系统看作是一个黑箱,我们并不关心系统内部是如何完成它所提供的功能的。用例方法首先描述了被定义系统有哪些外部使用者(抽象成为Actor),这些使用者与被定义系统发生交互;针对每一参与者,用例方法又描述了系统为这些参与者提供了什么样的服务(抽象成为Use Case),或者说系统是如何被这些参与者使用的。所以从用例图中,我们可以得到对于被定义系统的一个总体印象。

    与传统的功能分解方式相比,用例方法完全是从外部来定义系统的功能,它把需求与设计完全分离开来。在面向对象的分析设计方法中,用例模型主要用于表述系统的功能性需求,系统的设计主要由对象模型来记录表述。另外,用例定义了系统功能的使用环境与上下文,每一个用例描述的是一个完整的系统服务。用例方法比传统的SRS更易于被用户所理解,它可以作为开发人员和用户之间针对系统需求进行沟通的一个有效手段。

    在RUP中,用例被作为整个软件开发流程的基础,很多类型的开发活动都把用例作为一个主要的输入工件(Artifact),如项目管理、分析设计、测试等。根据用例来对目标系统进行测试,可以根据用例中所描述的环境和上下文来完整地测试一个系统服务,可以根据用例的各个场景(Scenario)来设计测试用例,完全地测试用例的各种场景可以保证测试的完备性。




    回页首

    2. 建立用例模型

    使用用例的方法来描述系统的功能需求的过程就是用例建模,用例模型主要包括以下两部分内容:

    • 用例图(Use Case Diagram)
      确定系统中所包含的参与者、用例和两者之间的对应关系,用例图描述的是关于系统功能的一个概述。
    • 用例规约(Use Case Specification)
      针对每一个用例都应该有一个用例规约文档与之相对应,该文档描述用例的细节内容。

    在用例建模的过程中,我们建议的步聚是先找出参与者,再根据参与者确定每个参与者相关的用例,最后再细化每一个用例的用例规约。

    2.1 寻找参与者

    所谓的参与者是指所有存在于系统外部并与系统进行交互的人或其他系统。通俗地讲,参与者就是我们所要定义系统的使用者。寻找参与者可以从以下问题入手:

    • 系统开发完成之后,有哪些人会使用这个系统?
    • 系统需要从哪些人或其他系统中获得数据?
    • 系统会为哪些人或其他系统提供数据?
    • 系统会与哪些其他系统相关联?
    • 系统是由谁来维护和管理的?

    这些问题有助于我们抽象出系统的参与者。对于ATM机的例子,回答这些问题可以使我们找到更多的参与者:操作员负责维护和管理ATM机系统、ATM机也需要与后台服务器进行通讯以获得有关用户帐号的相关信息。


    2.1.1 系统边界决定了参与者

    参与者是由系统的边界所决定的,如果我们所要定义的系统边界仅限于ATM机本身,那么后台服务器就是一个外部的系统,可以抽象为一个参与者。


    如果我们所要定义的系统边界扩大至整个银行系统,ATM机和后台服务器都是整个银行系统的一部分,这时候后台服务器就不再被抽象成为一个参与者。


    值得注意的是,用例建模时不要将一些系统的组成结构作为参与者来进行抽象,如在ATM机系统中,打印机只是系统的一个组成部分,不应将它抽象成一个独立的参与者;在一个MIS管理系统中,数据库系统往往只作为系统的一个组成部分,一般不将其单独抽象成一个参与者。

    2.1.2 特殊的参与者――系统时钟

    有时候我们需要在系统内部定时地执行一些操作,如检测系统资源使用情况、定期地生成统计报表等等。从表面上看,这些操作并不是由外部的人或系统触发的,应该怎样用用例方法来表述这一类功能需求呢?对于这种情况,我们可以抽象出一个系统时钟或定时器参与者,利用该参与者来触发这一类定时操作。从逻辑上,这一参与者应该被理解成是系统外部的,由它来触发系统所提供的用例对话。


    2.2 确定用例

    找到参与者之后,我们就可以根据参与者来确定系统的用例,主要是看各参与者需要系统提供什么样的服务,或者说参与者是如何使用系统的。寻找用例可以从以下问题入手(针对每一个参与者):

    • 参与者为什么要使用该系统?
    • 参与者是否会在系统中创建、修改、删除、访问、存储数据?如果是的话,参与者又是如何来完成这些操作的?
    • 参与者是否会将外部的某些事件通知给该系统?
    • 系统是否会将内部的某些事件通知该参与者?

    综合以上所述,ATM系统的用例图可表示如下,


    在用例的抽取过程中,必须注意:用例必须是由某一个主角触发而产生的活动,即每个用例至少应该涉及一个主角。如果存在与主角不进行交互的用例,就可以考虑将其并入其他用例;或者是检查该用例相对应的参与者是否被遗漏,如果是,则补上该参与者。反之,每个参与者也必须至少涉及到一个用例,如果发现有不与任何用例相关联的参与者存在,就应该考虑该参与者是如何与系统发生对话的,或者由参与者确定一个新的用例,或者该参与者是一个多余的模型元素,应该将其删除。

    可视化建模的主要目的之一就是要增强团队的沟通,用例模型必须是易于理解的。用例建模往往是一个团队开发的过程,系统分析员在建模过程中必须注意参与者和用例的名称应该符合一定的命名约定,这样整个用例模型才能够符合一定的风格。如参与者的名称一般都是名词,用例名称一般都是动宾词组等。

    对于同一个系统,不同的人对于参与者和用例都可能有不同的抽象结果,因而得到不同的用例模型。我们需要在多个用例模型方案中选择一种"最佳"(或"较佳")的结果,一个好的用例模型应该能够容易被不同的涉众所理解,并且不同的涉众对于同一用例模型的理解应该是一致的。

    2.3 描述用例规约

    应该避免这样一种误解――认为由参与者和用例构成的用例图就是用例模型,用例图只是在总体上大致描述了系统所能提供的各种服务,让我们对于系统的功能有一个总体的认识。除此之外,我们还需要描述每一个有例的详细信息,这些信息包含在用例规约中,用例模型是由用例图和每一个用例的详细描述――用例规约所组成的。RUP中提供了用例规约的模板,每一个用例的用例规约都应该包含以下内容:

    • 简要说明 (Brief Description)
      简要介绍该用例的作用和目的。
    • 事件流 (Flow of Event)
      包括基本流和备选流,事件流应该表示出所有的场景。
    • 用例场景 (Use-Case Scenario)
      包括成功场景和失败场景,场景主要是由基本流和备选流组合而成的。
    • 特殊需求 (Special Requirement)
      描述与该用例相关的非功能性需求(包括性能、可靠性、可用性和可扩展性等)和设计约束(所使用的操作系统、开发工具等)。
    • 前置条件 (Pre-Condition)
      执行用例之前系统必须所处的状态。
    • 后置条件 (Post-Condition)
      用例执行完毕后系统可能处于的一组状态。

    用例规约基本上是用文本方式来表述的,为了更加清晰地描述事件流,也可以选择使用状态图、活动图或序列图来辅助说明。只要有助于表达的简洁明了,就可以在用例中任意粘贴用户界面和流程的图形化显示方式,或是其他图形。如活动图有助于描述复杂的决策流程,状态转移图有助于描述与状态相关的系统行为,序列图适合于描述基于时间顺序的消息传递。

    2.3.1 基本流

    基本流描述的是该用例最正常的一种场景,在基本流中系统执行一系列活动步骤来响应参与者提出的服务请求。我们建议用以下格式来描述基本流:

    1) 每一个步骤都需要用数字编号以清楚地标明步骤的先后顺序。

    2) 用一句简短的标题来概括每一步骤的主要内容,这样阅读者可以通过浏览标题来快速地了解用例的主要步骤。在用例建模的早期,我们也只需要描述到事件流步骤标题这一层,以免过早地陷入到用例描述的细节中去。

    3) 当整个用例模型基本稳定之后,我们再针对每一步骤详细描述参与者和系统之间所发生的交互。建议采用双向(roundtrip)描述法来保证描述的完整性,即每一步骤都需要从正反两个方面来描述:(1)参与者向系统提交了什么信息;(2)对此系统有什么样的响应。具体例子请参见附录。

    在描述参与者和系统之间的信息交换时,需指出来回传递的具体信息。例如,只表述参与者输入了客户信息就不够明确,最好明确地说参与者输入了客户姓名和地址。通常可以利用词汇表让用例的复杂性保持在可控范围内,可以在词汇表中定义客户信息等内容,使用例不至于陷入过多的细节。

    2.3.2 备选流

    备选流负责描述用例执行过程中异常的或偶尔发生的一些情况,备选流和基本流的组合应该能够覆盖该用例所有可能发生的场景。在描述备选流时,应该包括以下几个要素:

    1) 起点:该备选流从事件流的哪一步开始;

    2) 条件:在什么条件下会触发该备选流;

    3) 动作:系统在该备选流下会采取哪些动作;

    4) 恢复:该备选流结束之后,该用例应如何继续执行。

    备选流的描述格式可以与基本流的格式一致,也需要编号并以标题概述其内容,编号前可以加以字母前缀A(Alternative)以示与基本流步骤相区别。

    2.3.3 用例场景

    用例在实际执行的时候会有很多的不同情况发生,称之为用例场景;也可以说场景是用例的实例,我们在描述用例的时候要覆盖所有的用例场景,否则就有可能导致需求的遗漏。在用例规约中,场景的描述可以由基本流和备选流的组合来表示。场景既可以帮助我们防止需求的遗漏,同时也可以对后续的开发工作起到很大的帮助:开发人员必须实现所有的场景、测试人员可以根据用例场景来设计测试用例。

    2.3.4 特殊需求

    特殊需求通常是非功能性需求,它为一个用例所专有,但不适合在用例的事件流文本中进行说明。特殊需求的例子包括法律或法规方面的需求、应用程序标准和所构建系统的质量属性(包括可用性、可靠性、性能或支持性需求等)。此外,其他一些设计约束,如操作系统及环境、兼容性需求等,也可以在此节中记录。

    需要注意的是,这里记录的是专属于该用例的特殊需求;对于一些全局的非功能性需求和设计约束,它们并不是该用例所专有的,应把它们记录在《补充规约》中。

    2.3.5 前置和后置条件

    前置条件是执行用例之前必须存在的系统状态,后置条件是用例一执行完毕后系统可能处于的一组状态。

    2.4 检查用例模型

    用例模型完成之后,可以对用例模型进行检查,看看是否有遗漏或错误之处。主要可以从以下几个方面来进行检查:

    • 功能需求的完备性
      现有的用例模型是否完整地描述了系统功能,这也是我们判断用例建模工作是否结束的标志。如果发现还有系统功能没有被记录在现有的用例模型中,那么我们就需要抽象一些新的用例来记录这些需求,或是将他们归纳在一些现有的用例之中。
    • 模型是否易于理解
      用例模型最大的优点就在于它应该易于被不同的涉众所理解,因而用例建模最主要的指导原则就是它的可理解性。用例的粒度、个数以及模型元素之间的关系复杂程度都应该由该指导原则决定。
    • 是否存在不一致性
      系统的用例模型是由多个系统分析员协同完成的,模型本身也是由多个工件所组成的,所以我们要特别注意不同工件之前是否存在前后矛盾或冲突的地方,避免在模型内部产生不一致性。不一致性会直接影响到需求定义的准确性。
    • 避免二义性语义
      好的需求定义应该是无二义性的,即不同的人对于同一需求的理解应该是一致的。在用例规约的描述中,应该避免定义含义模糊的需求,即无二义性。




    回页首

    3. 系统需求

    RUP中根据FURPS+模型将系统需求分为以下几类:

    • 功能(Functionality)
    • 可用性(Usability)
    • 可靠性(Reliability)
    • 性能(Performance)
    • 可支持性(Supportability)
    • 设计约束等

    除了第一项功能性需求之外的其他需求都归之为非功能性需求。

    3.1 需求工件集

    用例模型主要用于描述系统的功能性需求,对于其他的非功能性需要用其他文档来记录。RUP中定义了如下的需求工件集合。

    • 用例模型:记录功能性需求
      • 用例图:描述参与者和用例之间的关系
      • 用例规约:描述每一个用例的细节信息
    • 补充规约:记录一些全局性的功能需求、非功能性需求和设计约束等
    • 词汇表:记录一些系统需求相关的术语

    在实际应用中,除了这些工件之外,我们还可以根据实际需求灵活选用其他形式的文档来补充说明需求。并不是所有的系统需求都适保合用用例模型来描述的,如编译器,我们很难用用例方法来表述它所处理的语言的方法规则,在这种情况下,采用传统的BNF范式来表述更加合适一些。在电信软件行业中,很多电信标准都是采用SDL语言来描述的,我们也不必用UML来改写这些标准(UML对SDL存在着这样的兼容性),只需将SDL形式的电信标准作为需求工件之一,在其他工件中对其加以引用就可以了。总之,万万不可拘泥于用例建模的形式,应灵活运用各种方式的长处。

    3.2 补充规约

    补充规约记录那些在用例模型中不易表述的系统需求,主要包括以下内容。

    • 功能性
      功能性需求主要在用例模型中刻画,但是也有部分需求不适合在用例中表述。有些功能性需求是全局性的,适用于所有的用例,如出错处理、I18N支持等,我们不需要在所有的用例中描述这些功能性需求,只需要在补充规约中统一描述就可以了。
    • 可用性
      记录所有可用性相关的需求,如系统的使用者所需要的培训时间、是否应附合一些常见的可用性标准如Windows界面风格等。
    • 可靠性
      定义系统可靠性相关的各种指标,包括:
      • 可用性:指出可用时间百分比(xx.xx%),系统处于使用、维护、降级模式等操作的小时数;
      • 平均故障间隔时间(MTBF):通常表示为小时数,但也可表示为天数、月数或年数;
      • 平均修复时间(MTTR):系统在发生故障后可以暂停运行的时间;
      • 精确度:指出系统输出要求具备的精密度(分辨率)和精确度(按照某一已知的标准);
      • 最高错误或缺陷率:通常表示为bugs/KLOC(每千行代码的错误数目)或bugs/function-point(每个功能点的错误数目)。
    • 性能
      记录系统性能相关的各种指标,包括:
      • 对事务的响应时间(平均、最长);
      • 吞吐量(例如每秒处理的事务数);
      • 容量(例如系统可以容纳的客户或事务数);
      • 降级模式(当系统以某种形式降级时可接受的运行模式);
      • 资源利用情况:内存、磁盘、通信等。
    • 可支持性
      定义所有与系统的可支持性或可维护性相关的需求,其中包括编码标准、命名约定、类库、如何来对系统进行维护操作和相应的维护实用工具等。
    • 设计约束
      设计约束代表已经批准并必须遵循的设计决定,其中包括软件开发流程、开发工具、系统构架、编程语言、第三方构件类库、运行平台和数据库系统等等。

    3.3 词汇表

    词汇表主要用于定义项目特定的术语,它有助于开发人员对项目中所用的术语有统一的理解和使用,它也是后续阶段中进行对象抽象的基础。




    回页首

    4. 调整用例模型

    在一般的用例图中,我们只表述参与者和用例之间的关系,即它们之间的通讯关联。除此之外,我们还可以描述参与者与参与者之间的泛化(generalization)、用例和用例之间的包含(include)、扩展(extend)和泛化(generalization)关系。我们利用这些关系来调整已有的用例模型,把一些公共的信息抽取出来重用,使得用例模型更易于维护。但是在应用中要小心选用这些关系,一般来说这些关系都会增加用例和关系的个数,从而增加用例模型的复杂度。而且一般都是在用例模型完成之后才对用例模型进行调整,所以在用例建模的初期不必要急于抽象用例之间的关系。

    4.1 参与者之间的关系

    参与者之间可以有泛化(Generalization)关系(或称为"继承"关系)。例如在需求分析中常见的权限控制问题(如下图所示),一般的用户只可以使用一些常规的操作,而管理员除了常规操作之外还需要进行一些系统管理工作,操作员既可以进行常规操作又可以进行一些配置操作。


    在这个例子中我们会发现管理员和操作员都是一种特殊的用户,他们拥有普通用户所拥有的全部权限,此外他们还有自己独有的权限。这里我们可进一步把普通用户和管理员、操作员之间的关系抽象成泛化(Generalization)关系,管理员和操作员可以继承普通用户的全部特性(包括权限),他们又可以有自己独有的特性(如操作、权限等)。这样可以显著减速少用例图中通讯关联的个数,简化用例模型,使之更易于理解。


    4.2 用例之间的关系

    用例描述的是系统外部可见的行为,是系统为某一个或几个参与者提供的一段完整的服务。从原则上来讲,用例之间都是并列的,它们之间并不存在着包含从属关系。但是从保证用例模型的可维护性和一致性角度来看,我们可以在用例之间抽象出包含(include)、扩展(extend)和泛化(generalization)这几种关系。这几种关系都是从现有的用例中抽取出公共的那部分信息,然后通后过不同的方法来重用这部公共信息,以减少模型维护的工作量。

    4.2.1 包含(include)

    包含关系是通过在关联关系上应用<<include>>构造型来表示的,如下图所示。它所表示的语义是指基础用例(Base)会用到被包含用例(Inclusion),具体地讲,就是将被包含用例的事件流插入到基础用例的事件流中。


    包含关系是UML1.3中的表述,在UML1.1中,同等语义的关系被表述为使用(uses),如下图。


    在ATM机中,如果查询、取现、转帐这三个用例都需要打印一个回执给客户,我们就可以把打印回执这一部分内容提取出来,抽象成为一个单独的用例"打印回执",而原有的查询、取现、转帐三个例都会包含这个用例。每当以后要对打印回执部分的需求进行修改时,就只需要改动一个用例,而不用在每一个用例都作相应修改,这样就提高了用例模型的可维护性。


    在基础用例的事件流中,我们只需要引用被包含用例即可。

    查询-基本事件流

    1. 用户插入信用卡

    2. 输入密码

    3. 选择查询

    4. 查看帐号余额

    5. 包含用例"打印回执"

    6. 退出系统,取回信用卡

    在这个例子中,多个用例需要用到同一段行为,我们可以把这段共同的行为单独抽象成为一个用例,然后让其他的用例来包含这一用例。从而避免在多个用例中重复性地描述同一段行为,也可以防止该段行为在多个用例中的描述出现不一致性。当需要修改这段公共的需求时,我们也只需要修改一个用例,避免同时修改多个用例而产生的不一致性和重复性工作。

    有时当某一个用例的事件流过于复杂时,为了简化用例的描述,我们也可以把某一段事件流抽象成为一个被包含的用例。这种情况类似于在过程设计语言中,将程序的某一段算法封装成一个子过程,然后再从主程序中调用这一子过程。

    4.2.2 扩展(extend)

    扩展(extend)关系如下图所示,基础用例(Base)中定义有一至多个已命名的扩展点,扩展关系是指将扩展用例(Extension)的事件流在一定的条件下按照相应的扩展点插入到基础用例(Base)中。对于包含关系而言,子用例中的事件流是一定插入到基础用例中去的,并且插入点只有一个。而扩展关系可以根据一定的条件来决定是否将扩展用例的事件流插入基础用例事件流,并且插入点可以有多个。


    例如对于电话业务,可以在基本通话(Call)业务上扩展出一些增值业务如:呼叫等待(Call Waiting)和呼叫转移(Call Transfer)。我们可以用扩展关系将这些业务的用例模型描述如下。




    在这个例子中,呼叫等待和呼叫转移都是对基本通话用例的扩展,但是这两个用例只有在一定的条件下(如应答方正忙或应答方无应答)才会将被扩展用例的事件流嵌入基本通话用例的扩展点,并重用基本通话用例中的事件流。

    值得注意的是扩展用例的事件流往往可以也可抽象为基础用例的备选流,如上例中的呼叫等待和呼叫转移都可以作为基本通话用例的备选流而存在。但是基本通话用例已经是一个很复杂的用例了,选用扩展关系将增值业务抽象成为单独的用例可以避免基础用例过于复杂,并且把一些可选的操作独立封装在另外的用例中。

    4.2.3 泛化(generalization)

    当多个用例共同拥有一种类似的结构和行为的时候,我们可以将它们的共性抽象成为父用例,其他的用例作为泛化关系中的子用例。在用例的泛化关系中,子用例是父用例的一种特殊形式,子用例继承了父用例所有的结构、行为和关系。在实际应用中很少使用泛化关系,子用例中的特殊行为都可以作为父用例中的备选流存在。


    以下是一个用例泛化关系的例子,执行交易是一种交易抽象,执行房产交易和执行证券交易都是一种特殊的交易形式。

    用例泛化关系中的事件流示例如下:


    4.3 调整用例模型

    用例模型建成之后,我们可以对用例模型进行检视,看是否可以进一步简化用例模型、提高重用程度、增加模型的可维护性。主要可以从以下检查点(checkpoints)入手:

    • 用例之间是否相互独立?如果两个用例总是以同样的顺序被激活,可能需要将它们合并为一个用例。
    • 多个用例之间是否有非常相似的行为或事件流?如果有,可以考虑将它们合并为一个用例。
    • 用例事件流的一部分是否已被构建为另一个用例?如果是,可以让该用例包含(include)另一用例。
    • 是否应该将一个用例的事件流插入另一个用例的事件流中?如果是,利用与另一个用例的扩展关系(extend)来建立此模型。




    回页首

    5. 管理用例模型复杂度

    一般小型的系统,其用例模型中包含的参与者和用例不会太多,一个用例图就可以容纳所有的参与者,所有的参与者和用例也可以并存于同一个层次结构中。对于较复杂的大中型系统,用例模型中的参与者和用例会大大增加,我们需要一些方法来有效地管理由于规模上升而造成的复杂度。

    5.1 用例包

    包(Package)是UML中最常用的管理模型复杂度的机制,包也是UML中语义最简单的一种模型元素,它就是一种容器,在包中可以容纳其他任意的模型元素(包括其他的包)。在用例模型中,我们可以用构造型(Sterotype)<<use case>>来扩展标准UML包的语义,这种新的包叫作用例包(Use Case Package),用于分类管理用例模型中的模型元素。

    我们可以根据参与者和用例的特性来对它们进行分类,分别置于不同的用例包管理之下。例如对于一个大型的企业管理信息系统,我们可以根据参与者和用例的内容将它们分别归于人力资源、财务、采购、销售、客务服务这些用例包之下。这样我们将整个用例模型划分成为两个层次,在第一层次我们看到的是系统功能总共分为五部分,在第二层次我们可以分别看到每一用例包内部的参与者和用例。


    一个用例模型需要有多少个用例包取决你想怎么样来管理用例模型的复杂度(包括参与者和用例的个数,以及它们之间的相互关系)。UML中的包其实就类似于文件系统中的目录,文件数量少的时候不需要额外的目录,文件数量一多就需要有多个目录来分类管理,同样一组文件不同的人会创建不同的目录结构来进行管理,关键是要保证在目录结构下每一个文件都要易于访问。同样的道理存在于用例建模之中,如何创建用例包以及用例包的个数取决于不同的系统和系统分析员,但要保证整个用例模型易于理解。

    5.2 用例的粒度

    我的系统需要有多少个用例?这是很多人在用例建模时会产生的疑惑。描述同一个系统,不同的人会产生不同的用例模型。例如对于各种系统中常见的"维护用户"用例,它里面包含了添加用户、修改用户信息、删除用户等操作,这些操作在该用例的事件流可以表述成为基本流的子事件流(subflow)。


    维护用户-基本事件流

    该基本流由三个子事件流构成:

    1) 添加用户子事件流

    2) 修改用户 子事件流

    3) 删除用户子事件流

    但是你也可以根据该用例中的具体操作把它抽象成为三个用例,它所表示的系统需求和单个用例的模型是完全一样的。


    应该如何确定用例的粒度呢?在一次技术研讨会上,有人问起Ivar Jacoboson博士,一个系统需要有多少个用例?大师的回答是20个,当然他的意思是最好将用例模型的规模控制在几十个用例左右,这样比较容易来管理用例模型的复杂度。在用例个数大致确定的条件下,我们就很容易来确定用例粒度的大小。对于较复杂的系统,我们需要控制用例模型一级的复杂度,所以可以将复杂度适当地移往每一个用例的内部,也就是让一个用例包含较多的需求信息量。对于比较简单的系统,我们则可以将复杂度适度地曝露在模型一级,也就是我们可以将较复杂的用例分解成为多个用例。

    用例的粒度不但决定了用例模型级的复杂度,而且也决定了每一个用例内部的复杂度。我们应该根据每个系统的具体情况,因时因宜地来把握各个层次的复杂度,在尽可能保证整个用例模型的易理解性前提下决定用例的大小和数目。

    5.3 用例图

    用例图的主要作用是描述参与者和用例之间的关系,简单的系统中只需要有一个用例图就可以把所有的关系都描述清楚。复杂的系统中可以有多个用例图,例如每个用例包都可以有一个独立的用例图来描述该用例包中所有的参与者和用例的关系。

    在一个用例模型中,如果参与者和用例之间存在着多对多的关系,并且他们之间的关系比较复杂,如果在同一个用例图中表述所有的参与者和用例就显得不够清晰,这时我们可创建多个用例图来分别表示各种关系。


    如果想要强调某一个参与者和多个用例的关系,你就可以以该参与者为中心,用一个用例图表述出该参与者和多个用例之间的关系。在这个用例图中,我们强调的是该参与者会使用系统所提供的哪些服务。


    如果想要强调某一个用例和多个参与者之间的关系,你就可以以该用例为中心,用一个用例图表述出该用例和多个参与者之间的关系。在这个用例图中,我们强调的是该用例会涉及到哪些参与者,或者说该用例所表示的系统服务有哪些使用者。


    总之在用例建模过程中,你可以根据自己的需要创建任意多个用例图,用不同的用例来强调参与者和用例之间不同的关系。但是最重要的是要考虑整个用例模型的可理解性,如果可以用一个用例图把意思表述清楚,就不要再用第二个,因为越是简洁的模型越易于理解。

  • ASP.NET中常用的26个优化性能方法

    2009-02-13 09:50:27

    . 数据库访问性能优化 
     
    数据库的连接和关闭

    访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源。ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响。系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求。连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能。因此,在建立数据库连接后只有在真正需要操作时才打开连接,使用完毕后马上关闭,从而尽量减少数据库连接打开的时间,避免出现超出连接限制的情况。   

    使用存储过程  
     
    存储过程是存储在服务器上的一组预编译的
    SQL语句,类似于DOS系统中的批处理文件。存储过程具有对数据库立即访问的功能,信息处理极为迅速。使用存储过程可以避免对命令的多次编译,在执行一次后其执行规划就驻留在高速缓存中,以后需要时只需直接调用缓存中的二进制代码即可。另外,存储过程在服务器端运行,独立于ASP.NET程序,便于修改,最重要的是它可以减少数据库操作语句在网络中的传输。

    优化查询语句
      
    ASP.NET中ADO连接消耗的资源相当大,SQL语句运行的时间越长,占用系统资源的时间也越长。因此,尽量使用优化过的SQL语句以减少执行时间。比如,不在查询语句中包含子查询语句,充分利用索引等。   

    2. 字符串操作性能优化 
     
    使用值类型的ToString方法
      
    在连接字符串时,经常使用"+"号直接将数字添加到字符串中。这种方法虽然简单,也可以得到正确结果,但是由于涉及到不同的数据类型,数字需要通过装箱操作转化为引用类型才可以添加到字符串中。但是装箱操作对性能影响较大,因为在进行这类处理时,将在托管堆中分配一个新的对象,原有的值复制到新创建的对象中。使用值类型的ToString方法可以避免装箱操作,从而提高应用程序性能。   

    运用StringBuilder类   

    String类对象是不可改变的,对于String对象的重新赋值在本质上是重新创建了一个String对象并将新值赋予该对象,其方法ToString对性能的提高并非很显著。在处理字符串时,最好使用StringBuilder类,其.NET 命名空间是System.Text。该类并非创建新的对象,而是通过Append,Remove,Insert等方法直接对字符串进行操作,通过ToString方法返回操作结果。   其定义及操作语句如下所示:


    int num;   System.Text.StringBuilder str = new System.Text.StringBuilder(); //创建字符串   str.Append(num.ToString()); //添加数值num   Response.Write(str.ToString); //显示操作结果3. 优化
    Web 服务器计算机和特定应用程序的配置文件以符合您的特定需要

    默认情况下,ASP.NET 配置被设置成启用最广泛的功能并尽量适应最常见的方案。因此,应用程序开发人员可以根据应用程序所使用的功能,优化和更改其中的某些配置,以提高应用程序的性能。下面的列表是您应该考虑的一些选项。

    仅对需要的应用程序启用身份验证。

    默认情况下,身份验证模式为
    Windows,或集成 NTLM。大多数情况下,对于需要身份验证的应用程序,最好在 Machine.config 文件中禁用身份验证,并在 Web.config 文件中启用身份验证。根据适当的请求和响应编码设置来配置应用程序。ASP.NET 默认编码格式为 UTF-8。如果您的应用程序为严格的 ASCII,请配置应用程序使用 ASCII 以获得稍许的性能提高。
      
    考虑对应用程序禁用 AutoEventWireup。

    在 Machine.config 文件中将 AutoEventWireup 属性设置为 false,意味着页面不将方法名与事件进行匹配和将两者挂钩(例如 Page_Load)。如果页面开发人员要使用这些事件,需要在基类中重写这些方法(例如,需要为页面加载事件重写 Page.OnLoad,而不是使用 Page_Load 方法)。如果禁用 AutoEventWireup,页面将通过将事件连接留给页面作者而不是自动执行它,获得稍许的性能提升。

    从请求处理管线中移除不用的模块。

    默认情况下,服务器计算机的 Machine.config 文件中 节点的所有功能均保留为激活。根据应用程序所使用的功能,您可以从请求管线中移除不用的模块以获得稍许的性能提升。检查每个模块及其功能,并按您的需要自定义它。例如,如果您在应用程序中不使用会话状态和输出缓存,则可以从 列表中移除它们,以便请求在不执行
    其他有意义的处理时,不必执行每个模块的进入和离开代码。

    4. 一定要禁用调试模式  

    在部署生产应用程序或进行任何性能测量之前,始终记住禁用调试模式。如果启用了调试模式,应用程序的性能可能受到非常大的影响。   

    5. 对于广泛依赖外部资源的应用程序,请考虑在多处理器计算机上启用网络园艺  

    ASP.NET 进程模型帮助启用多处理器计算机上的可缩放性,将
    工作分发给多个进程(每个CPU一个),并且每个进程都将处理器关系设置为其 CPU。此技术称为网络园艺。如果应用程序使用较慢的数据库服务器或调用具有外部依赖项的 COM 对象(这里只是提及两种可能性),则为您的应用程序启用网络园艺是有益的。但是,在决定启用网络园艺之前,您应该测试应用程序在网络园中的执行情况。   

    6. 只要可能,就缓存数据和页输出  

    ASP.NET 提供了一些简单的机制,它们会在不需要为每个页请求动态计算页输出或数据时缓存这些页输出或数据。另外,通过设计要进行缓存的页和数据请求(特别是在站点中预期将有较大通讯量的区域),可以优化这些页的性能。与 .NET Framework 的任何 Web 窗体功能相比,适当地使用缓存可以更好的提高站点的性能,有时这种提高是超数量级的。使用 ASP.NET 缓存机制有两点需要注意。首先,不要缓存太多项。缓存每个项均有开销,特别是在内存使用方面。不要缓存容易重新计算和很少使用的项。其次,给缓存的项分配的有效期不要太短。很快到期的项会导致缓存中不必要的周转,并且经常导致更多的代码清除和垃圾回收工作。若关心此问题,请监视与 ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate 性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称作内存压力。


    7. 选择适合页面或应用程序的数据查看机制  

    根据您选择在 Web 窗体页显示数据的方式,在便利和性能之间常常存在着重要的权衡。例如,DataGrid Web 服务器控件可能是一种显示数据的方便快捷的方法,但就性能而言它的开销常常是最大的。在某些简单的情况下,您通过生成适当的 HTML 自己呈现数据可能很有效,但是自定义和浏览器定向会很快抵销所获得的额外功效。Repeater Web 服务器控件是便利和性能的折衷。它高效、可自定义且可编程。   

    8. 将 SqlDataReader 类用于快速只进数据游标  

    SqlDataReader 类提供了一种读取从 SQL
    Server 数据库检索的只进数据流的方法。如果当创建 ASP.NET 应用程序时出现允许您使用它的情况,则 SqlDataReader 类提供比 DataSet 类更高的性能。情况之所以这样,是因为 SqlDataReader 使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据。另外,SqlDataReader 类实现 IEnumerable 接口,该接口也允许您将数据绑定到服务器控件。有关更多信息,请参见 SqlDataReader 类。有关 ASP.NET 如何访问数据的信息,请参见通过 ASP.NET 访问数据。   

    9. 将 SQL Server 存储过程用于数据访问  

    在 .NET Framework 提供的所有数据访问方法中,基于 SQL Server 的数据访问是生成高性能、可缩放 Web 应用程序的推荐选择。使用托管 SQL Server 提供程序时,可通过使用编译的存储过程而不是特殊查询获得额外的性能提高。   

    10. 避免单线程单元 (STA) COM 组件  

    默认情况下,ASP.NET 不允许任何 STA COM 组件在页面内运行。若要运行它们,必须在 .aspx 文件内将 ASPCompat=true 属性包含在 @ Page 指令中。这样就将执行用的线程池切换到 STA 线程池,而且使 HttpContext 和其他内置对象可用于 COM 对象。前者也是一种性能优化,因为它避免了将多线程单元 (MTA) 封送到 STA 线程的任何调用。使用 STA COM 组件可能大大损害性能,应尽量避免。若必须使用 STA COM 组件,如在任何 interop 方案中,则应在执行期间进行大量调用并在每次调用期间发送尽可能多的信息。另外,小心不要在构造页面期间创建任何 STA COM 组件。例如下面的代码中,在页面构造时将实例化由某个线程创建的 MySTAComponent,而该线程并不是将运行页面的 STA 线程。这可能对性能有不利影响,因为要构造页面就必须完成 MTA 和 STA 线程之间的封送处理。


    <%@ Page Language="VB" ASPCompat="true" %><% Response.Write(myComp.SayHello) %>


    首选机制是推迟对象的创建,直到以后在 STA 线程下执行上述代码,如下面的例子所示。




    <%@ Page Language="VB" ASPCompat="true" %><% Response.Write(myComp.SayHello) %>

    推荐的做法是在需要时或者在 Page_Load 方法中构造任何 COM 组件和外部资源。永远不要将任何 STA COM 组件存储在可以由构造它的线程以外的其他线程访问的共享资源里。这类资源包括像缓存和会话状态这样的资源。即使 STA 线程调用 STA COM 组件,也只有构造此 STA COM 组件的线程能够实际为该调用服务,而这要求封送处理对创建者线程的调用。此封送处理可能产生重大的性能损失和可伸缩性问题。在这种情况下,请研究一下使 COM 组件成为 MTA COM 组件的可能性,或者更好的办法是迁移代码以使对象成为托管对象。   

    11. 将调用密集型的 COM 组件迁移到托管代码  

    .NET Framework 提供了一个简单的方法与传统的 COM 组件进行交互。其优点是可以在保留现有投资的同时利用新的平台。但是在某些情况下,保留旧组件的性能开销使得将组件迁移到托管代码是值得的。每一情况都是不一样的,决定是否需要迁移组件的最好方法是对 Web 站点运行性能测量。建议您研究一下如何将需要大量调用以进行交互的任何COM 组件迁移到托管代码。许多情况下不可能将旧式组件迁移到托管代码,特别是在最初迁移 Web 应用程序时。在这种情况下,最大的性能障碍之一是将数据从非托管环境封送到托管环境。因此,在交互操作中,请在任何一端执行尽可能多的任务,然后进行一个大调用而不是一系列小调用。例如,公共语言运行库中的所有字符串都是 Unicode 的,所以应在调用托管代码之前将组件中的所有字符串转换成 Unicode 格式。另外,一处理完任何 COM 对象或本机资源就释放它们。这样,其他请求就能够使用它们,并且最大限度地减少了因稍后请求垃圾回收器释放它们所引起的性能问题。   

    12. 在 Visual Basic .NET 或 Jscrīpt. 代码中使用早期绑定  

    以往,开发人员喜欢使用 Visual Basic、VBscrīpt. 和 Jscrīpt. 的原因之一就是它们所谓“无类型”的性质。变量不需要显式类型声明,并能够简单地通过使用来创建它们。当从一个类型到另一个类型进行分配时,转换将自动执行。不过,这种便利会大大损害应用程序的性能。Visual Basic 现在通过使用 Option Strict 编译器指令来支持类型安全编程。为了向后兼容,默认情况下,ASP.NET 不启用该选项。但是,为了得到最佳性能,强烈建议在页中启用该选项。若要启用 Option Strict,请将 Strict 属性包括在 @ Page 指令中,或者,对于用户控件,请将该属性包括在 @ Control 指令中。下面的示例演示了如何设置该属性,并进行了四个变量调用以显示使用该属性是如何导致编译器错误的。

    <%@ Page Language="VB" Strict="true" %><% Dim B Dim C As String ' This will cause a compiler error. A = "Hello" ' This will cause a compiler error. B = "World" ' This will not cause a compiler error. C = "!!!!!!" ' But this will cause a compiler error. C = 0 %>Jscrīpt. .NET 也支持无类型编程,但它不提供强制早期绑定的编译器指令。若发生下面任何一种情况,则变量是晚期绑定的:被显式声明为 Object,是无类型声明的类的字段,是无显式类型声明的专用函数或方法成员,并且无法从其使用推断出类型。   最后一个差别比较复杂,因为如果 Jscrīpt. .NET 编译器可以根据变量的使用情况推断出类型,它就会进行优化。在下面的示例中,变量 A 是早期绑定的,但变量 B 是晚期绑定的。

    var A;   var B;   A = "Hello";   B = "World";   B = 0; 为了获得最佳的性能,当声明 Jscrīpt. .NET 变量时,请为其分配一个类型。例如,var A : String。

    13. 使请求管线内的所有模块尽可能高效  

    请求管线内的所有模块在每次请求中都有机会被运行。因此,当请求进入和离开模块时快速地触发代码至关重要,特别是在不使用模块功能的代码路径里。分别在使用及不使用模块和配置文件时执行吞吐量测试,对确定这些方法的执行速度非常有用。

    14. 使用 HttpServerUtility.Transfer 方法在同一应用程序的页面间重定向  

    采用 Server.Transfer 语法,在页面中使用该方法可避免不必要的客户端重定向。
      
    15. 必要时调整应用程序每个辅助进程的线程数  

    ASP.NET 的请求结构试图在执行请求的线程数和可用资源之间达到一种平衡。已知一个使用足够 CPU 功率的应用程序,该结构将根据可用于请求的 CPU 功率,来决定允许同时执行的请求数。这项技术称作线程门控。但是在某些条件下,线程门控算法不是很有效。通过使用与 ASP.NET Applications 性能对象关联的 Pipeline Instance Count 性能计数器,可以在 PerfMon 中监视线程门控。当页面调用外部资源,如数据库访问或 XML Web services 请求时,页面请求通常停止并释放 CPU。如果某个请求正在等待被处理,并且线程池中有一个线程是自由的,那么这个正在等待的请求将开始被处理。遗憾的是,有时这可能导致 Web 服务器上存在大量同时处理的请求和许多正在等待的线程,而它们对服务器性能有不利影响。通常,如果门控因子是外部资源的响应时间,则让过多请求等待资源,对 Web 服务器的吞吐量并无帮助。为缓和这种情况,可以通过更改 Machine.config 配置文件节点的 maxWorkerThreads 和 maxIOThreads 属性,手动设置进程中的线程数限制。   

    注意:辅助线程是用来处理 ASP.NET 请求的,而 IO 线程则是用于为来自文件、数据库或 XML Web services 的数据提供服务的。分配给这些属性的值是进程中每个 CPU 每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。无论如何,对于有四个或八个 CPU 的计算机,最好更改默认值。对于有一个或两个处理器的计算机,默认值就可以,但对于有更多处理器的计算机的性能,进程中有一百或两百个线程则弊大于利。注意进程中有太多线程往往会降低服务器的速度,因为额外的上下文交换导致
    操作系统将 CPU 周期花在维护线程而不是处理请求上。   

    16. 适当地使用公共语言运行库的垃圾回收器和自动内存管理  

    小心不要给每个请求分配过多内存,因为这样垃圾回收器将必须更频繁地进行更多的工作。另外,不要让不必要的指针指向对象,因为它们将使对象保持活动状态,并且应尽量避免含 Finalize 方法的对象,因为它们在后面会导致更多的工作。特别是在 Finalize 调用中永远不要释放资源,因为资源在被垃圾回收器回收之前可能一直消耗着内存。最后这个问题经常会对 Web 服务器环境的性能造成毁灭性的打击,因为在等待 Finalize 运行时,很容易耗尽某个特定的资源。   

    17. 如果有大型 Web 应用程序,可考虑执行预批编译  

    每当发生对目录的第一次请求时都会执行批编译。如果目录中的页面没有被分析并编译,此功能会成批分析并编译目录中的所有页面,以便更好地利用磁盘和内存。如果这需要很长时间,则将快速分析并编译单个页面,以便请求能被处理。此功能带给 ASP.NET 性能上的好处,因为它将许多页面编译为单个程序集。从已加载的程序集访问一页比每页加载新的程序集要快。批编译的缺点在于:如果服务器接收到许多对尚未编译的页面的请求,那么当 Web 服务器分析并编译它们时,性能可能较差。为解决这个问题,可以执行预批编译。为此,只需在应用程序激活之前向它请求一个页面,无论哪页均可。然后,当用户首次访问您的站点时,页面及其程序集将已被编译。没有简单的机制可以知道批编译何时发生。需一直等到 CPU 空闲或者没有更多的编译器进程(例如 csc.exe(C# 编译器)或 vbc.exe(Visual Basic 编译器))启动。还应尽量避免更改应用程序的 \bin 目录中的程序集。更改页面会导致重新分析和编译该页,而替换 \bin 目录中的程序集则会导致完全重新批编译该目录。在包含许多页面的大规模站点上,更好的办法可能是根据计划替换页面或程序集的频繁程度来设计不同的目录结构。不常更改的页面可以存储在同一目录中并在特定的时间进行预批编译。经常更改的页面应在它们自己的目录中(每个目录最多几百页)以便快速编译。Web 应用程序可以包含许多子目录。批编译发生在目录级,而不是应用程序级。

    18. 不要依赖代码中的异常  

    因为异常大大地降低性能,所以您不应该将它们用作控制正常程序流程的方式。如果有可能检测到代码中可能导致异常的状态,请执行这种操作。不要在处理该状态之前捕获异常本身。常见的方案包括:检查 null,分配给将分析为数字值的 String 一个值,或在应用数学运算前检查特定值。下面的示例演示可能导致异常的代码以及测试是否存在某种状态的代码。两者产生相同的结果。


     try   {   result = 100 / num;   }   catch (Exception e)   {   result = 0;   }   // ...to this.   if (num != 0)   result = 100 / num;   else   result = 0;

    19. 使用 HttpResponse.Write 方法进行字符串串联

    该方法提供非常有效的缓冲和连接服务。但是,如果您正在执行广泛的连接,请使用多个 Response.Write 调用。下面示例中显示的技术比用对 Response.Write 方法的单个调用连接字符串更快。




    Response.Write("a");   Response.Write(myString);   Response.Write("b");   Response.Write(myObj.ToString());   Response.Write("c");   Response.Write(myString2);   Response.Write("d"); 20. 除非有特殊的原因要关闭缓冲,否则使其保持打开

    禁用 Web 窗体页的缓冲会导致大量的性能开销。   

    21. 只在必要时保存服务器控件视图状态  

    自动视图状态管理是服务器控件的功能,该功能使服务器控件可以在往返过程上重新填充它们的属性值(您不需要编写任何代码)。但是,因为服务器控件的视图状态在隐藏的窗体字段中往返于服务器,所以该功能确实会对性能产生影响。您应该知道在哪些情况下视图状态会有所帮助,在哪些情况下它影响页的性能。例如,如果您将服务器控件绑定到每个往返过程上的数据,则将用从数据绑定操作获得的新值替换保存的视图状态。在这种情况下,禁用视图状态可以节省处理时间。默认情况下,为所有服务器控件启用视图状态。若要禁用视图状态,请将控件的EnableViewState 属性设置为 false,如下面的 DataGrid 服务器控件示例所示。




    您还可以使用 @ Page 指令禁用整个页的视图状态。当您不从页回发到服务器时,这将十分有用:


    <%@ Page EnableViewState="false" %>

    注意:@ Control 指令中也支持 EnableViewState 属性,该指令允许您控制是否为用户控件启用视图状态。若要分析页上服务器控件使用的视图状态的数量,请(通过将 trace="true" 属性包括在 @ Page 指令中)启用该页的跟踪并查看 Control Hierarchy 表的 Viewstate 列。有关跟踪和如何启用它的信息,请参见 ASP.NET 跟踪。

    22. 避免到服务器的不必要的往返过程  

    虽然您很可能希望尽量多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些情况下却不宜使用 ASP.NET 服务器控件和回发事件处理。通常,只有在检索或存储数据时,您才需要启动到服务器的往返过程。多数数据操作可在这些往返过程间的客户端上进行。例如,从 HTML 窗体验证用户输入经常可在数据提交到服务器之前在客户端进行。通常,如果不需要将信息传递到服务器以将其存储在数据库中,那么您不应该编写导致往返过程的代码。如果您开发自定义服务器控件,请考虑让它们为支持 ECMAscrīpt. 的浏览器呈现客户端代码。通过以这种方式使用服务器控件,您可以显著地减少信息被不必要的发送到 Web 服务器的次数。

    使用 Page.IsPostBack 避免对往返过程执行不必要的处理

    如果您编写处理服务器控件回发处理的代码,有时可能需要在首次请求页时执行其他代码,而不是当用户发送包含在该页中的 HTML 窗体时执行的代码。根据该页是否是响应服务器控件事件生成的。

    使用 Page.IsPostBack 属性有条件地执行代码

    例如,下面的代码演示如何创建数据库连接和命令,该命令在首次请求该页时将数据绑定到 DataGrid 服务器控件。


    void Page_Load(Object sender, EventArgs e)   {   // Set up a connection and command here.   if (!Page.IsPostBack)   {   String query = "select * from Authors where FirstName like '%JUSTIN%'";   myCommand.Fill(ds, "Authors");   myDataGrid.DataBind();   }   }

    由于每次请求时都执行 Page_Load 事件,上述代码检查 IsPostBack 属性是否设置为 false。如果是,则执行代码。如果该属性设置为 true,则不执行代码。注意 如果不运行这种检查,回发页的行为将不更改。Page_Load 事件的代码在执行服务器控件事件之前执行,但只有服务器控件事件的结果才可能在输出页上呈现。如果不运行该检查,仍将为 Page_Load 事件和该页上的任何服务器控件事件执行处理。   

    23. 当不使用会话状态时禁用它  

    并不是所有的应用程序或页都需要针对于具体用户的会话状态,您应该对任何不需要会话状态的应用程序或页禁用会话状态。   若要禁用页的会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如:



    <%@ Page EnableSessi %>注意:如果页需要访问会话变量,但不打算创建或修改它们,则将@ Page 指令中的 EnableSessionState 属性设置为ReadOnly。还可以禁用 XML Web services 方法的会话状态。有关更多信息,请参见使用 ASP.NET 和 XML Web services 客户端创建的 XML Web services。若要禁用应用程序的会话状态,请在应用程序 Web.config 文件的 sessionstate 配置节中将 mode 属性设置为 off。例如:



    24. 仔细选择会话状态提供程序  

    ASP.NET 为存储应用程序的会话数据提供了三种不同的方法:进程内会话状态、作为 Windows 服务的进程外会话状态和 SQL Server 数据库中的进程外会话状态。每种方法都有自己的优点,但进程内会话状态是迄今为止速度最快的解决方案。如果只在会话状态中存储少量易失数据,则建议您使用进程内提供程序。进程外解决方案主要用于跨多个处理器或多个计算机缩放应用程序,或者用于服务器或进程重新启动时不能丢失数据的情况。有关更多信息,请参见 ASP.NET 状态管理。   

    25. 不使用不必要的Server Control

    ASP.net中,大量的服务器端控件方便了程序开发,但也可能带来性能的损失,因为用户每操作一次服务器端控件,就产生一次与服务器端的往返过程。因此,非必要,应当少使用Server Control。   

    26. ASP.NET应用程序
    性能测试  

    在对ASP.NET应用程序进行性能测试之前,应确保应用程序没有错误,而且功能正确。具体的性能测试可以采用以下工具进行:Web Application Strees Tool (WAS)是Microsoft发布的一个免费
    测试工具,可以从http://webtool.rte.microsoft.com/上下载。它可以模拟成百上千个用户同时对web应用程序进行访问请求,在服务器上形成流量负载,从而达到测试的目的,可以生成平均TTFB、平均TTLB等性能汇总报告。Application Center Test (ACT) 是一个测试工具,附带于Visual Studio.NET的企业版中,是Microsoft正式支持的web应用程序测试工具。它能够直观地生成图表结果,功能比WAS多,但不具备多个客户机同时测试的能力。服务器操作系统"管理工具"中的"性能"计数器,可以对服务器进行监测以了解应用程序性能。   

    结论:

    对于网站开发人员来说,在编写ASP.NET应用程序时注意性能问题,养成良好的习惯,提高应用程序性能,至少可以推迟必需的硬件升级,降低网站的成本。

  • 转载:公司招聘中不能说的秘密

    2009-02-11 13:31:23

        你是否试过狂轰乱炸地在网上发简历,而回应者却寥寥无几?难道真的是人才饱和了吗?或者是你不够优秀,企业在第一时间就把你给枪毙了呢?这里,会告诉你一些企业人才招聘环节中一些不为外人知道的秘密,了解了这些秘密,应该可以让你以后的应聘变得更加有成效!

     

    现在中国所有招聘网站都是以人事经理为中心,因为他们是给钱的一方,较少从求职者角度考虑,如果我们从求职者立场或者中立立场来看中国招聘网站的生意模式及运作流程,将这个求职者并不知晓的事情公布出来,你就会更好清醒认识招聘网站,也更好的实际的利用人才网站求职:

    

    一、 人才网站与企业人事经理的生意模式对求职者影响:

     人才网站的行规是企业人事经理支付600元,可以在一个月内发布一定的职位让求职者投递简历,可以搜索查看人才网站简历库的简历,还可以下载一定数量的简历主动与求职者联系。而大企业及知名公司一般都是购买1年的招聘服务。问题就出来了:如果这个职位12周企业招到人了,但他购买的是1个月的服务,所以企业的职位还是挂在网上,而且人才网站竞争激烈,一般都会赠送1个月服务。所以一般职位至少1个月挂在人才网站是无效的,是浪费求职者查看与投递简历时间的。

    

     大公司最不可靠,他们利用网络、报纸、现场等多种方式,而1年的大大banner永远挂在人才网站上,他们并不需要人,只是为了广告宣传。

    

     所以人才网站50%以上的职位都是过期的、无效的、不招人或招满人的,所有求职者抱怨我投了那么多简历为什么反馈率那么低,反馈率低是非常正常的。

    

     另外51job网站的反馈率是最低的,这也是行业秘密:因为51Job的资源80%是以报纸招聘为主,报纸招聘效率高,但网站上的职位都是在报纸上刊登过后作为免费与补充服务,所以很多HR只会看报纸上来的简历,网站来的简历基本不看,在加上51job简历投递量实在太大,那些懒惰的HR才懒得一封一封的看,只会用搜索关键字来看,如本科+3工作经验+主管等,其他不符合条件的看都不看一眼。所以你知道为什么一直没有公司找你面试,不是你不行,而是HR都不看你发的简历。下面第二部分告诉你如何被他们搜索到。

    

     现在你知道网络招聘的反馈率为什么那么低了吧。不信你用自动回复邮件形式发送中国3大招聘网站职位HR的邮箱,看看有多少HR看了你的简历,一个工作论坛的网友说,他试着发了100HR邮箱简历,可以通过自动回复的反馈率统计,可以看到50HR不看简历就直接删除,30%根本就不打开邮件,只有20%打开邮箱,还有10%可能已经找到人的。他的分析说即使是中国前3名招聘网站以严格标准来看也只有10%职位是真正要急迫招人的。

    

    二、既然知道一些潜规则,那我们也迎合一些这些规则,告诉你一些网络求职的小秘密

    

    (1)采用行业招聘网站求职。

        因为行业招聘网站是按行业发布职位信息的,所以专业和工作经历比较对口。比如你要找物业管理类的工作,你到万行工作网www.114job.com.cn的物业管理招聘频道上去注册简历就比较好,因为那里全部都是物业管理类的企业在招聘;如果你要找外贸的工作,你就可以万行工作网的外贸招聘频道去找,肯定有大量的外贸工作机会。其它的就不在列举。在目前的情况下,几乎每个行业的人才在万行工作网上都能找到自己的频道。

    

    2)简历要与大公司沾边

    当人事经理搜索招聘网站简历库简历时,一般会以关键字“知名企业名称+职位名称”,比如消费品行业可能喜欢可口可乐及宝洁的人,人事经理会这样搜索,例如:“可口可乐+销售经理”,系统会搜索到简历中出现以上关键字的求职者,如果你的简历里出现知名企业名称的字样,就可以被搜索到,例如:“我在xx矿泉水公司工作,成功地令竞争对手——可口可乐旗下的天与地矿泉水在当地的市场份额减少……”:“我在可口可乐的广州白云区经销商工作”等。又提高了人事经理浏览简历的机会!

    

    (3 经常刷新简历

    当人事经理搜索简历库的简历时,符合条件的简历是按刷新的时间顺序排列,而一般只会看前面一两页。很多求职者其实并不知道刷新简历可以获得更多求职机会。因此每次登陆,最好都刷新简历,刷新以后,就能排在前面,更容易被人事经理找到! 4 不要只应聘最近三天的职位

    一般求职者认为刚刚发布的最新的招聘信息肯定是成功率最大的,其实不然。因为很多企业人事经理没有及时的登陆刷新刊登的职位,所以求职者在搜索职位时刚刷新的职位会排在前面,这些职位应聘的人多,竞争大,相反,一些职位已经是半个月甚至两个月的,应聘的人少,成功率反而高。

    

    5 让你的邮件永远在最前面

    你要知道每天人事经理看求职者邮箱,他们其实是很懒的,100多页简历邮件他们最多只看前5页!你现在应该知道为什么你的求职简历永远没有回应!

    

       所以发邮件到企业指定的邮箱时,怎样才能让你的邮件永远排在最前面,让人事经理每次打开邮箱都首先看到你的邮件?只要在发邮件前,把电脑系统的日期改为一个将来的日期,如2008年,因为大多邮箱都是默认把邮件按日期排序,所以你的邮件起码要到2008年以后才会被排在后面! (如果你求职成功,要向我请客耶!)

    

    (6 新颖的邮件标题

    人事经理每天收到大量的求职电子邮件,求职者一般会按企业要求把邮件题目写成:应聘xx职位,怎样才能吸引人事经理的眼球,让他先打开自己的邮件?可以在邮件题目上做文章。一天人事经理收到几百封邮件,只有标题新颖的才有机会被打开。

    

       例子:我的一个女性朋友发了100多封邮件求职都没有任何反应,因为应聘做文员的太多了,而我这个朋友做过空姐,我将她的邮件标题改为“空姐来广州找工作”,引起绝大部分男人事经理想入非非,结果三天之内有30多个男人事经理通知面试,3个月找不到工作的她而变成3天找到上十份工作。你现在知道邮件标题的重要性了吧。

    

    (7 简历最好放靓照

    对于人事经理来说,每天需要浏览大量简历,如果同等的条件,一般会先通知有照片的求职者来面试,因为通过照片,人事经理对应聘者又多了几分了解。如果是美女,被通知的可能性就更大。我作为人事经理,曾经招聘一个人事主管,收到300多封简历,我找出前30份有相片的前5份,通知了最漂亮相片的2个女孩,就定下了其中的一个。对于一般职位如文职人员之类,中国人的传统还是以貌取人,你即使不漂亮,也照一个艺术照,就增多了面试机会(与其等死,还不如放手一搏),毕竟很现实的是,简历的目的就是有面试的机会,其他就要靠实力与运气了。

     

    (8)求职信“骂”对方公司往往会带来意想不到的效果

    一般人认为在求职信中称赞对方公司会引起好感,其实不然。如果先指出这家公司的缺点,往往会引起关注,语不惊人死不休呢,我作为人事经理,我只会对指出我们缺点的求职者有好感,对恭维我们公司的求职者一般会放在一边。即使你不知道对方公司缺点,你随便写一些永远不会错的:“我认为贵司创新不够,市场表现过于常规化;我以消费者心态观察贵司,发现贵司客户服务还有许多待改进的地方;我发现贵司品牌形象还有可能做的更好……”如闻其详,可面谈。可勾引相关公司面试。只要有面试机会,其他再说。(简历有机会面试目的是一切,手段是无所谓的)。

    

    (9)自己要学会让简历与职位匹配

    2个观念都是有效的:一是不要太在乎对方职位要求的描述,很多职位描述只是写写,连经理都不知道要招什么样的人,如果你看到对方职位要求本科,你是专科就不敢投递简历,那就失去机会了。如果你看到对方要求有5年经验,你只有3年经验,你也不敢投,那完全没有必要。因为人事经理们对职位的描述只是例行公事随便谢谢而已,你千万不要当真!

    

    另外一个匹配观念就是他的职位如何描述,你就改变你的简历换一个说法匹配,如他说要求领导能力强,你的简历也说具有领导才能,他要沟通能力一流,你的简历也说我最擅长沟通。你的简历表面匹配度最高,也可以多增加机会。你可将简历改成为他职位描述完全量身定做的简历。

    其实求职者有更多的面试机会,不但可以增加成功求职机会,还可以增加自己的信心,工资越叫越高还可以积累面试经验。很多优秀的求职者网上发了很多简历没有回应,以为自己不行没有竞争力,只好自动降价,实为可惜! 特别是中国最大的招聘网站51job的简历投递反馈率是同行业最低的,因为它主要资源80%投入报纸,报纸招聘完甚至录取完毕后将职位入库,只是将职位作为一个摆设放到网站。

    要知道现在网络求职的成功率一般2个月是发1000份简历,有8份面试,2份成功,一个是你不想去的,另一个可能是你相对满意的。所以网络求职的朋友千万不要对自己失去信心。

     

  • 《软件性能测试过程详解与案例剖析》——PTGM模型

    2009-02-10 15:22:27

    性能测试模型PTGM(Performance Testing General Model),将性能测试过程分为测试前期准备、测试工具引入、测试计划、测试设计与开发、测试执行和管理以及测试分析等6个步骤。

    1.测试前期准备

    在前期准备阶段,至少要完成两个方面的工作:保证系统稳定和建立合适的测试团队。具体来说,测试前期准备包含如下的活动:

    (1)系统基础功能的验证

    (2)组建测试团队

    (3)测试工具需求确认

    需考虑如下几个方面:操作系统环境(能运行、支持监控)、应用服务器环境(支持监控)、数据库环境(支持监控)、应用使用的协议(是否支持)、网络环境(防火墙、负载均衡)、测试管理支持(测试结果分析和管理)。

    (4)性能预备测试(可选活动)

    所谓预备测试,是在正式的测试之前,通过简单的探索性测试或是其他方法,对系统的性能表现进行初步的了解。

    2.测试工具引入

    (1)工具选择

    (2)工具应用技能培训

    (3)确定工具应用过程

    3.测试计划

    (1)性能测试领域分析

     应用领域 性能测试目标  性能目标 
     能力验证 验证系统在给定环境中的性能能力  重点关注的关键业务响应时间、吞吐量 
     规划能力  验证系统的性能扩展能力,找出系统能力扩充的关键点,给出改善其性能扩展能力的建议 业务的性能瓶颈 
     性能调优  提高系统的性能表现 重点关注的关键业务响应时间、吞吐量 
     发现缺陷  发现系统中的缺陷 无 

    (2)用户活动剖析与业务建模

    用来寻找用户的关键性能关注点。

    用户活动剖析的方法大体分为两种:系统日志分析和用户调查分析。经过用户活动分析之后,最终形成的结果类似于以下的描述:

    用户最关心的业务之一是A业务,该业务具有平均每天3000次业务发生率,业务发生时间集中在9:00~18:00的时间段,业务发生的峰值为每小时1000次。A业务操作路径如下所示:……

    业务建模是对业务穖的行为及其襀方式和方法的建模,一般采用流程图的方式描绘出各进程之间的交互关系和数据流向。

    (3)确定性能目标

    首先从需求和设计中分析出性能测试需求,结合用户活动剖析与业务建模的结果,最终确定性能测试的目标。

    (4)制定测试时间计划

    4.测试设计与开发

    (1)测试环境设计

    此处的测试环境设计包括系统的软/硬件环境、数据环境设计、环境的维护方法。

    (2)测试场景设计

    测试场景模拟的一般是实际业务运行的剖面,其包括业务、业务比例、测试指标的目标以及需要的测试过程中进行监控的性能计数器。

    (3)测试用例设计

    针对每个测试场景规划出相应的工具部署、应用部署、测试方法和步骤。

    (4)脚本和辅助工具开发

    测试脚本是对业务操作的体现,一个脚本一般就是一个业务的过程描述。

    5.测试执行与管理

    (1)建立测试环境

    在设计完成用例之后就会开始该活动。建立测试环境一般包括硬件、软件系统环境的搭建,数据库环境建立,应用系统部署,系统设置参数的调整,以及数据环境准备几个方面的工作内容。

     

    (2)部署测试脚本和测试场景

    部署测试脚本和测试场景活动通过测试工具本身提供的功能来实现。部署活动最终需要保证场景与设计的一致性,保证需要监控的计数器都已经部署好了相应的监控手段。

    (3)执行测试和记录结果

    6.测试分析

    性能分析的通用方法之一是“拐点分析”的方法。“拐点分析”方法是一种利用性能计数器曲线图上的拐点进行性能分析的方法,只要关注性能表现上的“拐点”,获得“拐点”附近的资源使用情况,就能够定位出穖的性能瓶颈。

     

     

  • 测试TCP/IP配置

    2009-02-09 13:40:06

    安装网络硬件和网络协议之后,我们一般要进行TCP/IP协议的测试工作,那么怎样测试才算是比较全面的测试呢?我们认为,全面的测试应包括局域网和互联网两个方面,因此应从局域网和互联网两个方面测试,以下是我们在实际工作中利用命令行测试TCP/IP配置的步骤:
      1、 单击“开始”/“运行”,输入CMD按回车,打开命令提示符窗口。

      2、 首先检查IP地址、子网掩码、默认网关、DNS服务器地址是否正确,输入命令ipconfig /all,按回车。此时显示了你的网络配置,观查是否正确。

      3、 输入ping 127.0.0.1,观查网卡是否能转发数据,如果出现“Request timed out”,表明配置差错或网络有问题。

      4、 Ping一个互联网地址,如ping 202.102.128.68,看是否有数据包传回,以验证与互联网的连接性。

      5、 Ping 一个局域网地址,观查与它的连通性。

      6、 用nslookup测试DNS解析是否正确,输入如nslookup www.163.com,查看是否能解析。

      如果你的计算机通过了全部测试,则说明网络正常,否则网络可能有不同程度的问题。在此不展开详述。不过,要注意,在使用 ping命令时,有些公司会在其主机设置丢弃ICMP数据包,造成你的ping命令无法正常返回数据包,不防换个网站试试。

    ping命令详解

    ping [-n count][-l size][-w timeout]

    -n 发送的ICMP数据包数,默认是4个

    -l 发送的ICMP数据包大小,一般是56K+8K=64K

    -w 超时时间

     

    数据格式:
    数据帧:帧头+IP数据包+帧尾 (帧头包括源和目标主机MAC地址及类型,帧尾是校验字)
    IP数据包:IP头部+TCP数据信息 (IP头包括源和目标主机IP地址、类型、生存期等)
    TCP数据信息:TCP头部+实际数据 (TCP头包括源和目标主机端口号、顺序号、确认号、校
    验字等)

  • 《软件性能测试过程详解与案例剖析》——性能分析方法之处理器

    2009-02-06 18:17:38

    1.首先查看System\%Total Processor Time性能计数器的计数值

    用于体现服务器整体的处理器利用率,对多处理器的系统而言,体现的是所有CPU的平均利用率。

    2.其次查看每个CPU的Processor\%Processor Time和Processor\%User Time和Processor \%Privileged Time

    Processor\%User Time是指系统的非核心操作消耗的CPU时间,如果值大,可以考虑是否能通过算法优化等方法降低这个值。如果该服务器是数据库服务器,Processor\%User Time值大的原因很可能是数据库的排序或是函数操作消耗了过多的CPU时间,可考虑优化数据。

    3.研究系统处理器瓶颈

    查看System\Processor Queue Length的值,若其大于CPU数据的总数+1,说明産了处理器阻塞。在处理器的Processor \%ProcessorTime很高时,一般都伴随着处理器阻塞。

    %DPC Time,越低越好。在多处理器系统中,如果这个值大于50%,并且Processor \%ProcessorTime非常高,加入一个网卡可能会提高性能。

  • 《软件性能测试过程详解与案例剖析》——性能分析方法之内存

    2009-02-06 18:00:09

    1. 首先查看Memory\Available Mbytes指标

    该计数值是描述穖可用内存的直接指标,在对系统进行操作系统级别的内存分析时,首先通过该指标建立一个初步的印象,了解性能测试过程中,系统是否仍然有足够的内存可用。

    2.注意Page/sec、Pages Read/sec和Page Faults/sec的值

    OS经常会利用磁盘交换的方式提高系统可用的内存量或是提高内存的使用效率。而这3个指标直接反映了OS进行磁盘交换的频度。

    若Page/sec的计数持续高于几百,很可能会有内在方面的问题产生,但Page/sec的值很大不一定表现内存有问题,而可能是运行使用内存映射文件的程序所致。Page Faults/sec,页面失效次数越多,说明OS向内存中读取的次数越多。Pages Read/sec的阈值为5,如果值超过5,则可以判断存在内存方面的问题。

    3.根据Physical Disk计数器的值分析性能瓶颈

    对Physical Disk的分析包括对Pages Read/sec和%Disk Time及Average Disk Queue Length的分析。如果Pages Read/sec很低,同时%Disk Time和Average Disk Queue Length的值很高,则可能有磁盘瓶颈。但是,如果队列长度增加的同时Pages Read/sec并未降低,则是由于内存不足。

     

  • 《软件性能测试过程详解与案例剖析》——软件性能测试方法论

    2009-02-05 11:43:35

    1. SEI 负载测试计划过程

    SEI 负载测试计划过程(SEI Load Testing Planning Process)是一个关注于负载测试计划的方法,其目标是产生“清晰、易理解、可验证的负载测试计划”。SEI 负载测试计划过程包括6 个关注的区域(Area):目标、用户、用例、生产环境、测试环境和测试场景。

    SEI 负载测试计划过程在负载测试需要关注的具体内容上提供了参考,但其并不是一个完整的测试过程。

    2. RBI 方法

    RBI(Rapid Bottleneck Identify)方法是一种用于快速识别系统

    性能瓶颈的方法。该方法基于以下一些事实:


    (1)发现的80%系统的性能瓶颈都由吞吐量制约;

    (2)并发用户数和吞吐量瓶颈之间存在一定的关联;

    (3)采用吞吐量测试可以更快速定位问题。

    RBI 方法首先访问服务器上的“小页面”和“简单应用”,从应用服务器、网络等基础的层次上了解系统吞吐量表现;其次选择不同的场景,设定不同的并发用户数,使其吞吐量保持基本一致的增长趋势,通过不断增加并发用户数和吞吐量,观察系统的性能表现。

    在确定具体的性能瓶颈时,RBI 将性能瓶颈的定位按照一种“自上而下”的分析方式进行分析,首先确定是由并发还是由吞吐量引发的性能表现限制,然后从网络、数据库、应用服务器和代码本身4 个环节确定系统性能具体的瓶颈。

    RBI 方法在性能瓶颈的定位过程中能发挥良好的作用,其对性能分析和瓶颈定位的方法值得借鉴,但其也不是完整的性能测试过程。

    3.性能下降曲线分析法


    目标:性能随着用户数的增加而出现下降趋势的曲线分析、查看性能下降的环境点与上下文。确定性能阀值。
    内容:通过单用户区域、性能平坦区域、压力区域、性能拐点进行监控和分析。

    单用户区域——系统的一个单用户的响应时间。

    性能平坦区域——在不进行更多性能调优情况下所能期望达到的最佳性能。这个区域称为基线(Benchmark).

    压力区域——应用“轻微下降”的地方。最大的建议用户负载是人压力区域开始的。

    性能拐点——性能开始“急剧下降”的点。

    4. LoadRunner的性能测试过程

    LoadRunner的性能测试过程分为计划测试、测试设计、创建VU脚本、创建测试场景、运行测试场景和分析结果6个步骤。

    5. Segue提供的性能测试过程

    Segue公司Silk Performer提供的性能测试过程,是一个不断try-check过程。从确定性能基线开始,通过单用户对应用的访问获取性能取值的基线,然后设定可接受的性能目标(响应时间),用不同的并发用户数等重复进行测试。

    此种方法非常适合性能调优和性能优化。

    6.本书提供的PTGM模型

    性能测试模型PTGM(Performance Testing General Model),将性能测试过程分为测试前期准备、测试工具引入、测试计划、测试设计与开发、测试执行和管理以及测试分析等6个步骤。

  • 《软件性能测试过程详解与案例剖析》——术语

    2009-02-04 18:28:53

    1.响应时间

    应用系统从发出请求开始到客户端接收到响应所消耗的时间。

    2.并发用户数

    在实际的性能测试工作中,测试人员常常会关心到并发用户数,也就是从业务角度关注究竟应该设置多少个并发数比较合理,以下是一个估算并发用户数的方法:

      (1) 计算平均的并发用户数: C = nL/T

      (2) 并发用户数峰值: C’ ≈ C+3根号C

      公式(1)中,C是平均的并发用户数;n是login session的数量;L是login session的平均长度;T指考察的时间段长度。

      公式(2)则给出了并发用户数峰值的计算方式中,其中,C’指并发用户数的峰值,C就是公式(1)中得到的平均的并发用户数。该公式的得出是假设用户的login session产生符合泊松分布而估算得到的。

      实例:

      假设有一个OA系统,该系统有3000个用户,平均每天大约有400个用户要访问该系统,对一个典型用户来说,一天之内用户从登录到退出该系统的平均时间为4小时,在一天的时间内,用户只在8小时内使用该系统。

      则根据公式(1)和公式(2),可以得到:

      C = 400*4/8 = 200

      C’≈200+3*根号200 = 242

    另外,如果能够知道平均每个用户发出的请求数(假设为u),则系统的总吞吐量就可估算为u*C

    3. 吞吐量

    吞吐量是指“单位时间内系统处理的客户请求的数据,直接体现软件系统的性能承载能力。

    吞吐量指标可在两方面发挥作用:

    (1)用于协助设计性能测试场景,以及衡量性能测试场景是否达到了预期的设计目标。根据估算的吞吐量数据,可以对应到测试场景的事务发生频率、事务发生次数等;另外,在测试完成后,根据实际的吞吐量可以衡量测试是否达到了预期的目标。

    (2)用于协助分析性能瓶颈。

    在没有遇到性能瓶颈的时候,F=(Nv*R)/T

    其中,F表示吞吐量,Nv表示虚拟用户的个数,R表示每个VU发出的请求(单击)数量,T表示性能测试所用的时间。但如果遇到了性能瓶颈,此时吞吐量和VU数量之间就不再符合公式的关系。

    常用于分析吞吐量的图形是“吞吐量——VU数量”的关联图。

    4.性能计数器

    性能计数器是描述服务器或操作系统性能的一些数据指标。如,内存数(Memory In Usage),进程时间(Total Process Time)等都是常见的计数器。

    单一的性能计数器只能体现系统性能的某一个方面,对性能测试结果的分析必须基于多个不同的计数器。

    与性能计数器相关的另一术语是“资源利用率”。资源利用率在通常的情况下需要结合响应时间变化曲线、系统负载曲线等各种指标进行分析。

    5.思考时间(休眠时间)

    在具体的测试实践中,该如何选择合适的思考时间呢? 下面给出一个计算思考时间的一般步骤:

    (1)首先计算出系统的并发用户数;

    (2)统计出系统平均的吞吐量;

    (3)统计出平均每个用户发出的请求数量;

    (4)根据公式R=T/Ts计算出思考时间。其中,T=(Nv*R)/F

    为了让性能测试场景更加符合实际情况,可以考虑以计算出的思考时间为基准,让实际思考时间在一定幅度内随机变动。

    如果测试的目的是为了“验证应用系统具有预期的能力”,就应该尽量模拟用户在使用业务时的真实思考时间;如果是为了“了解系统在压力下的性能水平”或是“了解系统承受压力的能力”,则可以采用0思考时间。

     

     

  • 《软件性能测试过程详解与案例剖析》——什么是软件性能

    2009-02-04 18:07:37

    用户视角的软件性能

    从用户的角度来说,软件性能就是软件对用户操作的响应时间。从客观的角度来说,事务的结束应该是系统返回所有的数据,响应时间应该是从用户操作开始到所有数据返回完成的整个耗时;但从用户的主观感知来说,如果采用一种优化的数据呈现策略,当少部分数据返回之后就立刻将数据呈现在用户面前,则用户感受到的响应时间就会远远小于实际的事务响应时间(这也是C/S结构的管理系统中开发人员常用的一种技巧)。


    管理员视角的软件性能

     管理员关心的问题  软件性能描述
     服务器的资源使用状况合理吗  资源利用率
     应用服务器和数据库的资源使用善合理吗  资源利用率 
     系统是否能够实现扩展  系统可扩展性
     系统最多能支持多少用户的访问?系统最大的业务处理量是多少  系统容量
     系统性能可能的瓶颈在哪里   系统可扩展性
     更换哪些设备能够提高系统性能   系统可扩展性
     系统能否支持7*24小时的业务访问  系统稳定性


    开发人员视角的软件性能

     开发员关心的问题  软件性能描述
     架构设计是否合理  系统架构
     数据库设计是否存在问题  数据库设计 
     代码是否存在性能方面的问题  代码
     系统中是否有不合理的内在使用方式  代码
     系统中是否存在不合理的线程同步方式  设计与代码
     系统中是否存在不合理的资源竞争  设计与代码
     

     

  • 开始看书学习了

    2009-02-04 16:47:22

    由于金融危机的影响,公司好久没有接到需要测试人员的新项目了,我也就一直在做着公司的一些边缘事情,公司网站关键字推广、项目检查小组的文档规范及整理,以及最近的为公司网站写文章,一个头两个大了。最主要的是测试人员总做着与测试不沾边的事,心里很是着急,可这种情绪又影响了自己的学习激情。现在想来,白天在公司为文章焦头烂额,晚上周末又看小说,不沾测试知识已是好多日了。不能让仅有的一点测试灵感都消失啊。今天下午终于成功地将最后一篇文章也是对于我来说最难的文章(会涉及到许多开发技术)甩给了老大,心里真个畅快啊。也终于下决心得开始捡起学习的劲头了,不进则退,踏入这个行业,不学习,不就等于自甘堕落么?碰巧前两天无意中下载了一本性能测试方面的电子书,书评好如潮,很有学习的激情。更好的是,刚刚又找一个同事把这本书给借来了,幸得他糊涂,下了两次单买了两本,这样一来,我就可以白天在电脑上看电子版,晚上在家也可以接着翻翻。毕竟好久没有专注学习了,并且没有实际的项目供我实践,因此以防“白看”,我得捡重点做做读书笔记。在51testing上开通blog已有些时日了,真正自己留在上面的东西并不多。以后就将摘要记在上面吧,点点滴滴,或许会对自己有作用。

  • 电脑一族保护眼睛

    2009-01-22 15:54:13

    告诉大家一种保护眼睛的好方法,也许许多人都看过,不过还是想贴出来给大家看看,希望能有所帮助。   
    桌面->右键->属性->外观->高级->项目选择(窗口)、颜色1(L)选择(其它)
    将色调改为:85。饱和度:123。亮度:205->添加到自定义颜色->在自定义颜色选定点确定->确定这样所有的文档都不再是刺眼的白底黑字,
    而是非常柔和的豆沙绿色,这个色调是眼科专家配置的,长时间使用会很有效的缓解眼睛疲劳保护眼睛。
  • SQL Server数据库优化方案

    2009-01-22 15:12:36

    查询速度慢的原因很多,常见如下几种:
      1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
      2、I/O吞吐量小,形成了瓶颈效应。 3、没有创建计算列导致查询不优化。
      4、内存不足  5、网络速度慢
      6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
      7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
      8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
      9、返回了不必要的行和列
      10、查询语句不好,没有优化
      可以通过如下方法来优化查询 :
      1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.
      2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)
      3、升级硬件
      4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段
      5、提高网速;
      6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。
      7、增加服务器 CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作Update,Insert, Delete还不能并行处理。
      8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like 'a%' 使用索引 like '%a' 不使用索引用 like '%a%' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。
      9、DB Server 和APPLication Server 分离;OLTP和OLAP分离
      10、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件'分区视图')
      a、在实现分区视图之前,必须先水平分区表
      b、在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。
      11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:
      1、 查询语句的词法、语法检查
      2、 将语句提交给DBMS的查询优化器
      3、 优化器做代数优化和存取路径的优化
      4、 由预编译模块生成查询规划
      5、 然后在合适的时间提交给系统处理执行
      6、 最后将执行结果返回给用户其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。
    12、Commit和rollback的区别 Rollback:回滚所有的事物。 Commit:提交当前的事物. 没有必要在动态SQL里写事物,如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过程
      13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。
      14、SQL的注释申明对执行没有任何影响

     15、尽可能不使用光标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。游标可以按照它所支持的提取选项进行分类: 只进 必须按照从第一行到最后一行的顺序提取行。FETCH NEXT 是唯一允许的提取操作,也是默认方式。可滚动性可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项 READ_ONLY:不允许通过游标定位更新(Update),且在组成结果集的行中没有锁。 OPTIMISTIC WITH valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。选择这个并发选项OPTIMISTIC WITH ROW VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有所更改。在 SQL Server 中,这个性能由 timestamp 数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有 timestamp 列的行时,SQL Server 先在时间戳列中存储当前的 @@DBTS 值,然后增加 @@DBTS 的值。如果某 个表具有 timestamp 列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。 SCROLL LOCKS 这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更新该行。然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的 Select 语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则 COMMIT 语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标 Select 语句中的锁提示。锁提示 只读 乐观数值 乐观行版本控制 锁定无提示 未锁定 未锁定 未锁定 更新 NOLOCK 未锁定 未锁定未锁定 未锁定 HOLDLOCK 共享 共享 共享 更新 UPDLOCK 错误 更新 更新 更新 TABLOCKX 错误 未锁定 未锁定更新其它 未锁定 未锁定 未锁定 更新 *指定 NOLOCK 提示将使指定了该提示的表在游标内是只读的。

      16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引

      17、注意UNion和UNion all 的区别。UNION all好

      18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的

      19、查询时不要返回不需要的行、列

      20、用sp_configure 'query governor cost limit'或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。 SET LOCKTIME设置锁的时间

      21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行

      22、在SQL2000以前,一般不要用如下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'",因为他们不走索引全是表扫描。也不要在Where字句中的列名加函数,如Convert,substring等,如果必须用函数的时候,创建计算列再创建索引来替代.还可以变通写法:Where SUBSTRING(firstname,1,1) = 'm'改为Where firstname like 'm%'(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS NULL,"NOT", "NOT EXISTS", "NOT IN"能优化她,而"<>"等还是不能优化,用不到索引。

      23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。

      24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引: Select * FROM PersonMember (INDEX = IX_Title) Where processid IN ('男','女')

      25、将需要查询的结果预先计算好放在表中,查询的时候再Select。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。

      26、MIN() 和 MAX()能使用到合适的索引。

      27、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程序质量高,并且执行的速度快。

      28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌Insert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。

      29、Between在某些时候比IN 速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。 select * from chineseresume where title in ('男','女') Select * from chineseresume where between '男' and '女' 是一样的。由于in会在比较多次,所以有时会慢些。

      30、在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。
     31、不要建没有作用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。

      32、用OR的字句可以分解成多个查询,并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。

      33、尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰.为了加快视图的查询,MsSQL增加了视图索引的功能。

      34、没有必要时不要用DISTINCT和ORDER BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION 和UNION ALL一样的道理。

         select top 20 ad.companyname,comid,position,ad.referenceid,worklocation, convert(varchar(10),ad.postDate,120) as postDate1,workyear,degreedescrīption FROM jobcn_query.dbo.COMPANYAD_query ad where referenceID in('JCNAD00329667','JCNAD132168','JCNAD00337748','JCNAD00338345',
      'JCNAD00333138','JCNAD00303570','JCNAD00303569',
      'JCNAD00303568','JCNAD00306698','JCNAD00231935','JCNAD00231933',
      'JCNAD00254567','JCNAD00254585','JCNAD00254608',
      'JCNAD00254607','JCNAD00258524','JCNAD00332133','JCNAD00268618',
      'JCNAD00279196','JCNAD00268613') order by postdate desc 

      35、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数。

      36、当用Select INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是 select INTO. drop table t_lxh begin tran select * into t_lxh from chineseresume where name = 'XYZ' --commit 在另一个连接中Select * from sysobjects可以看到 Select INTO 会锁住系统表,Create table 也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。

      37、一般在GROUP BY 个HAVING字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:select 的Where字句选择所有合适的行,Group By用来分组个统计行,Having字句用来剔除多余的分组。这样Group By 个Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group BY的目的不包括计算,只是分组,那么用Distinct更快

      38、一次更新多条记录比分多次更新每次一条快,就是说批处理好

      39、少用临时表,尽量用结果集和Table类性的变量来代替它,Table 类型的变量比临时表好

      40、在SQL2000下,计算字段是可以索引的,需要满足的条件如下:

      a、计算字段的表达是确定的

      b、不能用在TEXT,Ntext,Image数据类型

      c、必须配制如下选项 ANSI_NULLS = ON, ANSI_PADDINGS = ON, …….

      41、尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句,是控制流语言的集合,速度当然快。反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在Tempdb中。以前由于SQL SERVER对复杂的数学计算不支持,所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs,现在支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源,如果返回大的结果采用存储过程

      42、不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快

      43、Select COUNT(*)的效率教低,尽量变通他的写法,而EXISTS快.同时请注意区别: select count(Field of null) from Table 和 select count(Field of NOT null) from Table 的返回值是不同的!!!

      44、当服务器的内存够多时,配制线程数量 = 最大连接数+5,这样能发挥最大的效率;否则使用 配制线程数量<最大连接数启用SQL SERVER的线程池来解决,如果还是数量 = 最大连接数+5,严重的损害服务器的性能。

      45、按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你(不经意的)某个存储过程中先锁定表B,再锁定表A,这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好,死锁很难被发现

      46、通过SQL Server Performance Monitor监视相应硬件的负载 Memory: Page Faults / sec计数器如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。

      Process:

      1、% DPC Time 指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC 正在运行的为比标准间隔优先权低的间隔)。 由于 DPC 是以特权模式执行的,DPC 时间的百分比为特权时间百分比的一部分。这些时间单独计算并且不属于间隔计算总数的一部 分。这个总数显示了作为实例时间百分比的平均忙时。

      2、%Processor Time计数器 如果该参数值持续超过95%,表明瓶颈是CPU。可以考虑增加一个处理器或换一个更快的处理器。

      3、% Privileged Time 指非闲置处理器时间用于特权模式的百分比。(特权模式是为操作系统组件和操纵硬件驱动程序而设计的一种处理模式。它允许直接访问硬件和所有内存。另一种模式为用户模式,它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操作系统将应用程序线程转换成特权模式以访问操作系统服务)。特权时间的 % 包括为间断和 DPC 提供服务的时间。特权时间比率高可能是由于失败设备产生的大数量的间隔而引起的。这个计数器将平均忙时作为样本时间的一部分显示。

      4、% User Time表示耗费CPU的数据库操作,如排序,执行aggregate functions等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。 Physical Disk: Curretn Disk Queue Length计数器该值应不超过磁盘数的1.5~2倍。要提高性能,可增加磁盘。 SQLServer:Cache Hit Ratio计数器该值越高越好。如果持续低于80%,应考虑增加内存。 注意该参数值是从SQL Server启动后,就一直累加记数,所以运行经过一段时间后,该值将不能反映系统当前值。

     47、分析select emp_name form employee where salary > 3000 在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。 48、查询的关联同写的顺序

         select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' (A = B ,B = '号码') 
      
      select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' and b.referenceid = 'JCNPRH39681' (A = B ,B = '号码', A = '号码') 
      
      select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681' (B = '号码', A = '号码') 

      49、

      (1)IF 没有输入负责人代码 THEN code1=0 code2=9999 ELSE code1=code2=负责人代码 END IF 执行SQL语句为: Select 负责人名 FROM P2000 Where 负责人代码>=:code1 AND负责人代码 <=:code2

      (2)IF 没有输入负责人代码 THEN  Select 负责人名 FROM P2000 ELSE code= 负责人代码 Select 负责人代码 FROM P2000 Where 负责人代码=:code END IF 第一种方法只用了一条SQL语句,第二种方法用了两条SQL语句。在没有输入负责人代码时,第二种方法显然比第一种方法执行效率高,因为它没有限制条件; 在输入了负责人代码时,第二种方法仍然比第一种方法效率高,不仅是少了一个限制条件,还因相等运算是最快的查询运算。我们写程序不要怕麻烦

      50、关于JOBCN现在查询分页的新方法(如下),用性能优化器分析性能的瓶颈,如果在I/O或者网络的速度上,如下的方法优化切实有效,如果在CPU或者内存上,用现在的方法更好。请区分如下的方法,说明索引越小越好。

         begin 
      
      DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20)) 
      
      insert into @local_variable (ReferenceID) 
      
      select top 100000 ReferenceID from chineseresume order by ReferenceID 
      
      select * from @local_variable where Fid > 40 and fid <= 60 
      
      end 和 
      
      begin 
      
      DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20)) 
      
      insert into @local_variable (ReferenceID) 
      
      select top 100000 ReferenceID from chineseresume order by updatedate 
      
      select * from @local_variable where Fid > 40 and fid <= 60 
      
      end 的不同 
      
      begin 
      
      create table #temp (FID int identity(1,1),ReferenceID varchar(20)) 
      
      insert into #temp (ReferenceID) 
      
      select top 100000 ReferenceID from chineseresume order by updatedate 
      
      select * from #temp where Fid > 40 and fid <= 60 drop table #temp 
      
      end 

     另附:存储过程编写经验和优化措施 From:网页教学网
      一、适合读者对象:数据库开发程序员,数据库的数据量很多,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。
      二、介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。
      三、内容:
      1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。
      2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。  3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:
      a)SQL的使用规范:
      i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。
      ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
      iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
      iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
      v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
      vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
      vii. 尽量使用“>=”,不要使用“>”。
      viii. 注意一些or子句和union子句之间的替换
      ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。
      x. 注意存储过程中参数和数据类型的关系。
      xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。

      b)索引的使用规范:
      i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。
      ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引
      iii. 避免对大表查询时进行table scan,必要时考虑新建索引。
      iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
      v. 要注意索引的维护,周期性重建索引,重新编译存储过程。
      c)tempdb的使用规范:

      i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。
      ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。
      iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
      iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。

      v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
      vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。
      d)合理的算法使用:
      根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。

  • 应用系统性能测试六大步(转自51)

    2008-12-31 11:35:12

     性能测试是为了保证产品发布后其性能能够满足用户的需求,本文结合具体案例介绍了应用系统性能测试的六大步骤。

      在本文介绍的这个案例中,被测应用系统是一家公司的客户信息系统,它主要用来录入、修改以及查询全球客户的信息,并将客户信息转入到业务系统。但是,在应用过程中,客户经常投诉在某个时刻新建或者修改一个客户信息非常慢,正常情况下完成该操作只需要1~5秒,而异常时却需要10分钟左右,而且系统管理员发现系统资源使用率都比较低,这种情况的出现并没有规律性。

      在这个案例中我们发现了系统存在性能问题,下一步工作就是要在性能测试过程中查找形成系统瓶颈和故障的根本原因,在此项工作中我们应该按照如下几个步骤进行。

      1.确定明确的测试目标

      性能调优是是无止境的,所以在测试之前应确定一个明确性能调优目标,这也是后面“评估性能验证”的一个基准,也是测试终止的一个基准。在本案例中目标设定为:在相同系统环境配置下30个并发用户在1~5秒钟内完成各类在线操作。

      2.测试需求分析

      性能调优的测试分析主要目的是要挖掘出可能造成系统瓶颈的因素,并为后面的测试用例设计提供保证。影响系统性能有很多种原因,在此应关注如下几个关键点:

      ●  应用配置需求: 例如应用整体框架、涉及到哪些第三方的组件、应用层与数据库层的接口、使用了什么数据库等。

      ●  系统配置需求: 例如用户客户端配置、客户端与服务器端的网络配置、应用服务器或数据库服务器操作系统等。

      ●  用户使用情况需求: 例如用户分布情况; 哪些模块用户使用比较频繁; 在用户操作的数据有哪些特点等。

      这方面工作是非常繁杂的,而且要求测试人员具有挖掘可能造成系统瓶颈因素的洞察力和敏锐感,但是很多测试人员在接手测试之后,很快进入到测试用例设计阶段。实践证明,这样做往往都适得其反,要么工期延期,要么项目开发失败。这个过程在测试整体过程中是非常关键的一环。性能测试分析有个特点: 它关注的是应用的整体,或者会仔细分析围绕着应用的各种外部因素,比如说它所涉及到的硬件、第三方软件,而不会深入到项目具体的内部。这是因为性能测试关注的是项目整体、是一种黑盒测试方法,我们关心一个项目的整体在运行时所暴露出来的问题。在此案例中我们获取到如表所示需求。

      3.测试用例设计

      此过程主要目的是设计出一些合理的场景去验证在需求分析阶段获得的可能影响性能的因素是否是造成系统瓶颈的因素。测试用例设计一般包括测试策略、测试案例、测试内容。

      测试策略一般包括对比测试环境与真实的业务操作环境,真实业务操作环境又可能涉及局域网测试环境与机房测试环境等

      测试案例主要是根据测试需求分析的结果制定出在测试执行时系统的执行方法,比如本案例中“5个人同时录入不同的新客户信息,以及具体的模拟步骤”。在测试案例设计时应注意如下几点:

      ●  虚拟用户的操作步骤要尽量类似于真实用户的操作。

      ●  操作的数据要类同于真实用户实际使用数据,例如在案例中用户录入客户信息时,根据需求得到的结果,我们可以设计有3~4个虚拟用户在录入一些小客户的信息,1~2个虚拟用户在录入大客户的信息等。

      ●  在案例设计时要充分考虑到需求中用户对模块的使用频率。使得在模拟时每个模块使用情况尽量地类似于真实环境。

      测试内容一般包括并发性能测试、疲劳强度测试、大数据量测试以及系统资源监控等,我们在做性能调优测试时主要是做并发性能测试以及系统资源监控这些方面的工作。从对系统产生并发性能测试过程中监控系统中各种资源的变化,来判断导致性能瓶颈的原因。

      4.脚本开发数据的准备以及测试执行与监控

      测试执行与监控的主要目的是根据设计方案去验证系统是否存在瓶颈,给测试分析提供各种分析数据。此过程会与下面的“测试分析”过程不断进行重复执行,直至真正确定出系统瓶颈所在。

      笔者认为,在此过程中如果有测试工具能够满足测试要求,那么应尽量使用测试工具,不要手工去开发测试程序,因为做企业项目往往时间紧张,而且测试工具毕竟是一个成熟的产品,在各方面都得到验证。使用工具将会缩短测试周期,而且现在市场上有很多成熟的测试软件。例如: MercuryLoadRunnerIBM的Robot、Compuware的QALoad等。在这个案例中笔者使用的是Mercury的 LoadRunner。关于一些技术细节笔者就不再赘述了,在这里主要提两点。

      一是数据的准备。数据准备一定要关注数据的质量和数量,不要出现一些不符合业务逻辑的废数据,并且数据量要满足测试运行的需要。例如测试需要100组数据,但是实际只准备了50组,从而导致测试执行结果出现大的偏差。

    二是测试执行。除了正确按照设计的要求去设置各种参数之外,还要关注系统是否存在功能问题,这往往成为性能测试的“盲点”。原则上性能测试之前必须确保功能测试已经完备,但是任何事情都不绝对,所以一般做性能测试之初,都会模拟一个用户去运行设计的场景,主要是确保“测试脚本正确性”、“在设计的场景中应用系统不存在功能上的问题”。很多性能测试过程往往因为功能问题导致性能测试失败,或者是测试延期的现象。

      5.测试分析

      测试分析的主要目的是要根据测试执行获取到的数据去判断造成系统出现瓶颈的位置,挖掘造成系统瓶颈的真正原因。这个过程是技术含量最高的一环,因为在测试执行过程获取到的数据会涉及到各个方面,在这个案例中就涵盖了网络方面的知识、系统方面的知识、应用方面的知识等,测试人员需要从这些繁杂的数据中挑出异常,系统越大越复杂在这个方面对测试人员要求会更高。但是这里面也有一些技巧:

      ●  在做测试分析时人员组成建议为: 开发人员、系统人员、测试人员共同组成。这样会在技术上填补个人技术上的不足。实际每个项目涉及到的技术都可能各有不同,对于个人来说不可能每样都精通。

      ●  反复比较一个类型的参数在不同时间的跳跃值,或者不同场景下同一个类型参数的变化。

      ●  在发现参数有异常变化时,不要轻易下结论,而是要尽量挖掘可能影响这个参数的其他参数值。在长期的测试过程中发现往往发现第一个所谓的瓶颈都是因为其他因素造成的。

      ●  在测试分析时使用较多的一种方式是排除法,根据开始获取到的信息大概能将问题定位在某一层面上。但具体在什么地方,就可以采取排除的方法去定位。

      ●  尽量使用一些比较成熟的工具协助分析工作,这样将大大减轻工作负担。

      ●  在确定出真正的性能瓶颈时,可以做一些小的测试模型去做验证,确定分析的正确性。

      在本案例中,在测试结果经过各种比对之后,最后确定是数据库层上出现问题。但是问题究竟出现什么地方呢?笔者在分析过程中采用了许多排除法,比如更新索引的统计值、将数据库中的表从页级锁改为行级锁等,但是都效果甚微。

      所以,我们回到上面看与数据库层相关的需求:

      (1) 因为业务需要,需要使用很多模糊查询。此类操作会进行表扫描。为了防止脏读,会向数据库申请表级意向锁。

      (2) 因为客户关系复杂,所以数据库设计的时候,存在多表关联。

      (3) 在应用开发时,我们使用了Hiberate这个组件,这些组件对于开发人员来说是一个黑盒,而且存在一些局限性: 在更新记录时会同步更新所有相关联的表,即使关联表不需要更新; 同步更新的记录操作会涵盖一个事物处理过程中,会产生大事务操作; 无法利用SQL优化技术去优化他所产生出来的SQL语句。

      研究之后发现: 在进行模糊查询与大客户信息录入与修改的操作时,由hiberate这个组件产生的大事务SQL导致了数据库的互锁,是系统瓶颈所在。为了验证这一判断的正确性,笔者做了一个小的模型去验证。

      假设库中有A、B、C三张表,现在有三个虚拟用户同时在上面进行操作: 用户Vuser1需要查询客户信息,他只知道客户的姓氏,所以他采取了模糊查询; 用户Vuser2正在修改一个客户信息,正准备保存; 用户Vuser3正在查询客户办公信息,也需要模糊查询。

      Vuser1操作先得到执行,在表扫描中出现表级意向锁; 此时Vuser2进来需要修改A、B、C三张表对应记录,并成功的锁定了B、C两张表对应的行(因为是行级锁),然后进行了修改,但是无法修改表A,所以 Vuser2此时等待Vuser1释放锁; 此时Vuser3进来了,需要查询C表,因为Vuser2并没有释放锁,此时Vuser3也处在等待状态。验证显示,在出现大数量的操作并且在多用户的操作下,此瓶颈将不断地暴露出来。

      6.系统调优与验证

      将获取的分析数据交付到开发组进行调优,经过调优后一般都需要再次进行验证,验证主要关注调优后的结果是否解决了所发现的系统性能瓶颈和是否产生了新的性能瓶颈。这方面的工作主要由开发人员来完成。在本案例中,去掉了Hiberate组件,改为由应用自身控制,尽量减少了大事物的出现概率,并同业务部门商议,降低了模糊查询操作的次数。在后来再做“性能评测”时确认系统达到了预期目标。


  • 如何在 LoadRunner 脚本中做关联 (Correlation)(转)

    2008-12-22 16:02:34

     当录制脚本时,VuGen会拦截client端(浏览器)与server端(网站服务器)之间的对话,并且通通记录下来,产生脚本。在VuGen的Recording Log中,您可以找到浏览器与服务器之间所有的对话,包含通讯内容、日期、时间、浏览器的请求、服务器的响应内容等等。脚本和Recording Log最大的差别在于,脚本只记录了client端要对server端所说的话,而Recording Log则是完整纪录二者的对话。
      当执行脚本时,您可以把VuGen想象成是一个演员,它伪装成浏览器,然后根据脚本,把当初真的浏览器所说过的话,再对网站伺服器重新说一遍,VuGen企图骗过服务器,让服务器以为它就是当初的浏览器,然后把网站内容传送给VuGen。
      所以纪录在脚本中要跟服务器所说的话,完全与当初录制时所说的一样,是写死的(hard-coded)。这样的作法在遇到有些比较聪明的服务器时,还是会失效。这时就需要透过「关联(correlation)」的做法来让VuGen可以再次成功地骗过服务器。

    何谓关联(correlation)?
      所谓的关联(correlation)就是把脚本中某些写死的(hard-coded)数据,转变成是撷取自服务器所送的、动态的、每次都不一样的数据。
      举一个常见的例子,刚刚提到有些比较聪明的服务器,这些服务器在每个浏览器第一次跟它要数据时,都会在数据中夹带一个唯一的辨识码,接下来就会利用这个辨识码来辨识跟它要数据的是不是同一个浏览器。一般称这个辨识码为Session ID。对于每个新的交易,服务器都会产生新的Session ID给浏览器。这也就是为什么执行脚本会失败的原因,因为VuGen还是用旧的Session ID向服务器要数据,服务器会发现这个Session ID是失效的或是它根本不认识这个Session ID,当然就不会传送正确的网页数据给VuGen了。
      下面的图示说明了这样的情形:
      当录制脚本时,浏览器送出网页A的请求,服务器将网页A的内容传送给浏览器,并且夹带了一个ID=123的数据,当浏览器再送出网页B的情求时,这时就要用到ID=123的数据,服务器才会认为这是合法的请求,并且把网页B的内容送回给浏览器。
    在执行脚本时会发生什么状况?浏览器再送出网页B的请求时,用的还是当初录制的ID=123的数据,而不是用服务器新给的ID=456,整个脚本的执行就会失败。
      要对付这种服务器,我们必须想办法找出这个Session ID到底是什么、位于何处,然后把它撷取下来,放到某个参数中,并且取代掉脚本中有用到Session ID的部份,这样就可以成功骗过服务器,正确地完成整个交易了。
      哪些错误代表着我应该做关联(correlation)?
    假如脚本需要关联(correlation),在还没做之前是不会执行通过的,也就是说会有错误讯息发生。不过,很不幸地,并没有任何特定的错误讯息是和关联(correlation)有关系的。会出现什么错误讯息,与系统实做的错误处理机制有关。错误讯息有可能会提醒您要重新登入,但是也有可能直接就显示HTTP 404的错误讯息。

      要如何做关联(correlation)?
      关联(correlation)函数
      关联(correlation)会用到下列的函数:
    • web_reg_save_param:这是最新版,也是最常用来做关联(correlation)的函数。
    语法:
    web_reg_save_param ( “Parameter Name” , < list of Attributes >, LAST );
    • web_create_html_param、web_create_html_param_ex:这二个函数主要是保留作为向前兼容的目的的。建议使用 web_reg_save_param 函数。
    详细用法请参考使用手册。在VuGen中点选【Help】>【Function reference】>【Contexts】>【Web and Wireless Vuser Functions】>【Correlation Functions】。

    如何找出要关联(correlation)数据
    简单的说,每一次执行时都会变动的值,就有可能需要做关联(correlation)。
    VuGen提供二种方式帮助您找出需要做关联(correlation)的值:
    1. 自动关联
    2. 手动关联
    自动关联
    VuGen内建自动关联引擎(auto-correlation engine),可以自动找出需要关联的值,并且自动使用关联函数建立关联。
    自动关联提供下列二种机制:
    • Rules Correlation:在录制过程中VuGen会根据订定的规则,实时自动找出要关联的值。规则来源有两种:
    o 内建(Built-in Correlation):
    VuGen已经针对常用的一些应用系统,如AribaBuyer、BlueMartini、BroadVision、InterStage、mySAP、NetDynamics、Oracle、PeopleSoft、Siebel、SilverJRunner等,内建关联规则,这些应用系统可能会有一种以上的关联规则。您可以在【Recording Options】>【Internet Protocol】>【Correlation】中启用关联规则,则当录制这些应用系统的脚本时,VuGen会在脚本中自动建立关联。
    您也可以在【Recording Options】>【Internet Protocol】>【Correlation】检视每个关联规则的定义。
    o 使用者自订(User-defined Rules Correlation):
    除了内建的关联规则之外,使用者也可以自订关联规则。您可以在【Recording Options】>【Internet Protocol】>【Correlation】建立新的关联规则。
    • Correlation Studio:有别于Rules Correlation,Correlation Studio则是在执行脚本后才会建立关联,也就是说当录制完脚本后,脚本至少须被执行过一次,Correlation Studio才会作用。Correlation Studio会尝试找出录制时与执行时,服务器响应内容的差异部分,藉以找出需要关联的数据,并建立关联。
    Rule Correlation
    请依照以下步骤使用Rule Correlation:
    1. 启用auto-correlation
    1. 点选VuGen的【Tools】>【Recording Options】,开启【Recording Options】对话窗口,选取【Internet Protocol】>【Correlation】,勾选【Enable correlation during recording】,以启用自动关联。
    2. 假如录制的应用系统属于内建关联规则的系统,如AribaBuyer、BlueMartini、BroadVision、InterStage、mySAP、NetDynamics、Oracle、PeopleSoft、Siebel、SilverJRunner等,请勾选相对应的应用系统。
    3. 或者也可以针对录制的应用系统加入新的关联规则,此即为使用者自订的关联规则。
    4. 设定当VuGen侦测到符合关联规则的数据时,要如何处理:
     【Issue a pop-up message and let me decide online】:跳出一个讯息对话窗口,询问您是否要建立关联。
     【Perform. correlation in sceipt】:直接自动建立关联
    2. 录制脚本
    开始录制脚本,在录制过程中,当VuGen侦测到符合关联规则的数据时,会依照设定建立关联,您会在脚本中看到类似以下的脚本,此为BroadVision应用系统建立关联的例子,在脚本批注部分可以看到关联前的数据为何。
    3. 执行脚本验证关联是OK的。
    Correlation Studio
    当录制的应用系统不属于VuGen预设支持的应用系统时,Rule Correlation可能既无法发挥作用,这时可以利用Correlation Studio来做关联。
    Correlation Studio会尝试找出录制时与执行时,服务器响应内容的差异部分,藉以找出需要关联的数据,并建立关联。
    使用Correlation Studio的步骤如下:
    1. 录制脚本并执行
    2. 执行完毕后,VuGen会跳出下面的【Scan Action for Correlation】窗口,询问您是否要扫描脚本并建立关联,按下【Yes】按钮。
    3. 扫描完后,可以在脚本下方的【Correlation Results】中看到扫描的结果。
    4. 检查一下扫瞄的结果后,选择要做关联的数据,然后按下【Correlate】按钮,一笔一笔做,或是按下【Correlate All】让VuGen一次就对所有的数据建立关联。
    注意:由于Correlation Studio会找出所有有变动的数据,但是并不是所有的数据都需要做关联,所以不建议您直接用【Correlate All】。
    5. 一般来说,您必须一直重复步骤1~4直到所有需要做关联的数据都找出来为止。因为有时前面的关联还没做好之前,将无法执行到后面需要做关联的部份。
    有可能有些需要做关联的动态数据,连Correlation Studio都无法侦测出来,这时您就需要自行做手动关联了。

    手动关联
    手动关联的执行过程大致如下:
    1. 使用相同的业务流程与数据,录制二份脚本
    2. 使用WinDiff工具协助找出需要关联的数据
    3. 使用web_reg_save_param函数手动建立关联
    4. 将脚本中有用到关联的数据,以参数取代
    接下来将详细的说明如何执行每个步骤
    使用相同的业务流程与数据,录制二份脚本
    1. 先录制一份脚本并存档。
    2. 依照相同的操作步骤与数据录制第二份脚本并存盘。注意,所有的步骤和输入的数据一定都要一样,这样才能找出由服务器端产生的动态数据。
    有时候会遇到真的无法使用相同的输入数据,那您也要记住您使用的输入数据,到时才能判断是您输入的数据,还是变动的数据。
    使用WinDiff工具协助找出需要关联的数据
    1. 在第二份脚本中,点选VuGen的【Tools】>【Compare with Vuser…】,并选择第一份脚本。
    2. 接着WinDiff会开启,同时显示二份脚本,并显示有差异的地方。WinDiff会以一整行黄色标示有差异的脚本,并且以红色的字体显示真正差异的文字。(假如没看到红色字体,请点选【Options】>【View】>【Show Inline Differences】)。
    3. 逐一检视二份脚本中差异的部份,每一个差异都可能是需要做关联的地方。选取差异的脚本,然后复制。
    在复制时,有时并不需要取整行脚本,可能只会选取脚本中的一部分。
    注意:请忽略lr_thik_time的差异部份,因为lr_thik_time是用来模拟每个步骤之间使用者思考延迟的时间。
    4. 接着要在Recording Log(单一protocol)或是Generation Log(多重protocol)中找这个值。将鼠标光标点到Recording Log的第一行开头,按下Ctrl+F,开启【Find】窗口,贴上刚刚复制的脚本,找出在Recording Log第一次出现的位置。
    结果会有二种:
    o 在Recording Log中找不到要找的数据,这时请先确认您找对了脚本,毕竟现在开启了二个几乎一样的脚本,很容易弄错。
    o 在Recording Log中找到了要找的数据,这时要确认数据是从服务器端传送过来的。首先可以先检查数据的标头,从标头的Receiving response可以知道数据是从服务器端传送到client端的。假如此数据第一次出现是在Sending request中,则表示此数据是由client端产生,不需要做关联,但是有可能需要做参数化(parameterized)。
    您要找的标头格式如下:
    *** [tid=b9 Action1 2] Receiving response from host astra.merc-int.com:80 ( 25/11/2002 12:04:00 )
    5. 现在您已经找到录制二次都不一样,而且是由服务器所产生的动态数据了,而此数据极有可能需要做关联。
    使用web_reg_save_param函数手动建立关联
    在找到是由服务器所产生的动态数据之后,接下来要做的就是找出适当的位置,使用web_reg_save_param函数,将这个动态数据撷取到某个参数中。
    1. 要在哪里使用web_reg_save_param函数?
    在之前的步骤,我们已经在Execution Log找到可能需要关联的动态数据。在Execution Log中选取动态数据前的文字然后复制,我们将会利用这段文字,来帮助我们找出要关联的动态数据。
    不过在这之前我们要先找出使用web_reg_save_param函数的正确位置,所以我们要再重新执行一遍脚本,而且这次会开启所有的Log。
    1. 在VuGen中点选【Vuser】>【Run-Time Settings】。
    2. 点选【General】>【Log】。
    3. 勾选【Enable logging】、【Always sends messages】、【Extended log】,以及【Extended log】下的所有选项。
    4. 按下【OK】就可以执行脚本了。
    执行完脚本之后,在Execution Log中搜寻刚刚复制的字符串。找到字符串后,在字符串前面会有A.tion1.c(7),这个7就是到时候要插入web_reg_save_param函数的位置,也就是要插入到脚本的第7行。
    在脚本的第7行前插入一行空白行,然后输入
    web_reg_save_param(“UserSession”,
    “UserSession” 这个 “UserSession” 就是到时要使用的参数名称,建议给个有意义的名字。
    注意:到这里整个web_reg_save_param函数还没完成。
    2. 找出web_reg_save_param中要用到的边界
    web_reg_save_param函数主要是透过动态数据的前面和后面的固定字符串,来辨识要撷取的动态数据的,所以我们还需要找出动态数据的边界字符串。
    找出左边界字符串
    再回到Execution Log中,选取动态数据前的字符串并且复制它。
    这时会有个问题,到底要选取多少字符串才足以唯一识别要找的动态数据呢?建议是越多越好,但是尽量不要包含到特殊字符。
    在这边我们选取「input type=hidden name=userSession value=」字符串。选好之后,还要再确认一次这段字符串真的是可以唯一识别的,所以我们在Execution Log中透过Ctrl+F的搜寻,找找看这段字符串是否可以找到要找的动态数据。假如找不到,web_reg_save_param函数还有个ORD参数可以使用,ORD参数可以设定出现在第几次的字符串才是要找的字符串。
    将这个边界字符串加到未完成的web_reg_save_param函数中:
    web_reg_save_param(“UserSession”, “LB= input type=hidden name=userSession value=”,
    找出右边界字符串
    接下来要找出动态数据的右边界字符串,这个字符串就比较好找了,从动态数据的最后一个字符开始,通常就是我们要找的右边界字符串了。
    以这个例子来看,就是「>」,所以再把右边界字符串加入,web_reg_save_param函数中,这时web_reg_save_param函数已经快完成了。最后再加上「LAST);」就完成整个web_reg_save_param函数了。
    web_reg_save_param(“UserSession”, “LB= input type=hidden name=userSession value=”, “RB=>”, LAST);
    将脚本中有用到关联的数据,以参数取代
    当使用web_reg_save_param建立参数后,接下来就是用“UserSession”参数去取代脚本中写死的(hard-coded)资料。
    范例:

    “Name=userSession”, “Value=75893.0884568651DQADHfApHDHfcDtccpfAttcf”, ENDITEM,
    换成
    “Name=userSession”, “Value={UserSession}”, ENDITEM,
    到这里您已经完成了一个关联了,接下来就是执行脚本,是否能成功运行,假如还是有问题,就要检查看看是否还需要再做另一个关联。
    关于 web_reg_save_param 函数
    对于关联(correlation)来说,web_reg_save_param是最重要的一个函数,其功能是在下载的网页内容中,透过设定的边界字符串,找出特定的数据并将其储存在一个参数中,以供后续脚本使用。
    接下来将针对web_reg_save_param做比较详细的说明。
    Service and registration type function
    web_reg_save_param是一个Service function。service function主要是用来完成一些特殊的工作的,如关联、设定proxy、提供认证信息等,当其作用时,不会对网页的内容做任何的修改。
    web_reg_save_param同时也是一个registration type function (只要函数名称中包含_reg_的字眼,表示其为registration type function)。registration type function意味着其真正作用的时机是在下一个action function完成时执行的。举例来说,当某个web_url执行时所接收到的网页内容中包含了要做关联的动态数据,则必须将web_reg_save_param放在此web_url之前,则web_reg_save_param会在web_url执行完毕后,也就是网页内容都下载完后,再执行web_reg_save_param找寻要做关联的动态数据并建立参数。
    所以要记住一点,要使用registration type function时,要注意其放置的位置必须在要作用的action function之前。
    语法
    int web_reg_save_param(const char *ParamName, <list of Attributes>, LAST);
    参数说明
    ParamName:存放动态数据的参数名称
    list of Attributes:其它属性,包含 Notfound, LB, RB, RelFrameID, Search, ORD, SaveOffset, Convert, 以及 SaveLen。属性值不分大小写,例如 Search=all。以下将详细说明每个属性值的意义:
    • Notfound:指定当找不到要找的动态数据时该怎么处置。
    o Notfound=error:当找不到动态数据时,发出一个错误讯息。假如没设定此属性,此为LoadRunner的默认值。
    o Notfound=warning:当找不到动态数据时,不发出错误讯息,只发出警告,脚本也会继续执行下去不会中断。在对角本除错时,可以使用此属性值。
    • LB:动态数据的左边界字符串。此属性质是必须要有的,而且区分大小写。
    • RB:动态数据的右边界字符串。此属性质是必须要有的,而且区分大小写。
    • RelFrameID:相对于URL而言,欲搜寻的网页的Frame。此属性质可以是All或是数字,而且可有可无。
    • Search:搜寻的范围。可以是Headers(只搜寻headers)、Body(只搜寻body部分,不搜寻header)、Noresource(只搜寻body部分,不搜寻header与resource)或是All(搜寻全部范围,此为默认值)。此属性质可有可无。
    • ORD:指明从第几次出现的左边界开始才是要撷取的数据。此属性质可有可无,默认值是1。假如值为All,则所有找到符合的数据会储存在数组中。
    • SaveOffset:当找到符合的动态数据时,从第几个字符开始才开始储存到参数中。此属性质不可为负数,其默认值为0。
    • Convert:可能的值有二种:
    o HTML_TO_URL: 将HTML-encoded数据转成URL-encoded数据格式
    o HTML_TO_TEXT:将HTML-encoded数据转成纯文字数据格式
    • SaveLen:从offect开始算起,到指定的长度内的字符串,才储存到参数中。此参数可有可无,默认值是-1,表示储存到结尾整个字符串。
    范例
    web_reg_save_param("A", "LB/ic=<a href=", "RB='>", "Ord=All", LAST);nner会搜寻网页中所有以 「<a href=」 开头,且以 「’>」结束,当中包含的字符串,并且储存在「A」参数中。
    Tips and Tricks
    以下提供一些关联的常见问题:
    • 如何打印出参数值?
    lr_output_message这二个函数来做到。例如:
    lr_output_message(“Value Captured = %s”, lr_eval_string(“{ParameterName}”));
    lr_eval_string与lr_output_message函数的使用说明请参考LoadRunner Online Function Reference。
    • 在脚本的data目录下找不到路制时的快照(snapshot)
    造成在脚本的data目录下找不到路制时的快照(snapshot)的可能原因如下:
    o 脚本是由VuGen 6.02或更早的版本所录制的
    o 汇入的Action不会包含快照(snapshot)的档案
    o 脚本是储存在只读的目录下,早成VuGen无法储存执行时撷取的快照(snapshot)
    o 某些步骤并不会产生快照(snapshot),如浏览某个资源
    o 快照(snapshot)功能被取消
    【Tools】>【General options】>【Correlation】tab >【Save correlation information during replay】
    • 开启WinDiff时出现「File no longer available」的错误讯息
    WinDiff这个工具有些限制,无法开启包含空格符的目录或是脚本,所以建议命名时不要使用空格符,并且尽可能将名称取短一点。
    • 录制时突然跳出【Correlation warning】对话窗口
    当你有勾选自动关联的【Issue a popup message and let me decide online】选项,当VuGen发现有可能要做关联的数据时,就会跳出【Correlation warning】的窗口,询问你要做关联(Correlation in scrīpt)还是要忽略(Ignore)。
    另外你也可以勾选【Perform. correlation in scrīpt】,让VuGen自动作关联,不会再跳出询问窗口。
    或是勾选【Disable correlation engine】,关闭自动关联的功能。

    • 如何手动启动「Scan action for correlation」的功能
    要手动启动「Scan action for correlation」的功能,请先执行脚本一次后,点选【Vuser】>【Scan Action for Correlation】。

    • 执行完脚本后并未出现【Scan Action for Correlation】窗口
    要启用【Scan Action for Correlation】功能,请点选【Tools】>【General options】>【Correlation】tab,勾选【Show Scan for correlation popup after replay of Vuser】选项。
1002/5<12345>
Open Toolbar