数据库引擎-事务和并发

事务是一个很基本的概念,简单的说就是一个组操作一起做完。
当两个事务并发执行时,可能会出现以下的现象:
修改丢失:两个线程同时读出某行的值,修改完先后保存回去,先保存的值就丢失了。假设你和你老婆在两个ATM上同时对同一个账号存钱,开始时账户里 有10万,然后你存1万,你老婆存2千。你们家收入不少,不过在北京买房子还是不够的。题外话。如果出现了这里的错误,糟糕,你会发现存完了只有11万或 者10万2千。

读取未提交写:某个事务写入了一个值,事务还没有提交,就被另一个事务读取了,接着事务1又失败回滚了,事务2却不知道。好吧,又存钱了。这次你 LP先存,你稍后存。你LP把钱放入自动存款机,机器数了数,嗯 2千,于是改变账户余额。这时你又开始存钱,于是账户余额变成11万2千。不料这时候,存款机突然爆裂,钱自己吐出来了。于是你老婆又拿回了钱,而你们的 账号却多了很多钱。

重复读不一致:多次读同一行,结果不一样。跟未提交读类似,但这次事务1可能提交了。比如有天你去银行,在第一个柜台,职员说,您的余额是100万,使我们的贵宾客户,请上二楼。您愉快的上到二楼,职员说您的余额只有10万块,请到地下一楼小客户部。
重复读不一致还有两个变种:
幻像:第一读出某个范围内的一些行,再次读的时候某行被删掉了,或者某个新行被插入了。编辑该文章,辛辛苦苦改了100页,发表出去,结果第二页谈的是反党话题。原来这第二页是在编辑该第50页的时候加进来的。
迷踪:你查到100行数据,正从小到大依次查看,孙悟空,猪八戒,太上老君,嗯,怎么又是孙悟空,不过这下变高了。嗯观音菩萨跑哪里去了,哦,刚才 排序 时候站着,现在坐下了,就拍到前面去了。这和幻像比较类似,不同的是这次没有加入新的内容。即使在read committed的情况下,这种现象也可能发生。

需要注意的是,上面的这些现象并不一定是错误。交易所的股票价格一直都在变化,这并不意味着数据的损坏。
但是有些时候,确实需要避免上述的现象,比如银行。设置事务的隔离级别能够消除事务并发时不能容忍的现象。
隔离级别主要影响读操作的加锁请求,如果要读取的行还没有锁,那么是否要申请锁,这个锁何时释放;如果要读取的行正在被修改,那么等待写的事务提交,还是直接读取还没提交的值,或者读取目标行最后一个提交的值。
隔离级别不会影响到写操作的加锁情况,它永远是排他锁,但它会影响读操作的加锁情况。隔离级别越高,读操作的被阻塞的次数就越多,但读发生的错误现象就越少;相反,隔离级别越低,读操作被阻塞次数就越少,出现幻影,不重复读的次数就越多。
SQL Server支持4种标准的隔离级别:
Read uncommitted,非提交读,即使写操作的事务没有提交,也可以读取它的值
Read Committed,必须要等写事务提交了才能读取它的值。这是缺省隔离级别。
Repeatable read,在一个事务里读两次,结果是一样的。这比2要强,因为2允许数据在两次读之间被修改。从这个级别开始,读要加锁了。共享锁,阻止别的事务修改数据,但可以插入数据。
Serializable,最高级别了。所有事务顺序执行。不能读没有提交的数据,不允许别的事务修改已经读的数据,不允许别的事务插入范围内的行。使用Range lock锁定所有范围,阻止别的事务修改和插入行。
隔离级别针对整个连接有效。可以通过下面的语句设置隔离级别:
SET TRANSACTION ISOLATION LEVEL
    { READ UNCOMMITTED
    | READ COMMITTED
    | REPEATABLE READ
    | SNAPSHOT
    | SERIALIZABLE
    }
除非查询使用hints,使得对某个查询使用不同隔离别。比如某个查询在某个表上加了nolock,那么虽然数据库的默认隔离级别是read committed,别的事务还是可以修改该查询读到的数据。
除了上述的隔离级别外,SQL Server还支持Snapshot,它使用row version的技术,事务A可以读取目标行在A开始时刻的已经提交的值
隔离级别是通过加Lock来控制的。每个事务在访问某个资源之前,必须申请到资源上的锁,当申请成功后,该资源就被锁定。如果别的事务也需要访问该资源,申请的锁与目标已有的锁必须相容;如果不相容,则必须阻塞等待。
那么数据库里有哪些资源呢?
RID:行的ID号,用于锁定一行数据
KEY:键值,用于锁定一些具有相同键值的行
PAGE:页
Extent:连续的8个页
HoBT 堆或者索引树:当重建索引或者在堆上插入数据的时候
Table:锁定整个表
File:数据文件, 备份的时候?
Application: 不知道
Metadata 表的结构等:修改表结构的时候,给表加数据的时候
Allocation unit 分区:它应该比Table的粒度小才对
DataBase 整个数据库
锁可以作用在上述不同的粒度上,导致不同的类型资源被锁定。锁的粒度越低,并发时等待的时间就越少。但锁的个数就越多。由于锁本身也占用资源,所以也会引起资源的耗费。系统会根据任务选择锁的粒度。并随着任务的进展,提高或降低锁的粒度。而且为了查询一个数据,数据库通常需要在多个层次上加锁,才能完成保护数据。
那么数据库都能加什么锁呢?
简单来讲,数据库支持下面的锁类型:
1.    IS意向共享,表明要对资源下属的某些行进行共享的访问,读
2.    S 共享,表明要对资源进行读操作
3.    U 修改,表要对资源进行修改,但修改尚未进行,别人还可以读,但不能企图修改
4.    IX 意向排他,表明即将进行修改,不能再有新的读操作
5.    SIX共享意向排他,读取该资源,并对部分下属资源表明修改意向
6.    X排他,修改进行中
 如果事务1申请了一个锁,事务2只有申请序号小于或等于它的锁,否则必须等事务1释放锁。X锁不与任何锁兼容。IX和自己兼容,因为两个事务可能只是对意向中不同部分进行排他独占。详细相容表如下:

 可以通过存储过程Sp_lock来查看进程的锁的情况。

结合SP_who查看当前会话的进程。

如果需要查看更细粒度的锁的情况,可以通过sys.dm_tran_locks来查看。

 

更多内容将在下一文章讲述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值