虽然说SQL Server数据库本身提供了很好的锁管理机制。但是,从某一方面来说,其实数据库只是一些客户端应用程序的“傀儡”。这主要是因为客户端应用程序对服务器上获取的锁几乎有完全的控制能力。客户端应用程序发出的查询请求以及对结果的处理方式,往往具有直接的控制能力。所以,如果应用程序在设计上稍有不合理的情况时,就会因为锁机制而导致阻塞。
如当遇到如下几种情形时,就可能会导致阻塞情况的发生。
一、客户端取消查询后没有回滚实务。
查询是大部分应用程序经常发生的作业。但是,用户通过前台客户端应用程序查询后台数据库时,有时候往往会因为各种原因取消查询。如用户打开查询窗口后,因为死机或者用户觉得反映速度慢强制取消查询。但是,当客户端取消查询时,若没有加上回滚事务的语句,则此时,因为用户已经向服务器发送了查询请求,所以,后台数据库中所涉及的表,都已经加L上了锁。故即使用户取消查询后,所有在事务内获取的锁都将会保留。此时,若其他用户也需要查询这些表或者用户重新打开查询窗口想通过输入查询条件来提高系统响应速度时,就会发生阻塞的现象。
二、客户端没有及时取得所有查询的结果。
通常情况下,用户将查询请求发送到服务器之后,前台应用程序必须立即完成提取所有结果行。如果应用程序没有提取所有结果行的话,就会产生一个问题。因为只要应用程序没有及时提取所有结果,锁可能会留在表上而阻塞其他用户。既然应用程序已经将SQ语句递交给服务器,则该应用程序就必须提取所有的结果行。若应用程序不遵循这个原则的话(如因为一时疏漏而没有配置),就无法从根本上解决阻塞问题。
三、查询执行时间过长。
有些查询会耗用比较长的时间。如因为查询语句设计不合理或者查询设计到的表与记录比较多时,都会使得查询的执行时间加长。如有时候用户需要对纪录进行Update或者Delete操作时,如果涉及的行比较多时,就会获取很多的锁。这些锁无论是否最终升级到表锁,都会阻塞其他查询。
故通常情况下,不要将长时间运行的决策支持查询和联机事务处理查询混在一起。
当数据库遇到阻塞时,往往需要检查应用程序递交的SQL语句本身,以及检查与连接管理、所有结果行的处理等有关的应用程序行为。通常情况下,为了避免因锁冲突所导致的阻塞,笔者有如下建议。
建议一:查询完成后提取所有的结果行。
有些应用程序为了提高用户查询的响应速度,会有选择的提取所需要的记录。这个“小聪明”看起来很合理,但是,却会造成更大的浪费。因为查询结果没有及时提取的话,锁就不能释放。当其他人查询数据时,就会发生阻塞。
所以,笔者建议在应用程序设计时,对于数据库中查询的记录要及时的提取。可以通过其他方式,如添加查询条件、或者后台查询的方式,来提高查询的效率。同时,在应用程序层面设置合理的缓存,也可以非常明显的提高查询效率。
建议二:在事务执行时不要让用户输入内容。
虽然在事务执性的过程中,可以让用户参与进来,以提高互动性。但是,我们数据库管理员往往不建议这么做。因为若要用户在事务执行过程中输入参数,会延长事务的执行时间。虽然人比较聪明,但是其反应速度仍然没有电脑那么快。所以,在执行过程中加入让用户参与的过程,会延长事务的等待时间。故除非有特殊的需要,不要在应用程序的执行过程中,提醒用户输入参数。一些事务执行必须的参数,最好在事先就提供。如可以通过变量等预先把需要的参数传入进去。
建议三:使事务尽可能的简短。
笔者认为,数据库管理员应该把一些问题简单化。当某个需求需要很多SQL语句才能够完成时,不妨把任务进行分解。同时,也把事务分解成一些简短的事务。
如数据库中一张产品信息表,其记录数量有二百万条。现在处于管理的需要,把一次性更改其中的一百五十万条记录时。若通过一个事务进行更改,则其时间会比较长。若其中还牵涉到级联更新的话,则时间会更长。
针对这种情况,我们就可以学着把事务简短话。如这个产品信息中,可能有产品类型字段。那么在更新数据时,我们能否不一次性进行更新。而是通过产品类别字段进行控制,对记录进行分次更新的。如此每个类别的更新事务所耗用的时间就可能会大大缩短。如此虽然操作的时候,会需要多个步骤。但是,往往可以有效避免阻塞情况的发生,提高数据库的性能。