触发器(TRIGGER)对DB2数据库系统性能的影响
摘要:触发器(TRIGGER)是DB2数据库系统的一个重要特性。触发器是一种特殊的存储过程,它在插入(INSERT),删除(DELETE)或更新(UPDATE)特定表中的数据时触发执行一系列SQL操作,它比DB2数据库本身标准的功能有更精细和更复杂的数据控制能力。触发器对数据库系统的性能有什么样的影响呢?本文就针对触发器对DB2数据库系统性能的影响进行了测试和比较。
前言:
触发器(TRIGGER)是DB2数据库系统的一个重要特性。触发器(TRIGGER)是一种用户定义的特殊的存储过程,它在插入(INSERT),删除(DELETE)或更新(UPDATE)特定表中的数据时触发执行一系列用户定义的SQL操作,它比数据库本身标准的功能有更精细和更复杂的数据控制能力。因此触发器(TRIGGER)实际上是用户的数据库应用(APPLICATION)的一部分。
利用触发器(TRIGGER),用户可以实现复杂的业务逻辑,例如:
1. 可以利用触发器(TRIGGER)基于数据库的值赋予用户具有操作数据库的某种权利。
2. 可以利用触发器(TRIGGER)基于时间限制用户的操作,例如不允许下班后和节假日修改数据库数据。
3. 可以利用触发器(TRIGGER)基于数据库中的数据限制用户的操作,例如不允许价格的升幅一次超过10%。
4. 审计。可以利用触发器(TRIGGER)跟踪用户对数据库的操作。例如审计用户操作数据库的语句并且把用户对数据库的更新写入审计表。
5. 可以利用触发器(TRIGGER)实现复杂的数据完整性规则。实现非标准的数据完整性检查和约束。触发器可产生比规则更为复杂的限制。与规则不同,触发器可以引用列或数据库对象。例如,触发器可回退任何企图吃进超过自己保证金的期货。
6. 可以利用触发器(TRIGGER)提供可变的缺省值。
7. 可以利用触发器(TRIGGER)实现复杂的非标准的数据库相关完整性规则。触发器可以对数据库中相关的表进行连环更新。
8. 可以利用触发器(TRIGGER)同步实时地复制表中的数据。
9. 可以利用触发器(TRIGGER)自动计算数据值,如果数据的值达到了一定的要求,则进行特定的处理。例如,如果公司的帐号上的资金低于5万元则立即给财务人员发送警告数据。
触发器(TRIGGER)带给了DB2数据库应用开发巨大的便利, 使用户可以自己定制针对特定表的插入(INSERT),删除(DELETE)或更新(UPDATE)操作。但是, 触发器(TRIGGER)的存在使原本简单的插入(INSERT)、删除(DELETE)和更新(UPDATE)操作变得复杂, 因此我们在享受触发器(TRIGGER)带给我们的便利的时候, 也不得不面对触发器带给数据库系统的性能损失。
本文就针对触发器对DB2数据库系统性能的影响进行了测试和比较。
测试步骤:
在定义了触发器(TRIGGER)后, DB2数据库系统在执行完每一次的插入(INSERT)、删除(DELETE)或者更新(UPDATE)操作之后, 都要执行响应的触发器(TRIGGER)。 因此,触发器对数据库系统性能的影响来自于两个方面:
1. 初始化触发器运行上下文(CONTEXT)需要的时间;
2. 执行触发器(TRIGGER)中定义的SQL语句需要的时间。
笔者设计了以下测试步骤来测试以上两种因素对DB2数据库系统性能的影响。
1. 测试准备:创建表和触发器(TRIGGER)
笔者创建了4张结构相同表SRCTB,TB1, TB2,和TB3, 这3张表的定义如下:
CREATE TABLE SRCTB (A INTEGER, B INTEGER); CREATE TABLE TB1 (A INTEGER, B INTEGER); CREATE TABLE TB2 (A INTEGER, B INTEGER); CREATE TABLE TB3 (A INTEGER, B INTEGER); |
创建一个源数据表SRCTB,这个表和表TB1、TB2和TB3结构相同,在这个表中存有1,000,000条数据, 创建这个表是为了在测试中向表TB1、TB2和TB3中一次性插入1,000,000条数据。
为了测试和比较触发器(TRIGGER)对DB2数据库系统性能的影响, 笔者设计了以下测试环境:
1. 在表TB1上不定义触发器(TRIGGER);
2. 在表TB2上定义3个空(DUMMY)触发器,即这个触发器不执行任何SQL语句,这3个触发器分别是插入(INSERT)触发器、删除(DELETE)触发器 和更新(UPDATE)触发器;
3. 在表TB3上定义3个触发器,这3个触发器分别是插入(INSERT)触发器、删除(DELETE)触发器 和更新(UPDATE)触发器,这些触发器只用来执行一个简单的SQL语句, 把相应的数据存储到一个日志表(LOGTB)中。
创建脚本如下:
1. 创建源数据表(SRCTB)的脚本CREATE_SRCTB.SQL内容如下:
--清单1 CREATE_SRCTB.SQL CONNECT TO TESTTRG; CREATE TABLE SRCTB (A INTEGER, B INTEGER); CREATE INDEX ISRCTB ON SRCTB (A,B); DISCONNECT TESTTRG; |
2. 创建测试环境, 包括表TB1、TB2、TB3和相应的触发器(TRIGGER),创建脚本CREATE_TEST_ENV.SQL内容如下:
--清单2 CREATE_TEST_ENV.SQL CONNECT TO TESTTRG; CREATE TABLE TB1 (A INTEGER, B INTEGER); CREATE TABLE TB2 (A INTEGER, B INTEGER); CREATE TABLE TB3 (A INTEGER, B INTEGER); --LOGTB是日志表-- CREATE TABLE LOGTB (A INTEGER, B INTEGER); CREATE TRIGGER TESTTRG1 AFTER INSERT ON TB2 REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC END; CREATE TRIGGER TESTTRG2 AFTER UPDATE ON TB2 REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC END; CREATE TRIGGER TESTTRG3 AFTER DELETE ON TB2 REFERENCING OLD AS O FOR EACH ROW MODE DB2SQL BEGIN ATOMIC END; CREATE TRIGGER TESTTRG4 AFTER INSERT ON TB3 REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC INSERT INTO LOGTB (A,B) VALUES (N.A, N.B); END; CREATE TRIGGER TESTTRG5 AFTER UPDATE ON TB3 REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC INSERT INTO LOGTB (A,B) VALUES (N.A, N.B); END; CREATE TRIGGER TESTTRG6 AFTER DELETE ON TB3 REFERENCING OLD AS O FOR EACH ROW MODE DB2SQL BEGIN ATOMIC INSERT INTO LOGTB (A,B) VALUES (O.A, O.B); END; DISCONNECT TESTTRG; |
3. 销毁测试环境脚本DESTROY_TEST_ENV.SQL脚本如下:
--清单3 DESTROY_TEST_ENV.SQL CONNECT TO TESTTRG; DROP TRIGGER TESTTRG1; DROP TRIGGER TESTTRG2; DROP TRIGGER TESTTRG3; DROP TRIGGER TESTTRG4; DROP TRIGGER TESTTRG5; DROP TRIGGER TESTTRG6; DROP TABLE LOGTB; DROP TABLE TB3; DROP TABLE TB2; DROP TABLE TB1; DISCONNECT TESTTRG; |
2. 测试:分别在表TB1、TB2和TB3上执行插入(INSERT)、更新(UPDATE)和删除(DELETE)操作
测试名称 |
测试内容 |
TEST1 |
执行SQL语句,向表TB1中插入1,000,000条数据,并记录SQL语句执行的时间 |
TEST2 |
执行SQL语句,更新表TB1中的这1,000,000条数据,并记录SQL语句执行的时间 |
TEST3 |
执行SQL语句,删除表TB1中的这1,000,000条数据,并记录SQL语句执行的时间 |
TEST4 |
执行SQL语句,向表TB2中插入1,000,000条数据,记录SQL语句执行的时间,并与TEST1中的结果比较 |
TEST5 |
执行SQL语句,更新表TB2中的这1,000,000条数据,记录SQL语句执行的时间,并与TEST2中的结果比较 |
TEST6 |
执行SQL语句,删除表TB2中的这1,000,000条数据,记录SQL语句执行的时间,并与TEST3中的结果比较 |
TEST7 |
执行SQL语句,向表TB3中插入1,000,000条数据,记录SQL语句执行的时间,并与TEST1中的结果比较 |
TEST8 |
执行SQL语句,更新表TB3中的这1,000,000条数据,记录SQL语句执行的时间,并与TEST2中的结果比较 |
TEST9 |
执行SQL语句,删除表TB3中的这1,000,000条数据,记录SQL语句执行的时间,并与TEST3中的结果比较 |
由于表TB1上没有定义触发器(TRIGGER),因此TEST1、TEST2、TEST3的测试结果是后面测试的基准。
表TB2上定义的插入(INSERT)、更新(UPDATE)、删除(DELETE)为空(DUMMY)触发器, 因此TEST4、TEST5、TEST6的目的是测试初始化触发器运行上下文(CONTEXT)对DB2数据库系统性能的影响。
表TB3上定义的插入(INSERT)、更新(UPDATE)、删除(DELETE)为一个简单的触发器, 因此TEST7、TEST8、TEST9的目的是测试触发器对DB2数据库系统性能的影响。
在测试过程中, 笔者适用DB2数据库系统提供的GET SNAPSHOT语句来记录SQL语句的运行时间:
UPDATE MONITOR SWITCHES USING STATEMENT ON; GET SNAPSHOT FOR DYNAMIC SQL ON <DATABASE> |
测试脚本如下
1. 针对表TB1进行插入(INSERT)测试的脚本TEST1.SQL内容如下:
--清单4 TEST1.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; INSERT INTO TB1 (A,B) SELECT A,B FROM SRCTB; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
2. 针对表TB1进行更新(UPDATE)测试的脚本TEST2.SQL的内容如下:
--清单5 TEST2.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; UPDATE TB1 SET B=-1; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
3. 针对表TB1进行删除(DELETE)测试的脚本TEST3.SQL内容如下:
--清单6 TEST3.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; DELETE FROM TB1; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
6. 针对表TB2进行插入(INSERT)测试的脚本TEST4.SQL内容如下:
--清单7 TEST4.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; INSERT INTO TB2 (A,B) SELECT A,B FROM SRCTB; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
7. 针对表TB2进行更新(UPDATE)测试的脚本TEST5.SQL内容如下:
--清单8 TEST5.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; UPDATE TB2 SET B=-1; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
8. 针对表TB2进行删除(DELETE)测试的脚本TEST6.SQL内容如下:
--清单9 TEST6.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; DELETE FROM TB2; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; DISCONNECT TESTTRG; |
9. 针对表TB3进行插入(INSERT)测试的脚本TEST7.SQL内容如下:
--清单10 TEST7.SQL CONNECT TO TESTTRG; UPDATE MONITOR SWITCHES USING STATEMENT ON; INSERT INTO TB3 (A,B) SELECT A,B FROM SRCTB; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; SELECT COUNT(*) FROM LOGTB; DISCONNECT TESTTRG; |
10. 针对表TB3进行更新(UPDATE)测试的脚本TEST8.SQL的内容如下:
--清单11 TEST8.SQL CONNECT TO TESTTRG; DELETE FROM LOGTB; UPDATE MONITOR SWITCHES USING STATEMENT ON; UPDATE TB3 SET B=-1; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; SELECT COUNT (*) FROM LOGTB; DISCONNECT TESTTRG; |
11. 针对表TB3进行删除(DELETE)测试的脚本TEST9.SQL内容如下:
清单12 TEST9.SQL CONNECT TO TESTTRG; DELETE FROM LOGTB; UPDATE MONITOR SWITCHES USING STATEMENT ON; DELETE FROM TB3; GET SNAPSHOT FOR DYNAMIC SQL ON TESTTRG; SELECT COUNT(*) FROM LOGTB; DISCONNECT TESTTRG; |
综上所述,执行一次完整的测试的过程如下:
清单13 RUN_TEST.SH DB2 -TVF CREATE_TEST_ENV.SQL DB2 -TVF TEST1.SQL > TEST1_RESULT.TXT DB2 -TVF TEST2.SQL > TEST2_RESULT.TXT DB2 -TVF TEST3.SQL > TEST3_RESULT.TXT DB2 -TVF TEST4.SQL > TEST4_RESULT.TXT DB2 -TVF TEST5.SQL > TEST5_RESULT.TXT DB2 -TVF TEST6.SQL > TEST6_RESULT.TXT DB2 -TVF TEST7.SQL > TEST7_RESULT.TXT DB2 -TVF TEST8.SQL > TEST8_RESULT.TXT DB2 -TVF TEST9.SQL > TEST9_RESULT.TXT DB2 -TVF DESTROY_TEST_ENV.SQL |
3. 测试结果
笔者的测试环境为:
CPU: INTEL PENTIUM(R) 4 CPU 3.20GHZ 内存: 2G 硬盘: 120G 操作系统:REDHAT ENTERPRISE LINUX 4 UPDATE 4 DB2数据库系统版本为:DB2 V9.1.0.0 FOR LINUX |
测试结果为:
|
表TB1 |
表TB2 |
表TB3 | |||
|
性能损失 |
|
性能损失 | |||
插入(INSERT) |
执行时间(秒) |
8.763309 |
11.446719 |
30.6% |
21.071327 |
140.4% |
用户CPU时间(秒) |
3.050000 |
6.573333 |
115.5% |
11.080000 |
263.2% | |
系统CPU时间(秒) |
0.100000 |
0.116667 |
16.7% |
0.203333 |
120.3% | |
更新(UPDATE) |
执行时间(秒) |
6.409958 |
8.928048 |
39.3% |
20.508765 |
221.2% |
用户CPU时间(秒) |
2.393333 |
5.290000 |
120.4% |
11.506667 |
380.8% | |
系统CPU时间(秒) |
0.093333 |
0.103333 |
10.7% |
0.206667 |
121.4% | |
删除(DELETE) |
执行时间(秒) |
8.487340 |
10.503482 |
23.8% |
21.931643 |
158.4% |
用户CPU时间(秒) |
3.413333 |
5.433333 |
59.1% |
11.216667 |
228.6% | |
系统CPU时间(秒) |
0.100000 |
0.090000 |
-10% |
0.196667 |
96.7% |
从测试结果来看, 触发器(TRIGGER)对DB2数据库系统的性能的影响还是很大的,空(DUMMY)触发器使插入(INSERT)、更新(UPDATE)、删除(DELETE)SQL语句的执行时间增加了30%左右;如果触发器(TRIGGER)中还定义了其他的SQL语句,触发器使插入(INSERT)、更新(UPDATE)、删除(DELETE)SQL语句的执行时间增加了130%以上。
结论
在本文的测试用例中, 笔者创建的触发器(TRIGGER)比较简单。 然而实际的情况可能更要糟糕,在复杂的DB2数据库应用中,往往需要创建执行复杂逻辑的触发器(TRIGGER),在触发器执行的SQL语句可能会导致更严重的性能下降,例如这些SQL语句需要执行更长的时间, 或者这些SQL语言要锁住更多的资源,甚至造成死锁(DEADLOCK)。
综上所述, 在对性能要求较高DB2数据库应用中, 要尽可能的避免使用触发器(TRIGGER),在设计触发器的时候, 要使触发器尽可能的简单。