温立辉
(河源职业技术学院 电子与信息工程学院,广东 河源 517000)
触发器是关系数据库进行数据维护的一种重要机制[1],其能实现比关系数据自身携带CHECK函数更加强大的功能,目前主流的关系型数据库均支持这一重要的数据维护机制,触发器的应用存在于各种各样的业务场景中。
触发器作为一种数据维护机制,既不同于关系数据库自身所拥有的函数,也不同于关系数据库中的存储过程,而是一种基于事件与时间作为监测点的功能代码块。文献[2-6]描述了触发器的底层原理及相关的实现机制,阐述了触发器的功能作用及应用场景。从广泛意义上来说,触发器也是属于关系数据库中的功能函数,用户可以自行定义相关的触发器函数,但此类型的自定义函数不能带有任何的参数,这是与一般函数的重要区别。同样,从广义上来说,触发器也是关系数据库中一种特殊的存储过程,但与一般存储过程有一个非常重要的区别就是,存储过程需要显式的被调用,而触发器则是无须被系统或用户显式调用,只要当相关事件发生时就能自动触发执行相关触发器的功能代码语句,从这个意义上来说触发器是自动被执行,不需要手动调用。
触发器存在于关系数据库服务器端[7],其功能除了数据完整性约束之外,还能实现数据审计,数据表级联操作等功能。所谓的数据审计是指跟踪关系数据库业务表中数据的变化过程,以审核数据变化过程是否合法,是否存在不规范的操作,以保证业务数据的正当、准确、合理。数据表级联操作是指业务数据表之间是一个一个统一的有机整体,而不是独立、分散的个体,当某张数据表变化时会引起与此表相关联的相关数据表的同时变化。如,在一个用户模块中,当从用户表删除一条用户记录时,也必需从其他的业务表中删除此用户订阅的相关业务记录,否则业务系统会出异常。
触发器是作用于数据表上的一种操作监测机制[1],在关系数据库中存在多种类型,一般来说常见的是关系数据库管理语言中(Data Manipulation Language,DML)中的插入、删除、更新3种操作类型的触发器,在对数据表进行insert,delete,update3种操作会触发对应类型的触发器。
触发器的触发时间点有之前(before)与之后(after)两种,当定义了触发时间点为before时,则会在对数据表的操作发生之前先行触发数据表上的触发器,触发器功能代码执行完毕后才能执行增、删、改的数据操作;after类型则恰好相反,在对数据表执行完增、删、改操作后才触发数据表上的触发器功能代码。
不言而喻触发器是一种强大的数据维护机制,在关系数据库的底层其采用的是逐行检查以及临时表的方式实现其强大的数据维护功能。
逐行检查触发器的一个基本原则,当一个数据表的某个字段做了某项自定义约束时,如会员表的年龄字段为必须大于18,当某个业务操作要修改会员表中某一条记录的年龄字段值时,则触发器会检查整个会员表中每一行记录的年龄字段的值是否有小于18的情况,如有回滚此次操作。这一原则当然能保证数据约束的完整功能,但同时也会引入其他的问题,如性能问题;在业务表中数据量比较大的情况下,逐行检查严重影响数据库管理系统性能。
使用临时表是触发器另一重要原则,当对业务表进行增、删、改的操作进而触发相应的触发器时,触发器会对操作将触动的业务数据在临时表中进行备份,以供后面所需的回滚、恢复操作。触发器中存在有插入临时表(insert表)与删除临时表(delete表)两种。
当外部要往数据表中插入新的数据时,触发器首先会往insert临时表中插入新数据,然后再往目标数据表插入相关数据,新数据进入目标数据表后再检查新插入的数据是否合法,如合法则直接删除insert临时表中的数据,如不合法则按insert临时表保存的数据去删除目标数据表中新进入的数据。
当外部要从业务数据表删除数据时,触发器则会先把要删除的旧数据插入delete临时表进行数据备份,然后再从目标数据表删除相关数据,最后检查删除操作是否合法,如果合法则直接从delete临时表中删除之前备份的数据,如不合法则用备份好的数据恢复到原目标数据表中。
当外部要从业务数据表修改某条业务数据时,触发器则会往两张临时表中备份数据。首先,把目标数据表中要被修改的旧数据备份到delete临时表;然后,再把修改后的数据同时写入目标数据表与insert临时表;最后,检查操作是否合法,如果合法则删除两张临时表中的数据,反之如果不合法,则按insert临时表的数据去删除目标数据表数据,并把delete临时表备份数据恢复到目标数据表中。
从逐行检查与使用临时表两个原则中可知,触发器在一定程度会影响到数据库性能,因而在使用触发器前一定要严格谨慎,如果有其他更好的解决方案则尽可能不使用触发器。
凡事物都是具有两面性的,虽然触发器的使用会在一定程度影响系统性能,但是触发器还是非常广泛地应用于很多的业务场景中,只要使用得当,考虑周全,触发器有非常广阔的天地,如以下两个场景中,就可以考虑使用触发器。
在一个电子商城平台中需开发一个秒杀抢购功能,按常规的思维,Web应用服务先从数据表中读取库存数据,再通过判断其是否大于0进而结论是否成功秒杀到。在秒杀过程中当库存数剩余为1时,因为平台上有多个Web节点,所以可能多个线程同时读取库存数为1并多个线程同时下结论成功抢到,但1件商品是不可能同时被多个人抢到的,因而常规思维走不通。
此时就要从后台数据库入手,可考虑在数据库的商品表的库存数量字段加个约束让其不能小于0,一般的数据库中可考虑使用CHECK函数进行约束就可以了,但MySQL中不支持CHECK函数功能,因而无法使用这一方式,必须使用触发器来对这个库存数量字段加一个不能小于0的约束,当秒杀开始时对库存数量的修改使用如下的累减原子操作SQL语句“UPDATE 商品表 SET 库存数量=库存数量-1”即可解决上面的问题。
交通违章管理系统记录着所有车辆交通违章信息,只有内部人员能操作系统中的相关数据,但如果有工作人员违规私下修改或删除系统里面数据,那就是一个很大的漏洞。
针对这一问题就可以考虑使用触发器对相关的业务数据表作数据的跟踪审计,记录下数据变化全过程,比如,哪个用户在什么时间基于什么理由修改了什么数据,这样业务系统中整个数据变化过程就能正常的追踪,堵塞了管理上的重大漏洞。
触发器作为一种独立的机制,其强大功能与优势是显而易见的,缺少触发器机制的关系数据库是不完整的。触发器虽然如此强大与重要,但决不能乱用,更不能滥用,如果使用不当极容易使数据库系统变得性能、效率低下。使用触发器需坚持一个原则,那就是:必需、必要、数据量小。