MySQL之事务

一、什么是事务?

事务是MySQL的一种机制,每开启一个事务,都可以往里放入一系列的SQL语句。

事务是单个逻辑工作单位执行的一系列操作,要么全执行,要么全不执行,每开一个事务,相当于给数据库拍了一张快照,这意味着在提交之前可以回滚,在事务中可以设置多个保存点,然后可以进行回滚到每个保存点

我们默认为每条sql开启事务,并且会在本条sql执行完毕后自动执行commit提交,直接用 SET 来改变 MySQL 的自动提交模式,手动开启的事务里默认不会自动提交。

结束事务可以使用commit和rollback,结束事务会释放事务中所有的锁

二、事务有哪些特性?

我们可以拿发起一笔转账作为例子来诠释事务的特性

(A) 原子性:事务内部的sql语句是一个不可分割的整体,这一系列的sql语句如果有一条运行失败,则整体都运行失败,比如转账行为(指转账方金额减少和接收方金额增加)只可能全执行成功或全执行失败,

(C)一致性:事务执行前后的状态保持一致,和原子性紧密相关,比如张三和李四都有500元的余额,他们余额的总数是1000元,然后开启了一个转账事务,张三给李四转了100元以后,他们各自的余额发生了变化,但他们的总金额仍然保持不变。

另外,在事务发生的前后,数据类型也应该保持一致。

(I)隔离性:多个事务并发运行,但彼此之间互不影响,比如转账时如果A和B同时各开一个事务给C转账,事务2的B先转账成功,但此时事务1中的A看不到B转账成功的信息,但A转账完,C能同时收到A和B的转账

(D) 持久性:⼀个事务被提交(commit)之后,它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。

验证事务的一致性

准备表:

create table employee(
id int primary key auto_increment,
name varchar(20) not null,
age int(3) unsigned not null default 20
);

insert into employee(name) values
('歪歪'),
('丫丫'),
('丁丁'),
('星星'),
('格格'),
('张野'),
;
update employee set age = 18 where id <=3;

实验:

事务一

start transaction;select * from employee where name = "ly"; -- 对应的age = 18

-- 在事务二commit之后,重新查询,发现对应的age 仍为18 select * from employee where name = "ly";

-- 虽然看到的age仍为18,但因为事务的一致性原则,其实此处的修改是在age=19的基础上进行的update employee set age=age+1 where name = "ly";  -- 查看到age变为20select * from employee where name = "ly";

commit;select * from employee where name = "ly"; -- age =20

三、MySQL开启事务的三种方式

1、隐式开启,隐式提交(默认)

默认情况下,你每敲一条SQL语句,都会开启一个事务。这条语句一运行完,会自动帮你commit提交,所以平时我们使用update语句修改数据库,都会自动提交。

2、显式开启,显示提交

使用代码 start transaction或者简写为begin。

这样你所写的事务就不会自动触发commit提交,你可以选择手动commit 提交或是rollback回滚。

注意点:无论是commit还是 rollback,该事务都会结束。

3、显示开启,隐式提交

设置参数

Set session autocommit =0

这样设置完,就不会自动提交
总结:MYSQL 默认为每条sql开启事务,并且会在本条sql执行完毕后自动执行commit提交,若想设置手动提交,有两种方式

方式一:直接用 SET 来改变 MySQL 的自动提交模式(下述设置均为会话级别的设置):
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
方式二: 手动开启的事务里默认不会自动提交
# 手动开启的事务里默认不会自动提交,所以我们可以将要执行的sql语句放在我们自己手动开启的事务里
start transaction;
update test.t1 set id=33 where name = "jack";
commit;

注意:这种方式在当你使用commit或者rollback后,事务就结束了,再次进入事务状态需要再次start transaction

四、读现象与事务隔离机制

1.数据库读现象

读现象在本质上来说,是数据库在高并发场景下,多个同时执行的事务带来的影响。

数据库主要有三大常见的读现象:

脏读:事务1和事务2并发执行,事务1改了数据,事务2读取了以后,但事务1进行了回滚,导致事务2读取的数据有误。

不可重复读:事务1读取了数据,事务2修改了数据并且提交了,接着事务1再次读取,发现两次的数据不相同

幻读:本质上说是不可重复读的一种现象,事务1更改或查询了数据,在极短时间内,事务2又插入了一条新的数据,导致事务1在接下来的查询中,就会发现有⼏列数据是它先前所没有的。

解决办法:要解决脏读,不可重复读和幻读的问题,我们就要引入几个概念:MVCC机制,事务隔离机制和数据库锁机制。

2.数据库事务隔离机制

事务具有原子性、一致性、隔离性、持久性四大特性,而隔离性顾名思义指的就是事务彼此之间隔离开,多个事务在同时处理一个数据时彼此之间互相不影响,如如果隔离的不够好就有可能会产生脏读、不可重复度、幻读等读现象,为此,隔离性总共分为四种级别

由低到高依次为Read uncommitted(未提交读) 、Read committed (提交读)、Repeatable read(可重复读) 、Serializable(串行化),这些级别分别依次解决了脏读,不可重复读和幻读的问题

而MySQL的存储引擎默认的隔离级别为Repratable read (可重复读),于是解决了脏读和不可重复读的问题,至于幻读问题,MySQL引入了Next-key lock的行级锁来解决

3.mvcc机制

MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) 。MVCC最大的好处是:读不加锁,读写不冲突。这个机制解决了数据的脏读问题。

MVCC的并发控制的系统中,读操作可分为两类:当前读和快照读。

快照读:简单的select操作,属于快照读,不加锁。

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

五、锁机制

1.什么是锁机制,为什么要用锁机制

我们可以通过一个很简单的比喻来理解事务的锁机制。比如同一个办公室的同事们,都想使用打印机打印文件,如果不加以控制,可能出现两个人同时打印不同的内容在一个文件里,就会引起内容混乱。于是,我们就引入了锁的概念,当有并发的多个事务同时操作同一份数据时,只有“抢到”了锁的事务,才能真正去操作数据,使得数据的安全性得到保证。

锁保证并发的多个事务同一时间只有一个能运行,会一定程度上降低程序的运行效率,但是能大大提升数据的安全性。

2.数据库的分类

1、按粒度分

数据库的锁按粒度分为行级锁,表级锁,页级锁

⾏级锁 ⾏级锁是Mysql中锁定粒度最细的⼀种锁,表示只针对当前操作的⾏进⾏加锁。⾏级锁能⼤⼤减少数据库操作的冲突。其加锁粒度最⼩,但加锁的开销也最⼤。⾏级锁分为共享锁和排他锁。

特点:开销⼤,加锁慢;会出现死锁;锁定粒度最⼩,发⽣锁冲突的概率最低,并发度也最⾼。

由于数据库的库和表都是事先建好的,所以我们针对数据库的操作一般都是针对记录。而对记录进行的四种操作(增删改查),我们可以分为两类,增删改属于读操作,而查询属于写操作。

写操作默认就会加锁,且加的是互斥锁,很容易理解,在进行写行为的时候一定是必须“排他”的。读操作默认不受任何锁影响,但是互斥锁和共享锁都可以加。

读操作加互斥锁  for update;

读操作加共享锁  lock in share mode;

提示:关于共享锁和互斥锁,我们将在下一小节更详细地讲述

行级锁锁的是索引,命中索引以后才会锁行,如果没有命中索引,会把整张表都锁起来。命中主键索引就锁定这条语句命中的主键索引,命中辅助索引就会先锁定这条辅助索引,再锁定相关的主键索引,考虑到性能,innodb默认支持行级锁,但是只有在命中索引的情况下才锁行,

否则锁住所有行,本质还是行锁,但是此刻相当于锁表了

行级锁有三种算法:

1、Record lock

2、Gap lock

3、Next-key lock

其中 Next-key lock 为MySQL默认的锁机制,相当于另外两种锁的功能的整合,并能够解决幻读问题。

提示:在RR事务隔离机制下,才会锁间隙,而RR机制是mysql的默认事务隔离机制。所以,在默认情况下,其实innodb存储引擎锁的是行以及间隙.

我们可以用一个实验来验证上述关于行锁的结论

实验

事务一

事务二

start transaction;

-- 开启事务start transaction;

-- 加排他锁select from t1 where id=7 for update; -- 须知-- 1、上述语句命中了索引,所以加的是行锁-- 2、InnoDB对于行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围(GAP)表记录的索引值为1,5,7,11,其记录的GAP区间如下:(-∞,1],(1,5],(5,7],(7,11],(11,+∞)因为记录行默认就是按照主键自增的,所以是一个左开右闭的区间其中上述查询条件id=7处于区间(5,7]中,所以Next-Key lock会锁定该区间的记录,但是还没完-- 3、*InnoDB存储引擎还会对辅助索引下一个键值加上gap lock**。区间(5,7]的下一个Gap是(7,11],所以(7,11]也会被锁定综上所述,最终确定5-11之间的值都会被锁定

-- 下述sql全都会阻塞在原地insert t1 values(5);insert t1 values(6);insert t1 values(7);insert t1 values(8);insert t1 values(9);insert t1 values(10); -- 下述等sql均不会阻塞insert t1 values(11); insert t1 values(1); insert t1 values(2);insert t1 values(3);insert t1 values(4);

-- 提交一下事务,不要影响下一次实验commit;

-- 提交一下事务,不要影响下一次实验commit;

2、按级别分

数据库的锁按级别分为共享锁,排他锁,共享锁,又被称作读锁,s锁,含义是多个事务共享同一把锁,其中每个事务都能访问到数据,但是没有办法进行修改。

注意:如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁或不加锁(在其他事务里一定不能再加排他锁,但是在事务T自己里面是可以加的)

排他锁,又被称作互斥锁,写锁,x锁,含义是如果有一个事务获取了一个数据的排他锁,那么其它的事务都无法再次获得该数据的任何锁了,但是排他锁支持文件读取,修改和写入。

3、按使用方式分

数据库的锁按使用方式分为悲观锁、乐观锁

悲观锁(Pessimistic Locking),顾名思义指的是对外界将要进行的数据修改操作持悲观态度,因此,在整个数据处理过程中,将数据处于锁定状态。现在由于互联网的高并发架构,即使加上悲观锁也无法保证数据不被外界修改,因此不推荐使用。

乐观锁(Optimistic Locking) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。通常乐观锁的实现是在表中加一个字段(可能是时间戳或版本号),在写入的时候会查询一下版本号,如果版本号没有改变,就写入数据库并同时改变版本号。从本质上来说,乐观锁并没有加锁,所以效率会大大提升,但也有一定的缺陷,就是可能导致一部分任务的写入失败。

三、死锁问题

我们举一个例子来形象的说明死锁这个概念。

比如你和你的邻居同时被锁在了屋子里,然而你有你邻居的钥匙,你的邻居也有你的钥匙,你们互相可以打开对方的房门,但是却都被锁在了各自的屋子里,这就是一个简单的死锁现象

1、第一种情况的死锁

事务1

事务2

begin

begin

select * from t1 where id=6 for update;

delete from t1 where id=3;

update t1 set age=18 where id=3;

delete from t1 where id=6;  -- 阻塞

第一种死锁情况非常好理解,也是最常见的死锁,每个事务执行两条SQL,分别持有了一把锁,然后加另一把锁,产生死锁。

大多数死锁问题,innodb存储引擎都会发现并抛出异常,但是有一种死锁问题极其隐蔽。

2、第二种情况的死锁

与上一种死锁情况不同的是,这种死锁现象必须是两个事务同时运行的情况下才可能发生。前面我们提到过,聚集索引对应的是一整行数据记录。当事务1根据一定的过滤条件,筛选出两条辅助索引时,根据索引的有序性,在锁完辅助索引后锁主键索引时,先锁主键1对应的记录再锁主键2。如果在此同时,事务2通过别的辅助索引同样访问到了这两条数据,但顺序却是先锁主键2再锁主键1,就会互相锁住,产生死锁现象,而且这种情况非常隐蔽,较难排查。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值