数据库并发操作异常及解决办法

数据库的并发操作

操作系统中对于并发的定义:并发是指两个或多个事件在同一时间间隔内发生。

注意同一时间间隔(并发)和同一时刻(并行)的区别。在多道程序环境下,一定时间内,宏观上有多道程序在同时执行,而在每个时刻,单处理机环境下仅有一道程序能够执行,因此微观上这些程序仍是分时交替执行的。操作系统的并发性是通过分时得以实现的。

数据库系统是操作系统中的应用软件,它也无法实现并行操作。因此,可以得知数据库的多个事务也是交替执行(并发)的。这种的交替运行,会引起一些数据的错误操作:丢失修改、脏读、不可重复读。

丢失修改

定义:当一个事务修改了数据,并且这种修改还没有还没有提交到数据库中时,另外一个事务又对同样的数据进行了修改,并且把这种修改提交到了数据库中。这样,数据库中没有出现第一个事务修改数据的结果,好像这种数据修改丢失了一样。

以火车售票系统为例,对于数据表(车次,剩余票数),一个售票事务的处理过程如下:

	(1) 查询该车次剩余票数x=16

	(2) 执行售票操作:x=x–1,得x=15
	
	(3) 将x=15写回该车次剩余票数。

这样一个事务在串行运行的数据库系统中是没有问题的,如果两个事务串行运行,各售一张票,最终结果为14。但如果在并行系统中,可能会有两个售票事务实例同时执行,由于CPU分时间片轮流执行事务,这时有可能发生如下情况(执行次序自上而下,两个事务交叉运行):

	售票事务T1 售票事务T2

  (1)查询剩余票数x=16

  (2)查询剩余票数x=16

  (3)x=x-1,得x=15

  (4)将x=15写回数据库。

  (5)x=x-1,得x=15

  (6)将x=15写回数据库。

即T1先查询出了剩余票数16,此时它把控制权交给CPU,等待分配下一个时间片执行。然后T2获得了执行权,查出票数依然是16。然后T1和T2不管如何轮换执行,售出一张票后(售多张票是类似的),都将得到结果15。那么后一个事务提交的数据就覆盖了前一个事务的数据,最终结果是15,这就是所谓的丢失修改问题。下面谈谈如何解决这种普遍性的问题。

丢失修改的解决方案

其实引起修改丢失的关键就是前一个事务修改之后,后一个事务再次修改,那么前面一个事务的修改便没有了作用,这是并发执行导致的。如果是串行执行,将不会出现丢失修改的错误。

我们可以采用一个时间戳来记录哪个事务先修改了数据库。时间戳不是一个时间,而类似于一个自动增长字段,但它有一个特点,就是每次更新某条记录时,会自动更新为一个新的时间戳数据。在SQL Server中,设置为一个字段为timestamp数据类型,读取时可以使用varbinary类型读取。

主要思路是:读取剩余票数时就同时读取该记录的时间戳,当更新记录时,判断时间戳是否与原来读取的相同,如果不同,说明已经有一个事务修改了这条记录,就让当前事务失败。这其实就实现了一种串行操作,一个事务的修改执行完成并提交之后,才允许其他修改事务成功执行。

这样我们把数据库表修改为:车次表(车次,剩余票数,修改时间)。注意修改时间字段设置为timestamp类型,不允许为空,这样初始化时就先自动生成了一个时间戳。

数据库的隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。

脏读

定义:当一个事务正在访问数据,并对数据进行了修改,而这种修改还没有提交到数据库中,这时,另一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

将隔离级别设置为Read committed 时,可以避免脏读,但是可能会造成不可重复读。

不可重复读

在一个事务内,多次读同一数据。在这个事务还没有结束时,另一个事务也访问该同一数据,那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能是不一样的。

不可重复读和脏读的区别是:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

将隔离级别设置为Repeatable read 时,可以避免不可重复读,但是可能会造成幻读。

幻读

指一个事务A对一个表中的数据进行了修改,而且该修改涉及到表中所有的数据行;同时另一个事务B也在修改表中的数据,该修改是向表中插入一行新数据。那么经过这一番操作之后,操作事务A的用户就会发现表中还有没修改的数据行,就像发生了幻觉一样。这就被称作幻读。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

将隔离级别设置为Serializable (串行化) 时, 可以解决幻读的问题。
Serializable 是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。

参考资料

  1. https://blog.csdn.net/qq_34569497/article/details/79064208
  2. https://www.cnblogs.com/pyq228/archive/2012/05/26/2519446.html
  3. https://blog.csdn.net/lph188/article/details/85260553
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值