在一些面试和书籍上经常会提到各种数据库锁以及事务,在这里结合一些资料简要概括下:
数据库并发需使用事务来控制,事务并发问题需要数据库锁来控制,数据库锁是跟并发控制和事务联系在一起的。
事务:必须满足:ACID(原子性,一致性,隔离性,持久性)四特性,事务是恢复和并发控制的基本单位。
原子性:是事务是数据库的逻辑工作单位,事务中操作要么都做,要么都不做。
一致性:事务的执行结果必须是使数据库从一个一致性状态变大另一个一致性状态,一致性和原子性密切相关的。
隔离性:是一个事务执行不能被其他事务干扰。
持久性:是一个事务一旦提交,他对数据库中数据的改变就是永久性的。
共享锁:共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。
排它锁:与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。
对共享锁类型,需在执行的语句后面加上for update
写锁:排他锁又称写锁。
读锁:共享锁又称读锁。
悲观锁:悲观锁是由数据库自己实现的,要用的时,直接调用数据库的相关语句即可。大致理解为觉得在读数据库的时候,
别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度。
乐观锁:不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导
致冲突。在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
行级锁:行锁,字面意思上理解,即是给某一条记录加锁。这是一种排他锁,防止其他事务修改此行。
行级锁分为共享锁和排它锁。
在使用以下语句时, Oracle会自动应用行级锁:
INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];
SELECT … FOR UPDATE语句允许用户一次锁定多条记录进行更新
使用COMMIT或ROLLBACK语句释放锁
表级锁:分为5类:
行共享 (ROW SHARE) – 禁止排他锁定表
行排他(ROW EXCLUSIVE) – 禁止使用排他锁和共享锁
共享锁(SHARE) - 锁定表,对记录只读不写,多个用户可以同时在同一个表上应用此锁
共享行排他(SHARE ROW EXCLUSIVE) – 比共享锁更多的限制,禁止使用共享锁及更高的锁
排他(EXCLUSIVE) – 限制最强的表锁,仅允许其他用户查询该表的行。禁止修改和锁定表。
页级锁:BDB支持页级锁。页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突
少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
补充:
共享锁和排它锁是具体的锁,是数据库机制上的锁,存在以下关系:
(x表示是排它锁(Exclusive),s表示共享锁(Share),Y表示yes,N表示no)
上图表示可以共存的锁,如,第二行表示,一个事务T1给某数据加了X锁,则事务T2就不能再给那数据加X锁了,同时也不能再加S锁了,只有到T1事务提交完成之后,才可以。默认来说,当sql脚本修改更新某条记录的时候,会给该条记录加X锁,读的话加的是S锁。
还有就是,并发控制会造成活锁和死锁,就像操作系统那样,会因为互相等待而导致。活锁指的是T1封锁了数据R,T2同时也请求封锁数据R,T3也请求封锁数据R,当T1释放了锁之后,T3会锁住R,T4也请求封锁R,则T2就会一直等待下去,这种处理方法就是采用“先来先服务”策略;死锁就是我等你,你又等我,双方就会一直等待下去,比如:T1封锁了数据R1,正请求对R2封锁,而T2封住了R2,正请求封锁R1,这样就会导致死锁,死锁这种没有完全解决的方法,只能尽量预防,避免,检查,解除死锁。
承上总结死锁原因:
死锁指两个事务或者多个事务在同一资源上相互占用,并请求对方所占用的资源,从而造成恶性循环的现象。
出现死锁的原因:
1. 系统资源不足
2. 进程运行推进的顺序不当
3. 资源分配不当
产生死锁的四个必要条件
1. 互斥条件: 一个资源只能被一个进程使用
2. 请求和保持条件:进行获得一定资源,又对其他资源发起了请求,但是其他资源被其他线程占用,请求阻塞,
但是也不会释放自己占用的资源。
3. 不可剥夺条件:指进程所获得的资源,不可能被其他进程剥夺,只能自己释放
4. 环路等待条件: 进程发生死锁,必然存在着进程-资源之间的环形链
数据库也会发生死锁的现象,数据库系统实现了各种死锁检测和死锁超时机制来解除死锁,锁监视器进行死锁检测,
是这样会扩大了封锁的范围,降低系统的并发度。
②顺序封锁发,指的是事先对数据对象指定一个封锁顺序,要对数据进行封锁,只能按照规定的顺序来封锁,但是这个一般不
大可能的。
另外,系统如何判断出现死锁呢,毕竟出现死锁不能一直干等下去,要及时发现死锁同时尽快解决出现的死锁。
诊断和判断死锁有两种方法,一是超时法,二是等待图法。
超时法就是如果某个事物的等待时间超过指定时限,则判定为出现死锁;
等待图法指的是如果事务等待图中出现了回路,则判断出现了死锁。
对于解决死锁的方法,只能是撤销一个处理死锁代价最小的事务,释放此事务持有的所有锁,同时对撤销的事务所执行的数据
修改操作必须加以恢复。