mysql事务隔离级别和锁机制_mysql事务的4种隔离级别及InnoDB锁机制

1.前言

本文是一篇个人总结性文章,参考了各位大佬的博客,并根据自己学习路线总结得到。

数据库中事务的四大特性:原子性、隔离性、持久性、一致性;那么在mysql中是如何实现这四大特性的?

1.1 mysql如何保证原子性?

原子性:事务所有操作要么全部提交成功,要么全部失败回滚。

实现:InnoDB中是通过undo log(回滚日志)实现。当事务回滚时能撤销所有已经成功执行的SQL语句,他需要记录你要回滚的相应日志信息。

(1)当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据

(2)当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作

(3)当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作

undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

1.2 mysql如何保证持久性?

持久性:一旦事务提交,其所做的修改将永远保存到数据库中,即使发生崩溃,事务执行的结果也不能丢失。

实现:InnoDB中是通过redo log

问题:Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。

如何解决:采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据。

采用redo log的好处:redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。

redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。

1.3 mysql如何保证隔离性?

隔离性:一个事务所做的修改在最终提交之前 对其他事务是不可见的

问题:在并发环境下,事务的隔离性很难保证,会出现很多并发一致性问题。

解决:对于并发一致性问题解决办法一般有:封锁和数据库管理系统提供的事务隔离级别以及多版本并发控制

封锁:行级锁和表级锁

事务隔离级别:见下文

多版本并发控制MVCC:见下文

1.4 mysql如何保证一致性?

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。

但是,如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。例如,你在转账的例子中,你的代码里故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。

从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!

2.四种隔离级别

低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

参考:www.iteye.com/blog/xm-kin…

2.1 Read uncommitted(读取未提交内容)

(1)所有事务都可以看到其他未提交事务的执行结果

(2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少

(3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据

e674d09185b05bd7a4cc84c999df90a3.png

6e0e5348cdf8f639a2b7d28931d7fdef.png

2.2 Read commited(读取提交内容RC)

针对当前读,RC隔离级别保证了对读取到的记录加锁(记录锁),存在幻读现象。

(1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)

(2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变

(3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。

|——>导致这种情况的原因可能有:(1)有一个交叉的事务有新的commit,导致了数据的改变;(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit

复制代码

53c0422b34f7d449543528936a11b2dc.png

1f528ce65d893c953f742ca07c3b553c.png

b51f4f26b3d06162179443441305243f.png

2.3 Repeatable Read(可重读RR)

InnoDB存储引擎默认的隔离级别是可重复读,即RR。针对当前读,RR隔离级别保证对读取到的记录加锁(记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入(间隙锁),不存在幻读现象。

(1)这是MySQL的默认事务隔离级别

(2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行

(3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行

(4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题

9c817c7df619bae6f4fe7487265406b4.png

9fcfea31e184564c6914c76bc89e5fa1.png

2.3.1 重要:RR如何解决幻读现象

通过MVCC+Next key lock:

很明显可重复读的隔离级别没有办法彻底的解决幻读的问题,如果需要解决幻读的话也有两个办法:

使用串行化读的隔离级别

MVCC+next-key locks:next-key locks由record locks(索引加锁) 和 gap locks(间隙锁,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据)

2.4 Serializable(可串行化)

从MVCC并发控制退化为基于锁的并发控制。不区别快照读和当前读,所有的读操作都是当前读,读加读锁(S锁),写加写锁(X锁)。在该隔离级别下,读写冲突,因此并发性能急剧下降,在MySQL/InnoDB中不建议使用。

(1)这是最高的隔离级别

(2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。

(3)在这个级别,可能导致大量的超时现象和锁竞争

995f375cec884a495d245d0e3c52e0cb.png

2.5 小结

X表示此隔离级别已解决的情况72863872c5d3e80f0842a90e58317fd4.png

3.分布式事务

4.脏读

见RC

5.phantom problem幻读

幻读指的是在同一事务下,连续执行两次同样的SQL语句可能会导致不同的结果,第二次的SQL可能会返回之前不存在的行。

5.1 如何解决幻读问题

MVCC+Next-key locks

什么是MVCC

读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的提高了系统的并发性能,在现阶段,几乎所有的RDBMS,都支持MVCC。其实,MVCC就一句话总结:同一份数据临时保存多个版本的一种方式,进而实现并发控制;

多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。

Mysql的大多数事务型存储引擎实现都不是简单的行级锁。基于提升并发性考虑,一般都同时实现了多版本并发控制(MVCC),包括Oracle、PostgreSQL。不过实现各不相同。

MVCC的实现是通过保存数据在某一个时间点快照来实现的。也就是说不管实现时间多长,每个事物看到的数据都是一致的。

分为乐观(optimistic)并发控制和悲观(pressimistic)并发控制

e7dadc69adb639eb983093ec4c676b37.png

eb430dd77b2e5c5b8d1c6cc1765a7484.png

e5347f4dc9c467446f0085303f1b8ad1.png

6.几个特殊的索引

6.1 辅助索引

6.2 聚集索引

也叫做聚簇索引。在InnoDB中,数据的组织方式就是聚簇索引:完整的记录,储存在主键索引中,通过主键索引,就可以获取记录中所有的列。

6.3 唯一属性(主键索引,唯一索引)

主键索引:

主键索引不可以为空

主键索引可以做外键

一张表中只能有一个主键索引

普通索引:

用来加速数据访问速度而建立的索引。多建立在经常出现在查询条件的字段和经常用于排序的字段。

被索引的数据列允许包含重复的值

唯一索引:

被索引的数据列不允许包含重复的值

6.4 复合索引

7.mysql锁机制

7.1 InnoDB存储引擎实现了两种标准的行级锁:

共享锁 S Lock ,允许事务读一行数据

排它锁 X Lock ,允许事务删除或更新一条数据

如果一个事务T1已经获得了r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为读取并没有改变r的数据,成这种情况为锁兼容(Lock Compatible)。但若有其他的事务T3箱获得行r的排它锁,则其必须等待T1、T2释放行r上的共享锁 —— 这种情况称为锁不兼容。

dd50a5f4e76f9fb4771e57f2d8129a8c.png

7.2 InnoDB有3种行锁的算法

1.记录锁 record lock 单个行记录上的锁,就是字面意思的行锁

0551b2cccb255c83e7c24dfed0ca09fa.png

2.间隙锁 Gap lock 锁定某一个范围内的索引,但不包括记录本身

3.next-key lock:gap lock+record lock 锁定一个范围,并且锁定记录本身

75be5fcf1db3a0c6083777a10026974f.png

7.3 InnoDB行锁的特点

1.当查询的索引含有唯一(unique)属性时(主键索引,唯一索引)InnoDB存储引擎会对Next-Key Lock优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。

例子:

beda569fdf211773f5cb077131fa9ca4.png

0e072b40d17cf3e2e6a911d66a8a6171.png

用户也可以通过以下两种方式关闭gap lock对辅助索引的下一个值的锁定。

1.配置参数 innodb_locks_unsafe_for_binlog = 1

2.隔离级别设为 READ COMMITTED

d7743f1616b9a6f9935f409937cbbac0.png

2.复合主键下,如果加锁时不带上所有主键,InnoDB会使用Next-Key Locking算法,如果带上所有主键,才会当作唯一索引处理,降级为Record Lock,只锁当前记录。否则依然为Next-key lock

d764bd23d4e698630c6b4da904f8dd87.png

f4026059566721ca1b819665a9d61840.png

3.当使用多列唯一索引时,加锁需要明确要锁定的行(即加锁时使用索引的所有列),InnoDB才会认为该条记录为唯一值,锁才会降级为Record Lock。否则会使用Next-Key Lock算法,锁住范围内的数据。

b485d14b2c8770f8d4fde99e0e96d124.png

c3ff426c44b716374b5f8bacfb8d9fb7.png

4.小结

当唯一索引是由多个列组成,而query仅查询多个列中的其中一个,则依然使用 Next-key lock。

强制关闭Gap Lock,仅使用记录锁从而避免阻塞(有幻读的风险):配置参数 innodb_locks_unsafe_for_binlog = 1 或者隔离级别设为 READ COMMITTED

在使用Mysql中的锁时要谨慎使用,尤其时更新/删除数据时,尽量使用主键更新,如果在复合主键表下更新时,一定通过所有主键去更新,避免锁范围变大带来的死锁等问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值