qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Oracle数据库的DML命令的处理过程详解

  Oracle数据库的DML命令的处理过程是本文我们主要要介绍的内容,从Oracle 9i起,有两种undo的管理方式:自动Undo管理(Automatic Undo Management,简称AUM)和手工Undo管理(Manual Undo Management,简称MUM)。Oracle 9i之前只能使用MUM,而且在MUM中,undo segment又叫做rollback segment。从Oracle 9i起,Oracle就建议使用AUM,而不应再使用MUM了。

  DML语句与undo

  当我们发出一条DML(比如update tab set col1='A' where col1='B')语句时,其执行过程可大致概括为以下几步。

  1、在shared pool里进行解析,从而生成执行计划。

  2、假设根据执行计划,得出col1='B'的记录存放在10号数据文件的54号数据块里。

  3、服务器进程在buffer cache里找一个可用的undo数据块,如果没有发现,则到undo表空间里找一个可用的undo块,并调入buffer cache。假设获得的undo数据块号为24号,位于11号undo数据文件里。

  4、将改变前的值,也就是A放入11号undo数据块。

  5、由于undo数据块发生了变化,于是产生重做记录,假设重做记录号为120。

  行号 事务id file# block# row column value

  120 T1 24 11 10 col1 A

  6、在buffer cache里找到54号数据块。如果没有发现,则从10号数据文件里调入。

  7、将改变后的值,也就是B放入54号数据块。

  8、由于数据块发生了变化,于是产生重做记录,假设重做记录号为121。

  行号 事务id file# block# row column value

  121 T1 10 54 10 col1 B

  9、控制权返回给用户,如果在SQL*Plus里执行DML,则表现为光标返回。

  10、当用户发出commit命令时,触发LGWR进程,将120与121这两个重做记录写入联机日志文件,并将54号数据块和11号undo数据块头部所记录的事务状态标记设置为已提交。然后控制权返回给用户,如果在SQL*Plus里执行DML操作,则表现为光标返回。

  11、这个时候,54号数据块以及11号undo块并不一定被DBWn写入数据文件。只有在脏数据块的数量达到一定程度才会被写入。

  事务只要被提交或回滚,那么该事务所使用的undo块就可以被覆盖。对于上面的例子来说,当第10步,用户发出commit命令以后,11号undo块里的数据就可以被其他事务所覆盖。

  关于Oracle数据库的DML命令的处理过程的相关知识就介绍到这里了,希望本次的介绍能够对您有所收获!

posted @ 2012-05-14 09:57 顺其自然EVO 阅读(181) | 评论 (0)编辑 收藏

Java调用SQL Server的存储过程详解

 1、使用不带参数的存储过程

  使用 JDBC 驱动程序调用不带参数的存储过程时,必须使用 call SQL 转义序列。不带参数的 call 转义序列的语法如下所示:

{call procedure-name}

  作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:

  1. CREATE PROCEDURE GetContactFormalNames  
  2. AS 
  3. BEGIN 
  4.  SELECT TOP 10 Title + ' ' + FirstName + ' ' + LastName AS FormalName  
  5.  FROM Person.Contact  
  6. END

  此存储过程返回单个结果集,其中包含一列数据(由 Person.Contact 表中前十个联系人的称呼、名称和姓氏组成)。

  在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 executeQuery 方法调用 GetContactFormalNames 存储过程。

  1. public static void executeSprocNoParams(Connection con) ...{  
  2.  try ...{  
  3.  Statement stmt = con.createStatement();  
  4. ResultSet rs = stmt.executeQuery("{call dbo.GetContactFormalNames}");  
  5.  while (rs.next()) ...{  
  6.  System.out.println(rs.getString("FormalName"));  
  7. }  
  8. rs.close();  
  9. stmt.close();  
  10.   }  
  11. catch (Exception e) ...{  
  12. e.printStackTrace();  
  13. }  
  14. }

  2、使用带有输入参数的存储过程

  使用 JDBC 驱动程序调用带参数的存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 IN 参数的 call 转义序列的语法如下所示:

{call procedure-name[([parameter][,[parameter]]...)]}

  构造 call 转义序列时,请使用 ?(问号)字符来指定 IN 参数。此字符充当要传递给该存储过程的参数值的占位符。可以使用 SQLServerPreparedStatement 类的 setter 方法之一为参数指定值。可使用的 setter 方法由 IN 参数的数据类型决定。

  向 setter 方法传递值时,不仅需要指定要在参数中使用的实际值,还必须指定参数在存储过程中的序数位置。例如,如果存储过程包含单个 IN 参数,则其序数值为 1。如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2。

  作为如何调用包含 IN 参数的存储过程的实例,使用 SQL Server 2005 AdventureWorks 示例数据库中的 uspGetEmployeeManagers 存储过程。此存储过程接受名为 EmployeeID 的单个输入参数(它是一个整数值),然后基于指定的 EmployeeID 返回雇员及其经理的递归列表。下面是调用此存储过程的 Java 代码:

  1. public static void executeSprocInParams(Connection con) ...{  
  2.  try ...{  
  3.  PreparedStatement pstmt = con.prepareStatement("{call dbo.uspGetEmployeeManagers(?)}");  
  4.  pstmt.setInt(150);  
  5.  ResultSet rs = pstmt.executeQuery();  
  6.  while (rs.next()) ...{  
  7.  System.out.println("EMPLOYEE:");  
  8.  System.out.println(rs.getString("LastName") + ", " + rs.getString("FirstName"));  
  9.  System.out.println("MANAGER:");  
  10.  System.out.println(rs.getString("ManagerLastName") + ", " + rs.getString("ManagerFirstName"));  
  11.  System.out.println();  
  12.  }  
  13.  rs.close();  
  14.  pstmt.close();  
  15.  }  
  16.  catch (Exception e) ...{  
  17.  e.printStackTrace();  
  18.  }  
  19. }

  3、使用带有输出参数的存储过程

  使用 JDBC 驱动程序调用此类存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 OUT 参数的 call 转义序列的语法如下所示:

{call procedure-name[([parameter][,[parameter]]...)]}

  构造 call 转义序列时,请使用 ?(问号)字符来指定 OUT 参数。此字符充当要从该存储过程返回的参数值的占位符。要为 OUT 参数指定值,必须在运行存储过程前使用 SQLServerCallableStatement 类的 registerOutParameter 方法指定各参数的数据类型。

  使用 registerOutParameter 方法为 OUT 参数指定的值必须是 java.sql.Types 所包含的 JDBC 数据类型之一,而它又被映射成本地 SQL Server 数据类型之一。有关 JDBC 和 SQL Server 数据类型的详细信息,请参阅了解 JDBC 驱动程序数据类型。

  当您对于 OUT 参数向 registerOutParameter 方法传递一个值时,不仅必须指定要用于此参数的数据类型,而且必须在存储过程中指定此参数的序号位置或此参数的名称。例如,如果存储过程包含单个 OUT 参数,则其序数值为 1;如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2。

 作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程: 根据指定的整数 IN 参数 (employeeID),该存储过程也返回单个整数 OUT 参数 (managerID)。根据 HumanResources.Employee 表中包含的 EmployeeID,OUT 参数中返回的值为 ManagerID。

  在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 execute 方法调用 GetImmediateManager 存储过程:

  1. public static void executeStoredProcedure(Connection con) ...{  
  2.  try ...{  
  3.  CallableStatement cstmt = con.prepareCall("{call dbo.GetImmediateManager(?, ?)}");  
  4.  cstmt.setInt(15);  
  5.  cstmt.registerOutParameter(2, java.sql.Types.INTEGER);  
  6.  cstmt.execute();  
  7.  System.out.println("MANAGER ID: " + cstmt.getInt(2));  
  8.  }  
  9.  catch (Exception e) ...{  
  10.  e.printStackTrace();  
  11.  }  
  12. }

  本示例使用序号位置来标识参数。或者,也可以使用参数的名称(而非其序号位置)来标识此参数。下面的代码示例修改了上一个示例,以说明如何在 Java 应用程序中使用命名参数。请注意,这些参数名称对应于存储过程的定义中的参数名称: 11x16CREATE PROCEDURE GetImmediateManager

  1.  @employeeID INT,  
  2.  @managerID INT OUTPUT 
  3. AS 
  4. BEGIN 
  5.  SELECT @managerID = ManagerID  
  6.  FROM HumanResources.Employee  
  7.  WHERE EmployeeID = @employeeID  
  8. END

  存储过程可能返回更新计数和多个结果集。Microsoft SQL Server 2005 JDBC Driver 遵循 JDBC 3.0 规范,此规范规定在检索 OUT 参数之前应检索多个结果集和更新计数。也就是说,应用程序应先检索所有 ResultSet 对象和更新计数,然后使用 CallableStatement.getter 方法检索 OUT 参数。否则,当检索 OUT 参数时,尚未检索的 ResultSet 对象和更新计数将丢失。

  4、使用带有返回状态的存储过程

  使用 JDBC 驱动程序调用这种存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。返回状态参数的 call 转义序列的语法如下所示:

{[?=]call procedure-name[([parameter][,[parameter]]...)]}

  构造 call 转义序列时,请使用 ?(问号)字符来指定返回状态参数。此字符充当要从该存储过程返回的参数值的占位符。要为返回状态参数指定值,必须在执行存储过程前使用 SQLServerCallableStatement 类的 registerOutParameter 方法指定参数的数据类型。

  此外,向 registerOutParameter 方法传递返回状态参数值时,不仅需要指定要使用的参数的数据类型,还必须指定参数在存储过程中的序数位置。对于返回状态参数,其序数位置始终为 1,这是因为它始终是调用存储过程时的第一个参数。尽管 SQLServerCallableStatement 类支持使用参数的名称来指示特定参数,但您只能对返回状态参数使用参数的序号位置编号。

  作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:

  1. CREATE PROCEDURE CheckContactCity  
  2.  (@cityName CHAR(50))  
  3. AS 
  4. BEGIN 
  5.  IF ((SELECT COUNT(*)  
  6.  FROM Person.Address  
  7.  WHERE City = @cityName) > 1)  
  8.  RETURN 1  
  9. ELSE 
  10.  RETURN 0  
  11. END

  该存储过程返回状态值 1 或 0,这取决于是否能在表 Person.Address 中找到 cityName 参数指定的城市。

 在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 execute 方法调用 CheckContactCity 存储过程:

  1.  public static void executeStoredProcedure(Connection con) ...{  
  2.  try ...{  
  3.  CallableStatement cstmt = con.prepareCall("{? = call dbo.CheckContactCity(?)}");  
  4.  cstmt.registerOutParameter(1, java.sql.Types.INTEGER);  
  5.  cstmt.setString(2, "Atlanta");  
  6.  cstmt.execute();  
  7.  System.out.println("RETURN STATUS: " + cstmt.getInt(1));  
  8.  }  
  9.  cstmt.close();  
  10.  catch (Exception e) ...{  
  11.  e.printStackTrace();  
  12.  }  
  13. }

  5、使用带有更新计数的存储过程

  使用 SQLServerCallableStatement 类构建对存储过程的调用之后,可以使用 execute 或 executeUpdate 方法中的任意一个来调用此存储过程。executeUpdate 方法将返回一个 int 值,该值包含受此存储过程影响的行数,但 execute 方法不返回此值。如果使用 execute 方法,并且希望获得受影响的行数计数,则可以在运行存储过程后调用 getUpdateCount 方法。

  作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下表和存储过程:

  1. CREATE TABLE TestTable  
  2.  (Col1 int IDENTITY,  
  3.  Col2 varchar(50),  
  4.  Col3 int);  
  5.  
  6. CREATE PROCEDURE UpdateTestTable  
  7.  @Col2 varchar(50),  
  8.  @Col3 int 
  9. AS 
  10. BEGIN 
  11.  UPDATE TestTable  
  12.  SET Col2 = @Col2, Col3 = @Col3  
  13. END;

  在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,并使用 execute 方法调用 UpdateTestTable 存储过程,然后使用 getUpdateCount 方法返回受存储过程影响的行计数。

  1. public static void executeUpdateStoredProcedure(Connection con) ...{  
  2.  try ...{  
  3.  CallableStatement cstmt = con.prepareCall("{call dbo.UpdateTestTable(?, ?)}");  
  4.  cstmt.setString(1, "A");  
  5.  cstmt.setInt(2, 100);  
  6.  cstmt.execute();  
  7.  int count = cstmt.getUpdateCount();  
  8.  cstmt.close();  
  9.  
  10.  System.out.println("ROWS AFFECTED: " + count);  
  11.  }  
  12.  catch (Exception e) ...{  
  13.  e.printStackTrace();

posted @ 2012-05-14 09:51 顺其自然EVO 阅读(147) | 评论 (0)编辑 收藏

watir+ruby(第一讲)安装文件下载 (windows下 linux下请用watir——webdriver)

第一步:JDK下载

官方地址:http://www.java.net/download/jdk6/6u10/promoted/b32/binaries/jdk-6u10-rc2-bin-b32-windows-i586-p-12_sep_2008.exe

其他地址;http://www.xin126.cn/soft_show.asp?id=17


第二步:eclipse下载地址(图1):

http://www.eclipse.org/downloads/


第三步:ruby插件下载地址(图2):

http://download.eclipse.org/technology/dltk/downloads/drops/R3.0/S-3.0.1-201108261011/

图中两个都需要下载(下载SDK)


第四步:ruby1.8.7下载.exe (图3)

http://rubyforge.org/frs/?group_id=167


第五步:rubygems1.8.7下载.zip(图4)

http://rubyforge.org/frs/?group_id=126


第六步: watir1.6.5 下载图中三个文件(图5)

http://rubyforge.org/frs/?group_id=104&release_id=28016


第七步:火狐浏览器下载

http://www.hacker.cn/Get/gjrj/06102608545293311.shtml



1.jpg (11.33 KB, 下载次数: 4)

1.jpg


2.jpg (13.05 KB, 下载次数: 7)

2.jpg


3.jpg (6.22 KB, 下载次数: 2)

3.jpg


4.jpg (6.08 KB, 下载次数: 7)

4.jpg


5.jpg (5.79 KB, 下载次数: 3)

5.jpg


框架运行效果展示图.jpg (35.99 KB, 下载次数: 3)

框架运行效果展示图.jpg

posted @ 2012-05-11 11:15 顺其自然EVO 阅读(1643) | 评论 (0)编辑 收藏

部署linux(ubuntu11.04) 中ruby&watir运行环境

 Ubuntu Linux 11.04ruby&watir运行环境:

Step 1.Ruby安装

$sudo apt-get install ruby

查看是否安装成功:

$ ruby -v

ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]



Step 2. RubyGems安装(管理ruby包)

$sudo apt-get install rubygems1.8

查看是否安装成功:

$ gem -v

1.3.7

Step 3.更新gem update

如果直接使用命令sudo gem update –system,则会报错如下:

$ gem update –system

ERROR: While executing gem ... (RuntimeError)

gem update --system is disabled on Debian, because it will

overwrite the content of the rubygems Debian package......

解决方法:
$sudo gem install rubygems-update
Successfully installed rubygems-update-1.3.7
再执行update_rubygems更新命令:
$sudo /var/lib/gems/1.8/bin/update_rubygems
RubyGems installed the following executables:/usr/bin/gem1.8

Step4.watir-webdriver安装(支持iefirefox,chromeand Opera)
$sudo gem install watir-webdriver --no-ri --no-rdoc
(...)
Successfully installed watir-webdriver-0.2.8
4 gems installed
Let's check if it can drive Firefox :

wdd@ubuntu:~$ irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'watir-webdriver'
=> true
irb(main):003:0>browser = Watir::Browser.new :ff
/usr/lib/ruby/gems/1.8/gems/selenium-webdriver- 2.13.0/lib/selenium/webdriver/common/platform.rb:129: warning: Insecure world writable dir /opt in PATH, mode 040777=> #<Watir::Browser:0x..fb6fb4f80 url="about:blank" title="">
irb(main):004:0> browser.goto('www.google.com')
=> "http://www.google.com.hk/"

Note:
If you want to use chrome,you should install chrome first.You do this:
browser = Watir::Browser.new :chrome
if you use ie or Opera, do this:
browser = Watir::Browser.new :ie
browser = Watir::Browser.new :opera
Start your script!

posted @ 2012-05-11 11:13 顺其自然EVO 阅读(843) | 评论 (1)编辑 收藏

软件评测师的目标和任务

  评测,顾名思义,是评价和测试。因此,软件评测师是通过编写测试方案并按照测试方案和流程对软件产品进行功能和性能测试,检查产品是否有缺陷,性能是否稳定,并给出相应的评价的那些专业技术人员。作为软件开发的重要环节,软件测试越来越受到人们的重视。随着软件开发规模的增大、复杂程度的增加,以寻找软件中的错误为目的测试工作就显得更加困难。为了尽可能多地找出程序中的错误,生产出高质量的软件产品,需要大量的软件评测人员,因此软件评测工程师就应运而生了。

  对软件进行评价的基础是测试,测试的过程就是找出与软件功能和性能不一致的地方,并进行分析。软件评测师的主要工作就是软件测试。

   软件危机曾经是软件界甚至整个计算机界最热门的话题。为了解决这场危机,软件从业人员、专家和学者做出了大量的努力。软件是由人来完成的,在目前的技术 上不能避免错误,有错是软件的属性,是很难改变的。现在人们已经逐步认识到所谓的软件危机实际上仅是一种状况,那就是软件中有错误,正是这些错误导致了软 件开发在成本、进度和质量上的失控。因此,必须面对现实,避免软件中错误的产生和消除已经产生的错误,使程序中的错误密度达到尽可能低的程度。

  实践经验证明,软件测试是软件开发过程中的一个重要步骤,或者说测试应该贯穿在软件开发过程的每一个阶段。软件测试所起到的作用就是:能够确保在软件开发的过程中,随时发现问题,方便开发人员及时修改。

  软件测试的最终目的是提交用户一个高可用性产品,为了尽可能多地找出错误,测试的重点应该是软件比较复杂的部分或是以前出错比较多的位置。为了给最终用户提供具有一定可信度的质量评价,测试的重点就应该直接针对在实际应用中会经常用到的业务规则。

  Grenford J. Myers在《The Art of Software Testing》一书对软件测试有如下的观点:

  ① 软件测试是为了发现错误而执行程序的过程;

  ② 测试是为了证明程序有错,而不是证明程序无错误;

  ③ 一个好的测试用例是在于它能发现至今未发现的错误;

  ④ 一个成功的测试是发现了至今未发现的错误的测试。

  因此,测试并不仅仅是为了要找出错误。通过分析错误产生的原因和错误的分布特征,可以帮助项目管理者发现当前所采用的软件过程的缺陷,以便改进。同时,这种分析也能帮助我们设计出有针对性的检测方法,改善测试的有效性。另外,没有发现错误的测试也是有价值的,完整的测试是评定测试质量的一种方法。

  对于软件测试人员来说,其目标和任务就是:

  1、软件测试员的基本目标是发现软件缺陷

  软件测试员的基本目标是发现软件缺陷,这是做好测试的首要条件。

  2、软件测试员追求的是尽可能早的找出软件缺陷

   因为软件的修复费用,随着软件生命周期的推移,将数十倍的增长,所以软件测试员应尽可能早的找出软件缺陷。对大型的软件,在软件开发的同时,就应该有紧 随其后的测试,如果等到产品已经开发完毕才开始测试,非常有可能引起大量耗时费力的返工。怎样才能有效的用这些方法尽早的发现软件缺陷,需要大家在工作实 践中不断的摸索、总结,进而不断的提高自己的测试能力。

  3、软件测试员必需确保找出的软件缺陷得以关闭

   软件测试人员必需确保找出的软件缺陷得以关闭。关闭的含义不是要软件缺陷在任何时候都必须得到修复。软件测试员需要对自己找出的软件缺陷保持一种平常 心,并不是辛苦找出的每个软件缺陷都是必要修复的。可能是由于没有足够的时间、不算真正的软件缺陷、修复的风险太大等原因,产品开发小组可以决定对一些软 件缺陷不作修复。

  虽然软件测试员需要对自己找出的软件缺陷保持一种平常心,但同时又必须坚持有始有终的原则,跟踪每一个软件缺陷的处理 结果,确保软件缺陷得以关闭。关闭软件缺陷的前提可以是缺陷得以修复或决定不作修复。而缺陷是否需要修复的最终决定权在软件的最终负责人,检查缺陷得以关 闭的责任在测试人员。但值得指出的是,虽然测试工程师找出了错误,但决定是否修改的权限并不是在测试工程师手上的,最终是由项目经理来决定的。

  4、软件测试员依据事实对软件做出评价

  软件测试人员对软件的测试结束后,通过对测试结果进行分析,然后实事求是地对软件产品的功能和性能做出恰当的评价。

posted @ 2012-05-11 09:58 顺其自然EVO 阅读(194) | 评论 (0)编辑 收藏

基于AMF协议的Flex应用程序的性能测试

 接触过Flex应用的,基本上对于其表现层的视觉效果都是非常赞赏的,也正是由于其华丽的外表掩盖了其诸多内在的缺陷,比如说响应速度、内存泄漏等等性能问题,对于大部分的Flex开发程序员或者是测试人员都是非常头痛的事情。本人最近就碰到基于Flex应用程序的性能测试, 由于它本身的特殊性,通信组建、协议基本上都是Adobe自己开发的,所以基本上通用的测试工具对他支持都是不太理想的。通过一系列的摸索,最终还是得把 注意力转移到Flex本身上来,全面去解析AMF协议,其实AMF协议还是走HTTP协议的,但从执行效率上来说,我们不能单纯通过HTTP协议来模拟其 执行过程,因为中间必须要走AMF协议,虽然LoadRunner也 有支持AMF协议,但AMF本身是需要Flash Player来支持,录制得到的脚本,不易于理解和分析,我们无法很好分析其执行过程,而且它封包和解包都是二进制格式,如果以这种方案去执行,我们就需 要解析它每一次封包和解包过程,相当于要去深入到AMF协议包解析过程,这样就把简单的问题复杂化。本身对于C/S应用程序的性能测试,最大的问题就是数 据包解析,通过LoadRunner去压测,就可能要面临丢包的情况。所以通常情况下,我们为了保证协议数据包传输的完整性,尽量去避开直接去与协议数据 包打交道,而且是通过上层的封装方式进行请求,而不去干预内在的复杂过程,这样就既能保证数据传输的完整性,同时也保证与业务逻辑实现方式的一致性,达到 真实环境的压测方案。

  下面我们将具体了解哈AMF协议,并通过模拟Client与Server实现AMF协议通信的连接方式来完成大并发的压力测试。

  AMF是Adobe独家开发出来的通信协议,它采用二进制压缩,序列化、反序列化、传输数据,从而为Flash 播放器与Flash Remoting网关通信提供了一种轻量级的、高效能的通信方式。

   模拟AMF请求与Server端建立通信,Adobe官方提供了一个Server端的通信服务就是BlazeDS,Flex将数据通过AMF协议转换成 二进制格式进行传输给Server端的BlazeDS服务,然后BlazeDS再将数据解析成Java需要的格式,完成Flex客户端与Server端的 通信过程。因此通过进一步分析发现,我们的目的就是要模拟AMF与BlazeDS建立连接,就解决了问题了。这里,就是利用Java来模拟AMF请求,通 过AMFConnection连接Blazeds接口,具体Demo代码如下:

package org.test.service.TestLogin;

import flex.messaging.io.amf.client.AMFConnection;
import flex.messaging.io.amf.client.exceptions.ClientStatusException;
import flex.messaging.io.amf.client.exceptions.ServerStatusException;

public class AMFDemo {

    public static void main(String[] args) {
        // 创建AMF连接
        AMFConnection amfCon = new AMFConnection();
       
        //连接 remote URL
        String url = http://localhost:8080/TestLogin/messagebroker/amf ;
         try{
            amfCon.connect(url);
        }catch(ClientStatusException cse){
            System.out.println(cse);
            return ;
        }
       
        TestLogin result ;   
        try{
            result = (TestLogin)amfCon.call("TestLogin.login","username","passwd");//传输对象的参数,登录用户、密码
        }catch(ClientStatusException ce){
            System.out.println(ce);
        }catch(ServerStatusException se){
            System.out.println(se);
        }
        amfCon.close();       
        System.out.println("Sucessfull!!!");
    }
}

 如果以上连接测试成功之后,那么接下来的问题就好解决了,那就是模拟并发,对于Java来说,那就是多线程的事儿了,通过多线程来控制实现并发用户量,完成BlaseDS接口的压力测试。多线程代码实现如下:

package org.test.service.TestLogin;

import flex.messaging.io.amf.client.AMFConnection;
import flex.messaging.io.amf.client.exceptions.ClientStatusException;
import flex.messaging.io.amf.client.exceptions.ServerStatusException;

public class AMFDemo implements Runnable{

        public void run(){
        // 创建AMF连接
        AMFConnection amfCon = new AMFConnection();
       
        //连接 remote URL
        String url = http://localhost:8080/TestLogin/messagebroker/amf ;
        try{
            amfCon.connect(url);
        }catch(ClientStatusException cse){
            System.out.println(cse);
            return ;
        }
        //循环100次
        for(int i=0; i<100; i++){
            TestLogin result ;   
            try{
                Thread.sleep(1000L);
                long TestStart = System.currentTimeMillis();
                result = (TestLogin)amfCon.call("TestLogin.login","username","passwd");//传输对象的参数,登录用户、密码
                System.out.println("login:" + (System.currentTimeMillis() - TestStart));//打印出登录的响应时间
            }catch(ClientStatusException ce){
                System.out.println(ce);
            }catch(ServerStatusException se){
                System.out.println(se);
            }catch (final InterruptedException e) {
                e.printStackTrace();
            }
        }
        amfCon.close();       
        System.out.println("Sucessfull!!!");
    }
}   
   
public class AMFDemoTest {
    //创建100个线程
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 100; i++) {
            Runnable runnable = new AMFDemo();
            new Thread(runnable).start();
        }
    }

}

  通过Java模拟AMF请求与BlazeDS建立通信连接,完成基于AMF协议的Flex应用程序的压力测试,抛弃传统的工具压测方式,从而寻 找最适合FLex本身的性能测试方案。当然,这个只是一种纯代码方式来实现,我们也可以借助通用工具来完成,至少在完成以上的代码实现过程,之后可以通过 LoadRunner或者是JMeter来调用Java代码完成,不过似乎JMeter对于Java支持更方便。使用工具的目的其实为了采样压力测试的数 据方便分析,而真正实现压力测试,其实不管是工具还是脚本,其实都是通过模拟底层的交互方式来达到同样的目的。

  作为一名测试人员,对于Java与Flex的通信方式的理解远不如开发人员,所以可能以上对于BlazeDS的通信机制描述难免有误。但这里并 不只是记录如何实现一种压力测试,而是表达一种个人对于性能测试思想的理解,很多时候不要一味地去追求自动化工具的强大,而忽略了其性能测试本身的思想。 希望能通过不断总结,来逐步提高自己!

posted @ 2012-05-11 09:57 顺其自然EVO 阅读(385) | 评论 (0)编辑 收藏

如何诊断Oracle Redo Log引发的性能问题

 一、Rodo Log性能调整目标:

  在能够影响Oracle性 能的诸多因素中,Redo Log相关的因素从某种程度上可以说是最为重要同时也是最值得关注的。因为在一个OLTP系统中Oracle通过各种技术以及优良的设计,尽量做到将大部 分操作在内存中完成,以便最大程度的提升性能。因此在Oracle的诸多后台进程以及用户进程的大部分操作都是内存操作,而且这些操作会通过延迟写入技术 尽可能的将磁盘I/O操作滞后。但是在这些操作中却有某些例外,其中最明显的就是针对Redo Log的操作。

  在Oracle中针对Redo Log的操作主要由LGWR进程完成,这个进程可以说是Oracle所有后台进程中最繁忙的进程,而且这个进程可能要频繁的进行I/O操作,这是因为Oracle出于数据安全的考虑必须保证联机在线重做日志可 靠的写入日志文件,以便在发生崩溃时能够有效恢复数据,而真正的数据可能会等一些时间延迟写入数据文件。这种特点在Oracle的各个后台进程中显得有些 独树一帜。另外LGWR全局唯一,即一个实例只能有一个活动的LGWR进程,由于要进行频繁的I/O操作可想而知是很容易造成LGWR进程竞争的。由于 LGWR在Oracle实例结构设计中的特殊地位,一旦出现LGWR性能瓶颈,那么对整个系统的性能影响将会是极为严重的,同时对数据安全也是一个潜在的 威胁。

  因此作为Oracle日常的数据库管 理,我们要给与这部分相当的关注,尽早发现问题,尽早作出调整。调整的目标就是要做到Log_Buffer大小适中(不要过大,也不能太小),要满足用户 进程的使用需要,每当系统负载有一个明显的增加时,就应该考虑调整它的大小。比如因为业务拓展当前系统固定用户数量从1万人猛增到3万人,那么就应该对 Log_Buffer大小给与关注。另外就是要做到日志文件的大小适中,日志组的日志文件数量合适,不能影响LGWR写日志文件的性能,不能造成日志文件 间的写入竞争,不能在日志切换归档发生时引发磁盘竞争等等。

  二、监控与问题排查:

  在进行Redo Log问题监控时,主要关注两个方面:日志缓冲区空间使用的等待情况和日志缓冲区数据槽的分配情况。通过这两方面的监控并配合一些问题排查手段,通常可以发现大量问题。

  (1)日志缓冲区空间使用的等待情况:

  可以通过查询v$session_wait来监控日志缓冲区空间使用的等待情况,通过如下SQL语句进行查询:

select sid,event,seconds_in_wait,state
from v$session_wait
where event='log buffer space%';

  以上的查询中可以通过观察seconds_in_wait的数值来分析问题,这个数值可以显示如下问题:日志切换缓慢引发的等待、LGWR写入缓慢引发的等待、日志文件写入引起的磁盘竞争引发的等待。

  这些等待的发生可能是由于如下问题引起的:

  1、日志文件写入时存在磁盘竞争:

  这种情况多见于日志切换发生时,由于日志文件组的规划不当,或者存放日志文件的磁盘写入速度缓慢,或者是因为磁盘RADI类型不当都会引发这个问题,如果怀疑村在这些情况,可以通过如下语句进行监控:

select event,total_waits,time_waited,average_wait
from v$system_event
where event like 'log file switch completion%';

  可以通过观察total_waits,time_waited,average_wait数值来分析问题,如果这些值过高(注意何谓“过高”,不同系统考量标准不一样,要具体分析),那么说明存在以上问题。此时可以通过如下措施解决:

  ● 将同一日志文件组的各个成员分配到不同的磁盘上,进而减少日志写入以及日志切换和日志归档时引发的竞争;

  ● 将日志文件尽可能存放在快速的磁盘上;

  ● 要合理选择RADI类型对磁盘进行条带化,通常不要选择RADI5来作为日志文件磁盘的RADI类型,通常推荐使用RADI10;

  ● 可以增加REDO LOG文件大小,来延缓日志切换,下面是一个增加日志文件大小的方法;

  假如原来有3个小的redo log file,下面是UNIX环境下的一个例子:

  第一步:往数据库添加三个大的redo logfile

SVRMGRL>ALTER DATABASE ADD LOGFILE GROUP 4
('/opt/oradata/app/redo04.log',
'/ora_bak/oradata2/redolog/redo04.log') size 16M reuse;

SVRMGRL>ALTER DATABASE ADD LOGFILE GROUP 5
('/opt/oradata/app/redo05.log',
'/ora_bak/oradata2/redolog/redo05.log') size 16M reuse;

SVRMGRL>ALTER DATABASE ADD LOGFILE GROUP 6
('/opt/oradata/app/redo06.log',
'/ora_bak/oradata2/redolog/redo06.log') size 16M reuse;

  第二步: 手工地做log switch,使新建的redo logfile起作用

SVRMGRL>alter system switch logfile;

  此操作可以执行一到几次,使旧的redo logfile成invalid状态。

  第三步:删除原来旧的redo logfile

SVRMGRL>alter database drop logfile group 1;
SVRMGRL>alter database drop logfile group 2;
SVRMGRL>alter database drop logfile group 3;

  2、检查点发生时DBWR进程没有完成数据写入引发等待:

  当日志文件完成一个循环周期后再一次来到原来某个日志文件准备进行重新使用时,发现该日文件对应的数据还没有写入相应的数据文件中,此时LGWR必须等待DBWR完成写入,从而引发等待。

  如果怀疑存在这个问题可以通过如下查询来进行监控:

select event,total_waits,time_waited,average_wait
from v$system_event
where event like 'log file switch (check%';

  通过total_waits,time_waited,average_wait这些数值的大小来判断分析问题,如果还不能确定,那么可以查看 一下Oracle的alert.log文件看一下相关时间内是否存在“checkpoint not complete”。如果存在那么证明日志文件的操作性能被DBWR进程所拖累。此时可以通过如下措施解决:

  ● 检查存放数据文件的磁盘是否存在I/O瓶颈(如:是否存在读写竞争、是否存在物理损坏、是否存在RADI类型不符等);

  ● 合理规划调整日志文件组日志文件的数量和大小;

  ● 合理设置FAST_START_MTTR_TARGET参数,以便设置一个合适的数值来控制检查点的发生;

  ● 可以考虑增加DBWR进程的数量,Oracle最多可以有10个DBWR进程;

  ● 如果条件允许,可以开启异步I/O;

  3、由于日志归档引发的等待:

  当归档发生时,归档日志进程不能快速的进行日志归档,从而导致了LGWR的等待。如果怀由此问题可以通过如下语句来监控:

select event,total_waits,time_waited,average_wait
from v$system_event
where event like 'log file switch (arch%';

  同样通过total_waits,time_waited,average_wait这些数值来进行问题分析,如果出现由于归档日志写入缓慢引发的性能问题,可以采用如下办法:

  ● 确定存放归档日志的磁盘空间没有被写满,如果出现这种情况,那么要对归档日志进行有限度的删除,或者将这些归档日志移走如存放到磁带库上,或者分配更大的存储空间;

  ● 增加日志文件组,从而为归档多留出一些时间;

  ● 增加多个归档进程,Oracle最多允许10个归档进程存在,在归档发生时如果LGWR进程发现归档进程ARCH出现不足时,会自动产生新的归档进程,因 此如果系统负载有明显增加预先分配足够的归档进程可以提高性能,可以使用alter system命令通过更改LOG_ARCHIVE_MAX_PROCESSES参数来改变归档进程数目;

  (2)日志缓冲区数据槽的分配情况引发的等待:

  可以通过如下的语句来监控日志缓冲区数据槽的分配情况的百分比:

select r.value "retries",e.value "entries",r.value/e.value*100 "percentage"
from v$sysstat t,v$sysstat e
where r.name='redo buffer allocation retries'
and e.name='redo entries';

  这个百分比值不能高于1%,如果这个数值频繁增长,那么一定出现了Log_Buffer内存空间不足,从而使得新产生的redo log entries不能写入Log_Buffer中从而造成等待,这个等待是由于LGWR性能不佳写日志文件过慢造成的,通常来说LGWR写入速度都是非常快 速的可以保证新产生的redo log entries内存空间使用的需要,即使在高负载情况下也不会出现太大问题,因而上面的问题通常发生机率较小,但是如果一旦发生,那么很有可能是由于日志 文件磁盘I/O规划出现问题,或者日志文件磁盘出现物理损坏,因此在出现这种情况引发的性能问题时,主要应该进行日志文件磁盘I/O规划以及日志文件磁盘 是否出现物理损伤方面的排查,同时也可能综合应用如Oracle的alert.log等相关文件进行综合分析。

posted @ 2012-05-11 09:49 顺其自然EVO 阅读(1293) | 评论 (0)编辑 收藏

浅析Java web程序之客户端和服务器端交互原理

1、协议

  a. TCP/IP整体构架概述

  TCP/IP协议并不完全符合 OSI的七层参考模型。传统的开放式系统互连参考模型,是一种通信协议的7层抽象的参考模型,其中每一层执行某一特定任务。该模型的目的是使各种硬件在相 同的层次上相互通信。这7层是:物理层、数据链路层、网路层、传输层、话路层、表示层和应用层。而TCP/IP通讯协议采用了4层的层级结构,每一层都呼 叫它的下一层所提供的网络来完成自己的需求。这4层分别为:

  i. 应用层:应用程序间沟通的层,如超文本传送协议(HTTP)、简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。

  ii. 传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。

  iii. 互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。

  iv. 网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。

  b. HTTP协议介绍:

  i. HTTP是一种超文本传送协议(HyperText Transfer Protocol),是一套计算机在网络中通信的一种规则。在TCP/IP体系结构中,HTTP属于应用层协议,位于TCP/IP协议的顶层

  ii. HTTP是一种无状态的的协议,意思是指 在Web 浏览器(客户端)和 Web 服务器之间不需要建立持久的连接。整个过程就是当一个客户端向服务器端发送一个请求(request),然后Web服务器返回一个响应 (response),之后连接就关闭了,在服务端此时是没有保留连接的信息。

  iii. HTTP 遵循 请求/响应(request/response) 模型的,所有的通信交互都被构造在一套请求和响应模型中。

  iv. 浏览WEB时,浏览器通过HTTP协议与WEB服务器交换信息,Web服务器向Web浏览器返回的文件都有与之相关的类型,这些信息类型的格式由MIME定义。

  c. 协议的java实现方式

  不论是TCP/IP协议也好,还是HTTP协议也好,java都是通过套接字(java.net.Socket)来实现的,可以参考我的另一篇技术博客:一个项目看java TCP/IP Socket编程(1.3版)

  2、HTTP报文接口及客户端和服务器端交互原理

  a. HTTP定义的事务处理由以下四步组成:

  i. 建立连接:

   例如我在浏览器里输入 http://cuishen.iteye.com,客户端请求这个地址时即打开了web服务器HTTP端口的一个套接字。因为在网络中间作为传递数据的 实体介质就是网线,数据实质上是通过IO流进行输出和输入,这就不难理解我们为什么在写一个Servlet的时候要引用 import java.io.*; 的原因 ,包括我们在向客户端回发结果的时候要用到PrintWriter对象的println()方法。其实请求的这个地址还要加上端口号80,80可以不写, 是因为浏览器默认的端口号是80。

  在Java底层代码中是这样实现的,只不过它们已经帮我们做了。

  1. Socket socket = new Socket("cuishen.iteye.com",80);    
  2. InputStream in = socket.getInputStream();    
  3. OutputStream out = socket.getOutputStream();

  ii. 客户端发送HTTP请求报文(request)

  一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令,是一个ASCII文本请求行,后跟0个或多个HTTP头标,一个空行和实现请求的任意数据。

即报文分四个部分:请求行,请求头标,空行和请求数据

  1)请求行

  请求行由三个标记组成:请求方法、请求URL和HTTP版本,中间用空格分开

  例如: GET cuishen.iteye.com/blog/242842 HTTP/1.1

  HTTP规范定义了8种可能的请求方法:(最常见的就是 GET 和 POST 两种方法)

  ● GET -- 检索URI中标识资源的一个简单请求
  ● HEAD -- 与GET方法相同,服务器只返回状态行和头标,并不返回请求文档
  ● POST -- 服务器接受被写入客户端输出流中的数据的请求
  ● PUT -- 服务器保存请求数据作为指定URI新内容的请求
  ● DELETE -- 服务器删除URI中命名的资源的请求
  ● OPTIONS -- 关于服务器支持的请求方法信息的请求
  ● TRACE -- Web服务器反馈Http请求和其头标的请求
  ● CONNECT -- 已文档化但当前未实现的一个方法,预留做隧道处理

  2)请求头标

  请求头标:由key :value 健值组成,每行一对。请求头标用来通知服务器有关客户端的功能和标识。

  HOST -- 请求的哪一个服务器端地址,主地址,比如:我的技术blog:cuishen.iteye.com

  User-Agent -- 用户即客户端可以使用的浏览器 ,如: Mozilla/4.0

  Accept -- 即客户端可以接受的MIME 类型列表,如image/gif、text/html、application/msword

  Content-Length -- 只适用于POST请求,以字节给出POST数据的尺寸

  3)空行

  发送回车符和退行,通知服务器以下不再有头标。

  4)请求数据

  使用POST传送数据,最常使用的是Content-Type和Content-Length头标。

  请求报文总结:

  我们可以这样写出一个标准的 HTTP请求:

POST /blog/242842 HTTP1.1
HOST: cuishen.iteye.com/
User-Agent: Mozilla/4.0
Accpt: image/gif,text/html,application/pdf,image/png...
key=value&key=value&key=value...... (POST()请求的数据)

  这上面的一个例子意思是:

我要去访问的服务器端的地址是cuishen.iteye.com/ 它下面的资源 /blog/242842
连起来就是: cuishen.iteye.com/blog/242842
这个页面用的是 HTTP1.1 规范,我的浏览器版本是Mozilla/4.0
可以支持的MIME格式为 image/gif,text/html,application/pdf,image/png...等等

这个MIME格式我们在servlet中写法是:response.setContentType("text/html;charset=gb2312");
或者在jsp中写法是:<%@ page contentType="text/html;charset=gb2312"%>
或者在html中写法是:<meta http-equiv="content-Type" content="text/html; charset=gb2312">

  (c).空行:最后一个响应头标之后是一个空行,发送回车符和退行,表明服务器以下不再有头标。

  (d).响应数据:HTML文档和图像等,也就是HTML本身。out.println("<html>......");写到客户端。

  1. <html>    
  2. <head>    
  3. <title>Welcome to cuishen's IT blog</title>    
  4. </head>    
  5. <body>    
  6. <!-- 这里是具体的内容,看到了这里    
  7. 相信大家对 HTTP 工作原理及客户端与服务器交互过程已经很清楚了吧    
  8. -->     
  9. </body>    
  10. </html>

  iv. 服务器端关闭连接,客户端解析回发响应报文,恢复页面

  1)浏览器先解析状态行,查看请求是否成功的状态代码--HTTP响应码:404 400 200 ....

  2)解析每一个响应头标,如:

ContentType: text/html;charset=gb2312
Content-Length: 122 --- 响应中的字节数,只在浏览器使用永久(Keep-alive)HTTP连接时需要。

  3)读取响应数据HTML,根据标签<html></html>中的内容恢复标准的HTML格式页面或者其它。

  4)一个HTML 文档可能包含其它的需要被载入的资源,浏览器会识别,并对这些资源再进行额外的请求,这个过程可以是循环的方式一直到所有的数据都按照响应头标中规定的格式恢复到页面中。

  5)数据传送完毕,服务器端关闭连接,即无状态协议。

  3、总结

  不要被高深的名词和理论吓到,其实HTTP客户端和服务器端的交互原理很简单:即先是浏览器和服务器端建立Socket无状态连接,也就是短连 接,然后通过IO流进行报文信息(这个报文是严格遵循HTTP报文接口的)的交互,最后会话结束后就关闭连接。对于这些底层的协议和报文的打包解包交互的 实现,其实java和浏览器早都已经封装好了,程序员只要专注于业务逻辑的实现就行啦,这些都不必关心!!


posted @ 2012-05-11 09:48 顺其自然EVO 阅读(428) | 评论 (0)编辑 收藏

快速找出数据库的性能问题之:缺失索引 &无用的索引


没有用的索引

  正如在上一小节所的讲的,创建一个索引是一个非常需要重视的问题,需要考虑很多的方面,因为,如果我们建立的索引没有发挥作用,甚至说,查询优化器不采用我们的索引,那么就会带来适得其反的效果。

  索引的维护是需要成本,甚至使得数据库的性能变得很低,特别实在数据更新的时候。当在数据表上面进行数据的更新,删除,和插入的时候,都会导致索引页发生重新的调整,导致索引页中的数据重新的排序,从而导致数据表被锁定。

  所以,我们很有必要找出没有发挥作用的索引,我们还是可以采用DMV来快速的查看:

  这里不否认,要完全明白上面的查询的意思却不是一件容易的事情,大家可以暂时不用懂,可以把这些脚本保存起来,作为一个小的工具使用。

  查询结果如下:

  因为我这里采用的是一个示例数据库,所以看到的结果不是很多,但是可以发现:这些索引一些在被不断的更新(user_updates),但是没有被用过(system usage)。

  对无用索引的解决很简单:删除索引就OK了。

  关于脚本,请大家在附件中下载,可以保留起来,并且大家还可以修改,查询指定的数据库的情况。

  附件:scripts.zip  我们通过减少查询中的不必要的读取操作从而使得查询的性能得到提升。一个查询在数据库中执行的读操作越多,那么就对磁盘,CPU,内存的压力越大。除非整个数据库的数据全在在内存中,否则每次的读操作都会把数据从磁盘读入到内存中,然后返回。

  一个查询在读取一个资源的时候,通过加锁会阻止其他的查询对这个资源进行修改,此时其他要操作这个资源的查询就需要等待,从而导致了延时。

  诚然,有些等待是必须的,读取操作也是必须的,但是一些因为我们代码或者设计导致的过度的读取操作和等待,那就会严重影响性能,尤其是当数据库的访问量开始变大的时候。

  可以说在SQL Server中,最高效的读取数据方式就是通过索引去获取数据。如果在数据表中存在缺失索引的问题,结果可想而知。

  在本篇中,我们将会讨论下面几个议题:

  ● 如何识别缺失索引性能问题

  ● 识别没有用的索引

  ● 如何解决上面的问题

  确实本篇讲述的内容涉及到了一些与数据库性能调优的话题,对于调优而言,难点很多时候在于如何正确的找出性能问题。

  下面,我们首先来看看缺失索引。

  缺失索引

  SQL Server可以在表字段上面建立索引,从而使得Where和Join这样的语句执行的更快。当查询优化器在优化一个查询的时候,它会保存一些来暗示哪些列上可能建立索引之后可能性能会更快的信息。我们可以通过动态管理视图sys.dm_db_missing_index_details来查看,运行如下查询

  查询的结果如下:

  下面,我们就来稍微的解释一下结果中主要字段的含义:

字段名字

说明

DatabaseName

告诉我们是哪一个数据库上面存在缺失索引的问题

equality_columns

如果在某个字段上面进行了相等的操作,例如Name=’Agilesharp’,在Name字段上面进行了判等的操作,如果查询优化器认为这个Name上面缺失索引,那么这个Name就会出现在上述查询的结果中。

多个字段,用逗号分割

inquality_columns

在某个字段上进行了不等的操作,例如ID>1等,如果ID上面存在缺失索引,那么ID就会出现在这里

Included_columns

告诉我们那些数据列可以作为索引包含列放在索引中,从而减少书签查找的开销

Statement

告诉哪一个表上面存在缺失索引的问题

  当然,上面的DMV查询所得到的结果只是推荐结果,至于是否要去在相应的列上面建立索引,还需要进行综合的分析,不能单靠一方面来判断,例如,我们可以在去制定一些计划去运行SQL Profiler去跟踪数据库,然后分析跟踪的数据,并且分析这个列的数据的分布情况,分析数据的密度和差异性,而且还可以进一步的分析列的统计信息,然后决定是否要加索引。

  注:我也正在写SQL Server Profiler的文章,还没有发布,请大家耐心等待。另外SQL Server的调优是个非常深的话题,大家可以通过我这里的一些问题在掌握一些所谓的小技巧,起到一个抛砖引玉的作用!

  说了这么多,可能大家感觉像是没有说,感觉有点虚。确实,我也感觉这样,因为就这分析缺失索引的问题要考虑的问题就N多。agilesharp的其他系列文章也在讨论SQL Server的性能问题,这里,我们就不多说,也不把问题搞复杂了。我再送朋友一段分析的代码,可以更好的帮助我们找到缺失索引的问题:

  上面的查询比较不错,按照成本进行了分析,成本越大,就说明加了索引之后,收益就越大,可以看到如下的结果:

  然后大家加了索引之后,可以多多的测试,可以查看执行计划,也可以查看查询的数据页的读取情况,I/O的情况:

posted @ 2012-05-10 09:46 顺其自然EVO 阅读(380) | 评论 (0)编辑 收藏

一次软件测试的电话面试分享

  以下是一次典型的软件测试电话面试分享,答案仅为个人看法,非标准答案。希望对正在找软件测试工作的同学有所帮帮助。

  1、自我介绍

  我叫XXX,毕业于XX大学计算机科学与技术专业。毕业后进入XX公司做了X年的软件测试工作,主要从事XXX项目功能及性能测试的工作,熟悉TD/QTP/LoadRunner/SqlSever等,注重团队合作,工作认真负责,有强烈的责任心,喜欢专研新技术;同时带领过XX项目的管理工作,熟悉项目管理流程...

  有些公司可能还需要准备一份英文的自我介绍。

  2、测试流程

  就说说最近的这次xxxx网站功能的测试流程。

  首先:得到相关文档(需求文档和设计文档),理解需求和设计思想后,制定测试计划(需评审),拟定测试策略(需评审),需要考虑到测试环境,测试时间,测试风险等

  第二步:设计测试用例,测试策略是:先完成网站部分的功能点测试,然后再进行系统测试(包括与其他模块的联调测试)。进行测试用例的设计时,需要覆盖到各种正常、异常处理情况。同时还包括界面测试、浏览器兼容性测试,易用测试及性能测试等。

  第三步:搭建测试环境,执行测试,记录测试缺陷

  第四步:进行测试缺陷分析,完成测试报告编写

  3、LoadRunner 如何优化脚本

  ● 参数化(模拟真实的用户选择)

  ● 手动关联(服务器返回的信息,例如sessionid,key的值等)

  ● 添加相应事务的集合点(主要是用于控制并发情况)与检查点(主要用于检查文字是否正确和图片是否正常显示)

  4、说一说工作中发现有价值的bug

  拿xxx来说发现登录模块中,出现登录延时现象,服务器响应很慢,通过多次测试在分析与确认以及和开发人员的沟通发现是login的**有问题。

  拿xxx来说,用于导航的树型菜单,加载数据延时,通过反复测试与确认,和开发人员沟通发现是算法和当初的设计加载数据导致的。

  5、Bug管理流程

  ● 发现Bug,使用缺陷管理工具提交到Bug管理库,此时状态时New

  ● 测试TM审核缺陷,如果确认是问题,再分配给对应的开发人员,设置状态是Open

  ● 如果不是错误,则拒绝,设置为Declined(拒绝)状态

  ● 开发人员查询状态为Open的Bug,如果不是错误,则置状态为Declined;如果是Bug则修复并置状态为Fixed;不能解决的Bug,要留下文字说明及保持Bug为Open状态;对于不能解决和延期解决的Bug,不能由开发人员自己决定,一般要通过某种会议(评审会)通过才能认可

  ● 测试人员查询状态为Fixed的Bug,然后验证Bug是否已解决,如解决置Bug的状态为Closed,如没有解决置状态为Reopen

  6、软件错误流程管理要点

  ● 为了保证错误的正确性,需要有丰富测试经验的测试人员验证发现的错误是否是真正的错误,书写的测试步骤是否准确,可以重复。

  ● 每次对错误的处理都要保留处理信息,包括处理姓名,时间,处理方法,处理意见,Bug状态。

  ● 拒绝或延期错误不能由程序员单方面决定,应该由项目经理,测试经理和设计经理共同决定。

  ● 错误修复后必须由报告错误的测试人员验证后,确认已经修复,才能关闭错误。

  ● 加强测试人员与程序员的交流,对于某些不能重复的错误,可以请测试人员补充详细的测试步骤和方法,以及必要的测试用例

  7、谈谈个人软件测试职业发展计划

  个人认为测试经验越多,测试能力越高。所以我的职业发展是需要时间累积的,一步一步向测试经理目标靠近。而且我也有初步的职业规划,前3年累积测试经验,不断的更新自己改正自己,做好测试任务,扩展更多的技术。做一个全面的测试人员,我希望能够在一个好的职位上待几年,而且最好有一次晋升,然后就期待着下一步。不管是向上提升,还是在企业内横向调动,对我个人来说,我希望找到一家企业,一家愿意做相互投入的企业。

  8、你有哪些优点?

  我的学习能力和适应环境的能力很强,和同事们的关系处理的非常融洽,工作中很细心,投入,一旦下定决心做某事,我就要把它做好。熟话说的好:细节决定成败,我觉得这句话在测试当中相当实用,往往是一个不起眼的小问题,引起了Bug.

  9、你有哪些缺点?

  我这人比较执着,认定的事情就不会放弃。在家的时候老妈也经常说我蛮固执的,在就是个性子有点急,分配给我的工作,我总想赶在第一时间尽快做好。

  10、有没有意向去外地工作?

  我一直以来都比较独立,不喜欢依赖于人,年轻人也需要多在外地多闯一闯。至于去哪里工作,我服从公司的调度和安排。

11、对于加班有什么看法?

  有句话说的好加班是合理的,不加班也是合理,除特殊情况外,我都服从工作上的安排。

  12、对自己的评价?

  对于工作我比较有责任心和耐心,具备良好的职业素养。对于生活充满信心和爱心,怀着一颗感恩的心,努力做好每一件事情。

  13、您所熟悉的测试用例设计方法都有哪些?

  ● 等价类划分

  ● 边界值分析法

  ● 错误推测法:基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计测试用例的方法

  ● 因果图方法:输入条件之间的联系和相互组合情况,生成判定表。从而设计测试用例

  ● 比较法:比较每个版本 主要用于后期的用例

  ● 业务流程图分析和状态转换分析/业务逻辑分析

  14、通过画因果图来写测试用例的步骤为:

  ● 分析软件规格说明描述中,哪些是原因(即输入条件或输入条件的等价类),哪些是结果(即输出条件),并给每个原因和结果赋予一个标识符。

  ● 分析软件规格说明描述中的语义,找出原因与结果之间,原因与原因之间对应的是什么关系?根据这些关系,画出因果图。

  ● 由于语法或环境限制,有些原因与原因之间,原因与结果之间的组合情况不可能出现。为表明这些特殊情况,在因果图上用一些记号标明约束或限制条件。

  ● 把因果图转换成判定表。

  ● 把判定表的每一列拿出来作为依据,设计测试用例。

  15、您认为做好测试用例设计工作的关键是什么?

  ● 白盒测试用例设计的关键是以较少的用例覆盖尽可能多的内部程序逻辑结果

  ● 黑盒法用例设计的关键同样也是以较少的用例覆盖模块输出和输入接口。不可能做到完全测试,以最少的用例在合理的时间内发现最多的问题

  16、什么是安全性测试?

  全测试是在IT软件产品的生命周期中,特别是产品开发基本完成到发布阶段,对产品进行检验以验证产品符合安全需求定义和产品质量标准的过程 。

  ● 用户认证安全

  ● 系统网络安全

  ● 数据库安全

  17、什么是集成测试?

  在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行集成测试。英文一些模块虽然能够单独工作,但并不能保证连接起来也能正常的工作。程序在某些局部反映不出来的问题,在全局上很可能暴露出来,影响功能的实现。

  集成测试应该考虑以下问题:

  ● 在把各个模块连接起来的时候,穿越模块接口的数据是否会丢失;

  ● 各个子功能组合起来,能否达到预期要求的父功能;

  ● 一个模块的功能是否会对另一个模块的功能产生不利的影响;

  ● 全局数据结构是否有问题;

  ● 单个模块的误差积累起来,是否会放大,从而达到不可接受的程度

  18、你有什么问题要问的?

  可以问一问该公司的测试流程,个人职业发展方向,公司产品项目等等。

posted @ 2012-05-10 09:21 顺其自然EVO 阅读(3166) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 328 329 330 331 332 333 334 335 336 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜