数据库中的行锁和表锁

1、MyISAM表锁

MyISAM存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。随着应用对事务完整性和并发性要求的不断提高,MySQL才开始开发基于事务的存储引擎,后来慢慢出现了支持页锁的BDB存储引擎和支持行锁的InnoDB存储引擎(实际 InnoDB是单独的一个公司,现在已经被Oracle公司收购)。但是MyISAM的表锁依然是使用最为广泛的锁类型。

1.1、MySql表级的锁模式

MySql的表级锁有两种模式:表共享读锁(Table Read Lock)表独占写锁(Table Write Lock)

锁模式的兼容性

请求锁模式 是否兼容当前锁模式None读锁写锁
读锁
写锁
  • 对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求
  • 对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作
  • MyISAM表的读操作与写操作之间,以及写操作之间是串行的!

1.2、如何加表锁

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁;

在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁;

这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。

  • Lock TABLES给表显示加表锁时,必须同时取得所有涉及到的表的锁,并且MYSQL不支持锁升级
  • 在执行Lock TABLES后,只能访问显示加锁的表,不能访问未加锁的表
  • 如果是读锁,只能执行查询操作,而不能执行更新操作
  • 在自动加锁的情况下,MYISAM总是一次性获得SQL语句所需要全部锁
  • 当使用Lock Table时,不仅需要一次锁定用到的表,而且,同一个表在SQL语句中出现多少次,就要通过SQL语句中相同的别名锁定多少次

1.3、并发插入

上文提到过MyISAM表的读和写是串行的,但这是就总体而言的。在一定条件下,MyISAM表也支持查询和插入操作的并发进行。

MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。

  • 当concurrent_insert设置为0时,不允许并发插入。
  • 当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。
  • 当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。

1.4、MyISAM的锁调度

前面讲过,MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的。那么,一个进程请求某个 MyISAM表的读锁,同时另一个进程也请求同一表的写锁,MySQL如何处理呢?

答案是:写进程先获得锁

不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前!

**这是因为MySQL认为写请求一般比读请求要重要。**这也正是MyISAM表不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。这种情况有时可能会变得非常糟糕!

幸好我们可以通过一些设置来调节MyISAM 的调度行为。

2、InnoDB的行锁模式及加锁模式

2.1、InnoDB中的锁

InnoDB实现了以下两种类型的行锁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Y9Qa2Kz-1581743638787)(images/02.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3UrQW8iP-1581743638788)(images/03.png)]

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

2.2、锁的使用

意向锁是InnoDB自动加的,不需用户干预; 对于UPDATEDELETEINSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE

SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT… FOR UPDATE方式获得排他锁。

2.3、InnoDB行锁实现方式

InnoDB行锁是通过索引上的索引项来枷锁实现的

在实际应用中,需要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能

  • 在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁
  • 由于MySQL的行锁是针对索引加的锁不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的
  • 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引

2.4、间隙锁(Next-Key锁)

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

/*举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:*/
Select * from  emp where empid > 100 for update;
/*是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。*/


InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另外一方面,是为了满足其恢复和复制的需要

很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

3、事务

3.1、事务及其ACID属性

事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性

ACID属性

  • 原子性(Atomicitity):事务是由一个原子操作单元,其对数据的修改,要么全执行,要么全部执行
  • 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须因应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引和双向链表)也都必须是正确的。
  • 隔离型(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的独立环境执行。这意味着事务处理过程中的中间状态对外部是不可见的
  • 持久性(Durable):事务完成之后,它对于数据的修改时永久性的,即使出现系统故障也能够保持。

3.2、并发事务处理带来的问题

相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持更多的用户。但并发事务处理也会带来一些问题,主要包括以下几种情况。

  • 更新丢失当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新

  • 脏读一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做"脏读"

  • 不可重复读:**一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。 **

  • 幻读一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”

    note: 不可重复读和幻读本质上是一样的,就是一个事务内两次读取同样的数据,结果不一样;这就是统一的欢度问题;行级锁可以解决更新删除导致的幻读间隙锁可以解决插入导致的幻读

3.3、事务的隔离级别

为了解决上述的问题:

在上面讲到的并发事务处理带来的问题中,==“更新丢失”==通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题必须由数据库提供一定的事务隔离机制来解决

数据库实现事务隔离的方式,基本上可分为以下两种。

  • 一种是在读取数据前,对其加锁,阻止其他事务对数据进行修改。
  • 另一种是不用加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取。从用户的角度来看,好像是数据库可以提供同一数据的多个版本,因此,这种技术叫做数据多版本并发控制(MultiVersion Concurrency Control,简称MVCC或MCC),也经常称为多版本数据库。

为了解决“隔离”与“并发”的矛盾,ISO/ANSI SQL92定义了4个事务隔离级别,每个级别的隔离程度不同,允许出现的副作用也不同,应用可以根据自己的业务逻辑要求,通过选择不同的隔离级别来平衡 “隔离”与“并发”的矛盾。下表很好地概括了这4个隔离级别的特性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8PpV9p9-1581743638789)(images/01.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值