原文地址: 使用ngrinder进行数据库性能测试
nGrinder是一个开源的性能测试系统,项目主页:http://www.nhnopensource.org/ngrinder/。
nGrinder是将python脚本部署到多台agent机器,然后根据配置使用多进程多线程执行,达到高并发的测试目的。agent端使用jython来执行脚本。因此,nGrinder的一大优势就是可以通过使用java的包来扩展,在脚本里面调用java里面的方法来测试某些特殊的应用。
下面就使用这种方式,来使用nGrinder来对数据库进行压力测试。
首先,来创建脚本。在脚本页面,点击创建脚本:
输入脚本名,不需要输入url,选上下面的选项,他会帮我们创建lib目录,脚本执行过程中使用的扩展包就需要放在lib目录下,如果有其他资源,例如properties文件等,就放在resources目录下。
点击创建,它会帮我们使用http测试的模板,创建一个简单的脚本,我们按照下面直接修改这个脚本:
# -*- coding:utf-8 -*- # Database test. # from java.sql import DriverManager from net.grinder.script.Grinder import grinder from net.grinder.script import Test from cubrid.jdbc.driver import CUBRIDDriver from java.util import Random from java.lang import System test1 = Test(1, "Database insert") test2 = Test(2, "Database update") random = Random(long(System.nanoTime())) # Load the JDBC driver. DriverManager.registerDriver(CUBRIDDriver()) def getConnection(): return DriverManager.getConnection("jdbc:CUBRID:localhost:33000:ngrinder_cluster:::?charset=utf-8", "user", "password") def ensureClosed(object): try: object.close() except: pass # One time initialization that cleans out old data. connection = getConnection() statement = connection.createStatement() # initialize the table should want to test. statement.execute("drop table if exists ngrinder_update_temp") statement.execute("create table ngrinder_update_temp(testid integer, test_number integer)") ensureClosed(statement) ensureClosed(connection) class TestRunner: def __call__(self): connection = None updateStatement = None insertStatement = None try: # in this test, we will create connection and statement in every test transaction. connection = getConnection() updateStatement = connection.createStatement() insertStatement = connection.createStatement() insertStatement = test1.wrap(insertStatement) tmpId = random.nextInt(1024000) insertStatement.execute("insert into ngrinder_update_temp values(%d, %d)" % (tmpId, random.nextInt(1024000))) updateStatement = test2.wrap(updateStatement) updateStatement.execute("update ngrinder_update_temp set test_number=%d where testid=%d" % (random.nextInt(1024000), tmpId)) finally: ensureClosed(updateStatement) ensureClosed(insertStatement) ensureClosed(connection)
在上面的脚本里,我们先初始化,然后在每次执行时重新获取connection,创建statement,执行2个sql,然后关闭connection和statement。 其中,__call__方法就是要反复执行的方法,它里面有2个test,也就是说,。每执行一次这个方法,并且两个test都执行成功,那么执行的事物的次数就增加2.每秒钟,执行引擎都会统计这一秒执行成功的事物,来统计tps。
在保存之前,可以点击上面的“Validate”来验证脚本是否正确。注意Python需要正确的缩进来保证语法正确,复制过程中,可能会有缩进的问题。验证完以后,别忘记保存,验证过程中是不会保存脚本的。
如果Validate验证结果显示找不到数据库的driver,那就可以保存脚本,然后先上传数据库的JDBC驱动.
打开script列表,在刚才创建的脚本的同级目录中,找到lib进入,然后点击上传按钮:
选择要测试的数据库的相应的JDBC驱动包以后,上传,就可以在lib目录里看到.
再次打开刚才是脚本,验证,如果没有别的问题,就应该在下面看到如下的消息:
2013-01-05 11:06:08,622 INFO The Grinder version 3.9.1
2013-01-05 11:06:08,628 INFO Java(TM) SE Runtime Environment 1.6.0_38-b05: Java HotSpot(TM) 64-Bit Server VM (20.13-b02, mixed mode) on Linux amd64 2.6.18-238.el5
2013-01-05 11:06:08,633 INFO time zone is CST (+0800)
2013-01-05 11:06:08,749 INFO worker process 0
2013-01-05 11:06:08,916 INFO instrumentation agents: traditional Jython instrumenter; byte code transforming instrumenter for Java
2013-01-05 11:06:13,335 INFO running "jdbs-test.py" using Jython 2.2.1
2013-01-05 11:06:13,358 INFO starting, will do 1 run
2013-01-05 11:06:13,359 INFO start time is 1357355173359 ms since Epoch
2013-01-05 11:06:13,410 INFO finished 1 run
2013-01-05 11:06:13,412 INFO elapsed time is 53 ms
2013-01-05 11:06:13,412 INFO Final statistics for this process:
2013-01-05 11:06:13,423 INFO
Tests Errors Mean Test Test Time TPS
Time (ms) Standard
Deviation
(ms)
Test 1 1 0 3.00 0.00 18.87 "Database insert"
Test 2 2 0 0.50 0.50 37.74 "Database update"
Totals 3 0 1.33 1.25 56.60
Tests resulting in error only contribute to the Errors column.
Statistics for individual tests can be found in the data file, including
(possibly incomplete) statistics for erroneous tests. Composite tests
are marked with () and not included in the totals.
2013-01-05 11:06:13,347 INFO validation-0: starting threads
2013-01-05 11:06:13,423 INFO validation-0: finished
3)然后,打开test列表页面使用这个脚本创建一个test:
最好在Target Host里面添加数据库所在的机器作为监控目标,这样就能在Report页面看到这台服务器的监控信息。当然,你需要在那台机器上运行Monitor。
4)点击保存并运行。下面就是运行过程中的监控页面。由于,我们每次都重新连接,再加上测试的DB已经有应用在运行,所以TPS非常低。MTT(平静测试时间)也接近半秒。
下面详细report页面。
如何提高TPS? 上面的测试中,每次都会重新连接,下面再修改脚本,在测试中都使用一开始的connection进行测试。
# -*- coding:utf-8 -*- # Database test. # from java.sql import DriverManager from net.grinder.script.Grinder import grinder from net.grinder.script import Test from cubrid.jdbc.driver import CUBRIDDriver from java.util import Random from java.lang import System test1 = Test(1, "Database insert") test2 = Test(2, "Database update") random = Random(long(System.nanoTime())) # Load the JDBC driver. DriverManager.registerDriver(CUBRIDDriver()) def getConnection(): return DriverManager.getConnection("jdbc:CUBRID:localhost:33000:ngrinder_cluster:::?charset=utf-8", "user", "password") def ensureClosed(object): try: object.close() except: pass # One time initialization that cleans out old data. connection = getConnection() statement = connection.createStatement() # initialize the table should want to test. statement.execute("drop table if exists ngrinder_update_temp") statement.execute("create table ngrinder_update_temp(testid integer, test_number integer)") ensureClosed(statement) #ensureClosed(connection) class TestRunner: def __call__(self): #connection = None #updateStatement = None #insertStatement = None try: # in this test, we will create connection and statement in every test transaction. # connection = getConnection() updateStatement = connection.createStatement() insertStatement = connection.createStatement() insertStatement = test1.wrap(insertStatement) tmpId = random.nextInt(1024000) insertStatement.execute("insert into ngrinder_update_temp values(%d, %d)" % (tmpId, random.nextInt(1024000))) updateStatement = test2.wrap(updateStatement) updateStatement.execute("update ngrinder_update_temp set test_number=%d where testid=%d" % (random.nextInt(1024000), tmpId)) finally: ensureClosed(updateStatement) ensureClosed(insertStatement) # ensureClosed(connection)
下面是Report,结果就比刚才好很多。
这个例子中,使用JDBC的Jar包来扩展,达到测试的目的。基于这种方法,也可以使用任意的其他Jar来扩展,根据具体需要进行各种性能测试。