目录
(1)什么是锁?
确保可串行化的方法之一就是要求对数据项的访问以互斥的方式进行。封锁就是事务在对数据对象操作之前,先向系统发出请求,对其加锁,在锁释放之前其他事务不能更新此数据。
(2)乐观锁与悲观锁:
①乐观锁
1)定义
乐观锁(Optimistic Lock),就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少(读的期间发现数据被写了需要回滚,浪费时间)的应用场景,这样可以提高吞吐量。假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
2)乐观锁实现
使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。(MVCC与之类似,不过创建两个字段,一个是行的创建(修改)版本,一个是行的删除版本)。
使用时间戳(timestamp)。乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是过期数据。
②悲观锁
1)定义
悲观锁:总是担心别人会修改,所以在使用之前先加锁,这样别人想拿这个数据就会阻塞,直到拿到锁。
2)悲观锁级别
MySQL有三种锁的级别:页级、表级、行级。
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许。
- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。(innoDB默认行级锁)
- 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般,表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
3)悲观锁的类型
排他锁(X锁)
又叫写锁,若事务T对数据对象Q加上X锁,这事务T既可以读又可以写Q,其他事务不可再对Q加锁,直至该锁释放。
共享锁(S锁)
共享锁又叫读锁,若事务T对对象Q加上S锁,事务T可读但是不可写,其他事务只能在对Q加S锁,而不能加X锁,直到该锁释放。
3)两段锁协议
两段锁协议:(加锁一块加,解锁一块解)
每个事务分两个阶段加锁和解锁。增长阶段:事务可以获得锁但不能释放锁。缩减阶段:事务可以释放锁,但不能获得新锁。
遵守两段锁协议的事务,其执行结果一定是正确的,这些事务的所有并行调度策略都是可串行化的。
(3)死锁
死锁(innoDB会产生):简单来说就是互相等待。进程A等待进程B释放他的资源,B又等待A释放他的资源,这样就互相等待就形成死锁。
①死锁预防
1)一次封锁
一次封锁:事务一次性将要使用的数据全部加锁,否则就不能继续执行。
2)顺序封锁
顺序封锁:对数据对象规定一个封锁顺序,所有事物都按照这个顺序进行封锁(如封锁对象ABC,T3封锁BC发现还要封锁A,但A也等待T3解锁)。
3)抢占与事务回滚
事务回滚:当事务T1的锁被T2持有时,授予T2的锁可能通过事务回滚授予T1,通过时间戳判断事务是否回滚。
非抢占技术:老的事务等待新的事务释放锁,新的事务锁被占了会回滚。(不发生抢占),若新事务A数据项由于被老事务B持有,则新事务回滚,重启时,还是原来的时间戳,导致再度回滚。
抢占技术:老的事务抢占新的事务的锁,新的事务要等待老的解锁。
两个一个是老抢新,新等老,一个是老等新,新回滚。
②死锁解决
找到牺牲者,删除。