首页 / MYSQL / MySQL事务与锁机制详解
MySQL事务与锁机制详解
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了MySQL事务与锁机制详解,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8525字,纯文字阅读大概需要13分钟。
内容图文
![MySQL事务与锁机制详解](/upload/InfoBanner/zyjiaocheng/875/d775561f4b38448c9ba8dbda35477947.jpg)
1.事务
1.1 什么是事务?
事务是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
1.2 哪些引擎支持事务
InnoDB支持事务
1.3 事务的四大特性
原子性(Atomicity):强调事务的不可分割,也就意味着我们对数据库的一系列操作,要么都是成功,要么都是失败。在InnoDB里面,是通过undo log来实现的,它记录了数据修改之前的值(逻辑日志),一旦发生异常,就可以用undo log来实现回滚操作。
一致性(Consistency):事务执行的前后数据的完整性保持一致。
隔离性(Isolation):一个事务的执行过程中,不应该受到其他事务的干扰。对隔离性的定义,就是多个事务对表或者行的并发操作,是互不干扰的。通过这种方式,也是保证数据的一致性。
持久性(Durability):事务一旦提交成功,那么结果就是永久性的,不可能因为我们系统宕机或者重启了数据库的服务器,它又恢复到原来的状态了。持久性是通过redo log 来实现的,我们操作数据的时候,会先写到buffer pool里面,同时记录redo log,如果在刷盘之前出现异常,在重启后就可以读取redo log 的内容,写入到磁盘,保证数据的持久性。实际上还有一个双写缓冲(double write buffer)机制,因为存储引擎和操作系统的页的大小不一致,一个存储引擎page的数据要写4次,如果中间发生异常,造程页数据的不可用,所以,必须把数据备份起来,这个就是双写缓冲。
原子性,隔离性,持久性,最后都是为了实现一致性。
1.4 MySQL中事务的开启
1)自动开启:任意一个增删改的语句,都会开启一个事务,并且是自动提交的。InnoDB里面有一个autocommit的参数(分成两个级别,session级别和global级别)
show variables like 'autocommit';
2)手动开启:通过begin 或者 start transaction 开启一个事务,并且通过commit 或者 rollback 语句提交事务
begin;
update customer set cust_name = '马大炮' where cust_id = 14;
commit;
1. 5 如果不考虑隔离性(事务并发)会带来的读问题:
脏读: 一个事务读到另一个事务未提交的数据。
不可重复读: 同样的SQL语句,得到的查询结果不一样,是因为一个事务读到另一个事务已经提交的update数据。
幻读: 同样的SQL语句,得到的查询结果不一样,是因为一个事务读到另一个事务已提交的insert的数据。
脏读和不可重复读的区别是事务是否提交,不可重复读和幻读的区别是,前者是update、delete,后者是insert。
1.6解决读问题:设置事务的隔离级别
未提交读(Read Uncommitted ):,一个事务可以读取到其他事务未提交的数据,会出现脏读,所以叫做RU,它没有解决任何的问题。
已提交读(Read Commit): 一个事务只能读取到其他事务已提交的数据,不能读取到其他事务未提交的数据,它解决了脏读的问题,但是会出现不可重复读和幻读的问题。
可重复读(Repeatable Read ): 解决了不可重复读的问题,也就是在同一个事务里面多次读取同样的数据结果是一样的,但是在这个级别下,没有定义解决幻读的问题。
串行化(Serializable): 这个隔离级别里面,所有的事务都是串行执行的,也就是对数据的操作需要排队,已经不存在事务的并发操作了,所以它解决了所有的问题。
事务隔离级别,是数据库专家联合制定的一个标准,由数据库厂商按照这个标准来实现事务隔离,这个标准是SQL 92标准(SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。)
P1、P2、P3分别为脏读、不可重复读、幻读。
1.7 MySQL InnoDB对隔离级别的支持
InnoDB实现了上面的四种标准,并且都解决了相应的问题,唯一不同的是InnoDB不需要用Serializable这么高的隔离级别就能解决幻读,InnoDB默认使用的是Repeatable Read的隔离级别
1.8 MySQL实现事务隔离的两大方案
1)LBCC
第一种,读取数据的时候,锁定我们要操作的数据,不允许其他事务修改就行了。这种方案我们叫做基于锁的并发控制Lock Based Concurrency Control(LBCC)。
2)MVCC
在修改数据的时候给它建立一个备份或者叫快照,后面再来读取这个快照就行了。这种方案我们叫做多版本的并发控制Multi Version Concurrency Control(MVCC)。
MVCC的核心思想是:可以查到在这个事务之前已经存在的已提交的数据,即使它在后面被修改或者删除了,和之前查到的数据还是一样,不会改变,在我这个事务之后新增的数据,我是查不到的。可以看一下这个简化的模型来理解MVCC简化模型
在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:
快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外)
select * from table where ?;
当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
下面语句都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
2 MySQL InnoDB锁的基本类型
2.1 共享锁(Shared Locks)
又称为读锁,简称S锁。共享锁就是多个事务对于同一个数据可以共享一把锁,都能访问到数据,但是只能读不能进行修改
加锁方式:
begin; select cust_name from customer where cust_id = 14 lock in share mode;
释放锁:事务结束,包括提交事务和结束事务。
2.2 排他锁(Exclusive Locks)
又称为写锁,简称X锁。排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能在获取改行的锁(排他锁、共享锁),只有该获取了排他锁的事务才可以对数据行进行读取和修改。
加锁方式
自动加锁:delete / update / insert 默认加上X锁;
手动加锁:select cust_name from customer where cust_id = 14 for update;
释放锁:事务结束,包括提交事务和结束事务。
用SELECT …LOCK IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT… FOR UPDATE方式获得排他锁。
2.3 意向共享锁(IS)/ 意向排他锁(IX)
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,相当于一个标志。
意向共享锁(Intention Shared Lock,简称IS锁)
表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁 前必须先取得该表的IS锁。
意向排他锁(Intention Exclusive Lock,简称IX锁)
表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他 锁前必须先取得该表的IX锁。
2.4 行锁的原理
行锁,是通过给索引上的索引项加锁来实现的,如果数据库中没有索引,锁的是聚簇索引,InnoDB中,不可能没有聚簇索引,会将整个表都锁住,因为查询不会使用索引,就会进行全表扫描,然后会把每一个隐藏的聚簇索引都锁住了。
另外,给唯一索引加锁,主键索引项也会被锁住,在辅助索引里面, 索引存储的是二级索引和主键的值。 比如name=4,存储的是name的索引和主键id的值4。而主键索引里面除了索引之外,还存储了完整的数据。所以我们通过辅助索引锁定一行数据的时候,它跟我们检索数据的步骤是一样的,会通过主键值找到主键索引,然后也锁定。
2.5 锁的算法
在理解记录锁、间隙锁、临键锁之前,要先明白这几个概念。
有一张表t2,里面有四条数据
这些数据库里面存在的主键值,我们把它叫做Record,记录,那么这里我们就有4个Record。
根据主键,这些存在的Record隔开的数据不存在的区间,我们把它叫做 Gap,间隙,它是一个左开右开的区间。
间隙(Gap)连同它左边的记录(Record),我们把它叫做临键的区间,它是一个左开右闭的区间。
2.6 记录锁(Record Lock)
当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。
-- id为主键或者唯一索引
select * from t2 where id = 1;
2.7 间隙锁(Gap Lock)
当我们查询的记录不存在,没有命中任何一个record,无论是用等值查询还是范围查询的时候,它们使用的都是间隙锁,锁住的是间隙。
举个例子,where id >4 and id <7,where id = 6。
注意,间隙锁主要是阻塞插入insert。相同的间隙锁之间不冲突。
Gap Lock 只在 RR 中存在,如果要关闭间隙锁,就是把事务隔离级别设置成RC,并且把innodb_locks_unsafe_for_binlog设置为ON。
这种情况下除了外键约束和唯一性检查会加间隙锁,其他情况都不会用间隙锁。
2.8 临键锁(Next-Key Lock)
当我们使用了范围查询,不仅仅命中了Record记录,还包含了Gap间隙,在这种情况下我们使用的就是临键锁,它是MySQL里面默认的行锁算法,相当于记录锁加上间隙锁。
比如我们使用>5 <9, 它包含了记录不存在的区间,也包含了一个Record 7。
临键锁,锁住最后一个Key的下一个左开右闭的区间。
select*fromt2 where id>5 and id<=7 for update; -- 锁住(4,7]和(7,10]
select*fromt2 where id>8 and id<=10 for update; -- 锁住 (7,10],(10,+∞)
就是因为InnoDB中使用了Next-Key Lock,所以幻读才不会发生。
3.小结:InnoDB隔离级别的实现
3.1 Read Uncommit
RU隔离级别:不加锁。
3.2 Serializable
Serializable 所有的 select 语句都会被隐式的转化为select …Lock in share mode,会和update、delete互斥。
3.3 Repeatable Read
RR隔离级别下,普通的select使用快照读(snapshot read),底层使用MVCC来实现。
加锁的select(select … lock in share mode / select … for update)以及更新操作update、delete等语句使用当前读,底层使用记录锁或者间隙锁或者是临键锁。
3.4 Read Committed
RC隔离级别下,普通的select都是快照读,使用MVCC实现。
加锁的select都是用记录锁,因为没有Gap Lock。除了两种特殊情况——外键约束检查(foreign-key constraint checking)以及重复键检查(duplicate-key checking)时会使用间隙锁封锁区间。所以RC会出现幻读的问题。
如有不足之处,欢迎指正,谢谢!
内容总结
以上是互联网集市为您收集整理的MySQL事务与锁机制详解全部内容,希望文章能够帮你解决MySQL事务与锁机制详解所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。