数据库锁

锁是一种并发控制技术,锁是用来在多个用户同时访问同一个数据的时候 保护数据的。
1.锁类型
共享(S)锁多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被 读取完毕,S 锁立即被释放。在执行 select 语句的时候需要给操作对象(表或者一些记录) 加上共享锁,但加锁之前需要检查是否有排他锁,如果没有,则可以加共享锁(一个对象 上可以加 n 个共享锁),否则不行。共享锁通常在执行完 select 语句之后被释放,当然也 有可能是在事务结束(包括正常结束和异常结束)的时候被释放,主要取决与数据库所设 置的事务隔离级别。
排它(X)锁仅允许一个事务封锁此页;其他任何事务必须等到 X 锁被释放才能对该 页进行访问;X 锁一直到事务结束才能被释放。执行 insert、update、delete 语句的时候需 要给操作的对象加排他锁,在加排他锁之前必须确认该对象上没有其他任何锁,一旦加上 排他锁之后,就不能再给这个对象加其他任何锁。排他锁的释放通常是在事务结束的时候 (当然也有例外,就是在数据库事务隔离级别被设置成 Read Uncommitted(读未提交数 据)的时候,这种情况下排他锁会在执行完更新操作之后就释放,而不是在事务结束的时 候)

2.死锁
产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不可剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

2.1预防死锁
预防死锁的发生只需破坏死锁产生的四个必要条件之一即可
1) 破坏互斥条件 如果允许系统资源都能共享使用,则系统不会进入死锁状态。但有些资源根本不能同时 访问,如打印机等临界资源只能互斥使用。所以,破坏互斥条件而预防死锁的方法不太可 行,而且在有的场合应该保护这种互斥性。
2) 破坏不剥夺条件 当一个已保持了某些不可剥夺资源的进程,请求新的资源而得不到满足时,它必须释放 已经保持的所有资源,待以后需要时再重新申请。这意味着,一个进程已占有的资源会被 暂时释放,或者说是被剥夺了,或从而破坏了不可剥夺条件。 该策略实现起来比较复杂,释放已获得的资源可能造成前一阶段工作的失效,反复地 申请和释放资源会增加系统开销,降低系统吞吐量。这种方法常用于状态易于保存和恢复 的资源,如 CPU 的寄存器及内存资源,一般不能用于打印机之类的资源。
3) 破坏请求和保持条件 釆用预先静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资 源未满足前,不把它投入运行。一旦投入运行后,这些资源就一直归它所有,也不再提出 其他资源请求,这样就可以保证系统不会发生死锁。
4) 破坏环路等待条件 为了破坏循环等待条件,可釆用顺序资源分配法。首先给系统中的资源编号,规定每 个进程,必须按编号递增的顺序请求资源,同类资源一次申请完。也就是说,只要进程提 出申请分配资源 Ri,则该进程在以后的资源申请中,只能申请编号大于 Ri 的资源。 这种方法存在的问题是,编号必须相对稳定,这就限制了新类型设备的增加;尽管在 为资源编号时已考虑到大多数作业实际使用这些资源的顺序,但也经常会发生作业使甩资 源的顺序与系统规定顺序不同的情况,造成资源的浪费;此外,这种按规定次序申请资源 的方法,也必然会给用户的编程带来麻烦。

2.2.避免死锁
银行家算法。

2.3.检测死锁
死锁定理。

2.4.解除死锁
2.4.1 从死锁进程处剥夺资源
2.4.2 终止部分或全部进程

3.数据库锁的两种机制
3.1 悲观锁
1、排它锁,当事务在操作数据时把这部分数据进行锁定,直到操作完毕后再解锁,其他事务操作才可操作该部分数据。这将防止其他进程读取或修改表中的数据。
2、实现:大多数情况下依靠数据库的锁机制实现

3.2 乐观锁
1、如果有人在你之前更新了,你的更新应当是被拒绝的,可以让用户重新操作
2、实现:大多数基于数据版本(Version)记录机制实现
具体可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作。
并发量很高的话,建议使用悲观锁,否则的话就使用乐观锁

4.mysql 锁的粒度
MySQL 各存储引擎使用了三种类型(级别)的锁定机制:行级锁定,页级锁定和表 级锁定。
4.1.表级锁:直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许。特点:开销小,加锁快;不会出现死锁;锁定粒度最大,发生锁冲突的概率最高,并发度最低。

有 2 种模式:表共享读锁表独占写锁。加读锁的命令:lock table 表名 read;去掉锁的命令:unlock tables。

锁调度机制:写锁优先。一个进程请求某个 MyISAM 表的读锁,同时另一个进程也请求同一表的写锁,MySQL 如何处理呢?答案是写进程先获得锁

4.2.行级锁,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高
InnoDB 存储引擎既支持行级锁,也支持表级锁,但默认情况下是采用行级锁。

4.3.页级锁,一次锁定相邻的一组记录。开销和加锁时间界于表锁和行锁之间;会出现 死锁;锁定粒度界于表锁和行锁之间,并发度一般。 最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的 某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁 的粒度上。比如,(表锁)放在一个表上的锁限制对整个表的并发访问;(页锁) 放在数据页上的锁限制了对整个数据页的访问;(行锁)放在行上的锁只限制 对该行的并发访问

MySQL意向锁
①在MySQL中有表锁,
LOCK TABLE my_tabl_name READ; 用读锁锁表,会阻塞其他事务修改表数据。
LOCK TABLE my_table_name WRITe; 用写锁锁表,会阻塞其他事务读和写。
②Innodb引擎又支持行锁,行锁分为
共享锁(读锁),一个事务对一行的共享只读锁。
排它锁,一个事务对一行的排他读写锁。
③这两中类型的锁共存的问题
考虑这个例子:
事务A锁住了表中的一行,让这一行只能读,不能写。
之后,事务B申请整个表的写锁。
如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。
数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
数据库要怎么判断这个冲突呢?
step1:判断表是否已被其他事务用表锁锁表
step2:判断表中的每一行是否已被行锁锁住。
注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。
于是就有了意向锁。
在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。
在意向锁存在的情况下,上面的判断可以改成
step1:不变
step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
意向锁是为了提高封锁子系统的效率。
注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请

猜你喜欢

转载自blog.csdn.net/jcsyl_mshot/article/details/80087925