数据库篇(五)——数据库的锁

一、数据库的锁机制及数据库中有哪些锁?

锁是一种并发控制技术,锁是用来在多个用户同时访问同一个数据的时候保护数据的。

两种基本的锁类型

  • 共享锁(S):多个事务科封锁一个共享页,任何事务都不能修改该页;通常是该页被读取完毕,S锁立即被释放。
  • 排它锁(X):仅允许一个事务封锁该页,其他事务必须等X锁释放后才能对该页进行访问。

二、死锁

产生死锁的四个必要条件:

  • 互斥条件:一个资源只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源阻塞是,对已获得的资源保持不放
  • 不可剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  • 环路等待条件:若干进程形成一种循环等待资源的关系
    只要系统发生了死锁,这些条件必须满足,只要一个条件不满足,就不会发生死锁。

预防死锁:

预防死锁只需要破坏四个必要条件之一即可。

避免死锁:

银行家算法

检测死锁:

死锁定理

解除死锁:

  • 从死锁进程处剥夺资源
  • 终止部分或全部进程

三、MySQL锁的粒度(锁的级别)

MySQL各存储引擎使用了三种级别的锁定机制:行级锁定、页级锁定和表级锁定。

1、表级锁:

直接锁定整张表。锁定时,其他进程不能对该表进行写操作。如果是写锁,其他进程则读也不允许。

特点:

  • 开销小,加锁快;
  • 不会出现死锁;
  • 锁定粒度最大,发生锁冲突的概率最高,并发度最低。
    MyISAM存储引擎采用的是表级锁

有两种模式:

表共享读锁(lock table 表名 read;)和表独占写锁。去掉锁的命令(unlock tables)

支持并发插入:支持查询和插入操作并发进行(在表尾并发插入)

锁调度机制:

写锁优先。一个进程请求某个MyISAM表的读锁,同时有一个进程也请求这个表的写锁,MySQL是让写锁优先获得锁。

2、行级锁:

仅对指定的记录进行加锁,其他进程还可以对同一个表中的其他记录进行操作。

特点:

  • 开销大,加锁慢;
  • 会出现死锁;
  • 锁定粒度最小,发生锁冲突的概率最低,并发度最高
    InnoDB存储引擎即支持行级锁,也支持表级锁,默认情况下是采用行级锁。

3、页级锁

一次锁定相邻的一组记录。

特点:

  • 开销和加锁时间介于表锁与行锁之间;
  • 会出现死锁;
  • 锁粒度介于表锁与行锁之间,并发度一般。

总结

最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁随并发访问的影响体现在所的粒度上。比如:(表锁)放在一个表上的锁限制对整个表的并发访问;(页锁)在数据页的锁限制了对整个数据页的访问;(行锁)只限制对该行的并发访问。

四、数据库的乐观锁和悲观锁是什么?

1、悲观锁:

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
以MySQL的锁表过程为例:

要将select.... for update;放在MySQL事务中,即begincommit中,否则不起作用

表结构为:
这里写图片描述
表中内容如下:
这里写图片描述
开启两个窗口,其中一个执行select * from ta for update;
在两一个窗口中执行update操作:
这里写图片描述
窗口1
这里写图片描述
窗口2
在窗口1中执行commit后,窗口2中的update才执行。

特点:

悲观锁可能会造成加锁的时间很长,并发性不好,特别是长事务,影响系统的整体性能。

悲观锁的实现方式:

基于数据库的锁机制实现。传统的关系型数据库里用到了这种锁机制,比如:行锁、表锁等,读锁、写锁等,都是在做操作之前先上锁

2、乐观锁:

假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

特点:

比悲观锁加锁的时间短,大大提升了大并发量下的系统整体性能表现。

乐观锁的实现方式:

1)大多是基于数据版本记录机制实现

需要为每一行数据增加一个版本标识(每一行增加一个字段version),每次更新数据都要更新对应的版本号+1;
也就是说:提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据,不得不重新读取对象并作出更改。
示例:

假设数据库中账户信息表中有一个version字段,当前值为1,而当前账户余额字段(balance)为$100。有以下操作:
1、操作员A读出(version=1),并将balance扣除$50($100-$50);
2、在A操作的过程中,操作员B也读入用户信息(version=1),并扣除$20($100-$20);
3、A完成了修改操作,将version+1(2),balance为$50提交,由于提交版本大于数据记录当前版本,数据被更新,此时version=2,balance=$50;
4、B完成操作哦,也将版本号加1(为2)试图想数据库提交数据(balance=$80),但此时比对数据库记录版本时发现,提交版本号不大于数据库记录版本号,因此,B的提交被驳回。

以上示例中发现,乐观锁机制避免了长事务中的数据库加锁开销(A和B的操作中都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。

2)使用时间戳来实现

同样是在需要乐观锁控制的表中增加一个字段,字段类型是时间戳(timestamp),和上面的version类似,也是在提交更新时检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,若一致,提交成功;否则就是版本冲突。

3、悲观锁和乐观锁的适用场景

如果并发量不大,可以使用悲观锁解决并发问题;
若系统的并发量特别大的话,选择乐观锁;
现在大部分应用都是选择乐观锁。

参考http://www.open-open.com/lib/view/open1452046967245.html

猜你喜欢

转载自blog.csdn.net/u010843421/article/details/81664535