锁分类
乐观锁
乐观锁一般是指用户可以自己实现的一种锁,假设认为数据不会造成冲突,所以在数据更新提交的时候,才正式对数据的冲突与否进行检测,如果发生冲突,则返回错误信息,让用户决定如何去做。乐观锁的实现方式一般包括版本号和时间戳。
悲观锁
利用数据库的锁机制实现。他可以组织一个用户以影响其他用户的方式来修改数据。如果一个事务之行的操作数据都应用了锁。那只有当这个事务把锁释放了,其他事务才能执行与该锁冲突的操作。悲观锁主要应用在数据竞争激烈的环境,以及发生并发冲突时使用锁保护数据的成本低于事务会滚的成本环境中。
意向锁
行锁与表锁的写锁是互斥的,两者之间的读锁与写锁也是互斥的,如果事务A申请了行级读锁或写锁,事务B想要申请表写锁,mysql 如何知道已经存在事务申请了行锁,不能让事务B获得表锁呢,如果通过逐行遍历显然消耗巨大。于是就有了意向锁,事务A想申请行锁需要先申请表的意向锁,申请成功后再申请行锁。事务B在申请表锁时数据库通过判断意向锁是否存在以及锁类型(共享意向锁,意向写锁)对事务B申请的锁进行处理。
行级锁分类
共享锁
当一个事务执行select 语句时,数据库会为自动为事务分配一把共享锁,用来锁住被查询的数据。当数据被读取后,数据库会立即释放共享锁。当资源被放置共享锁后,还能再放置共享锁或更新锁,并发性好。
排他锁
当一个事务执行insert,update,delete语句时,数据库会自动对sql操作的数据资源使用度独占锁。如果该数据资源已有其他锁时,就无法对其再放置独占锁。如果数据资源已经放了独占锁,也不能对其再放置任何锁。并发性不好。
更新锁
更新锁在初始化阶段用来锁住可能要被修改的资源,这可以避免共享锁造成的死锁。
执行语句 update account set balance=900 where id=1;
更新操作需要分为两步。读取account表中id为1的数据 -> 执行更新操作。
如果在第一步使用共享锁,第二步使用排他锁,就容易造成两个事务都获取共享锁后都升级锁为排他锁,但又需要等他另一个事务释放共享锁,造成死锁。
当一个事务执行update操作时,数据库会为事务分配一把更新锁。当事务读取完数据后把更新锁升级为独占锁,执行更新操作。更新锁与共享锁是兼容的,也就是说一个资源可以同时放置共享锁与更新锁。但只能放置一个更新锁。这样,当多个事务同时更新相同的数据时,只有一个事务会获取更新锁,然后升级为独占锁。其他食物必须等待前一个事务结束时才能获得更新锁。这样就避免了死锁。
更新锁允许多个事务读取锁定的资源,但不允许其他事务修改他。
mysql行锁是通过锁索引实现的,因此只有通过索引更新的数据才会使用行锁,否则都是使用表锁。oracle行锁是通过锁数据块实现的,对行的更新使用的是行锁,不需要通过索引更新。
数据库的隔离级别
数据库的每一种隔离级别满足了不同的数据要求。使用了不同程度的锁。
读未提交
写加写锁,读不使用锁,如果读取其他事务未提交的数据,如果该事务最后回滚了造成当前事务脏读。
读已提交
写数据时添加写锁,事务提交后释放。读数据时加读锁,读完后释放读锁。这样可以避免脏读,但在一个读事务过程中如果读两次数据中间有其他修改数据的事务,就会造成两次读的数据不一致。造成不可重复读。
可重复读
事务在读取数据时添加读锁,写数据时添加写锁,直到事务结束。添加读锁能保证整个事务过程中数据不会被其他事务修改,即可重复读。写数据时添加写锁直到事务提交。保证其他事务不出现脏读。
可串行化
前三种事务隔离性都是基于行锁实现。如果在事务过程中发生数据的插入就造成了幻读。可串行化是数据库事务最高隔离级别。他强制事务排序,互相不冲突。这个隔离级别可能导致大量的超时现象和锁竞争。
三级封锁协议
一级封锁协议
事务T在修改事务R之前必须加X锁,直到事务结束。事务正常结束包括事务提交与事务回滚。一级封锁协议可以防止丢失更改,并保证事务T是可恢复的。在一级封锁协议中如果仅仅读取数据不对其进行修改,是不需要加锁的。所以他不能保证可重复读和不读“脏”数据。
二级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。二级封锁协议防止丢失修改,还可进一步防止读“脏”数据。
三级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。三级封锁协议可以防止修饰修改与读“脏”数据以外,还可进一步防止不可重复读。