事务(Transaction)定义
- 一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
- 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
- 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
事务四大特征
-
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
-
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。(比如:A向B转账,不可能A扣了钱,B却没有收到)
-
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。(比u人:A正在从一张银行卡里面取钱,在A取钱的过程中,B不能向这张银行卡打钱)
-
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
事务的操作
默认情况下,Mysql的事务是自动提交的,可以使用show variables like ‘autocommit‘;
查看。
也就是说,在没有手动开启(Begin)一个事务的情况下,执行增删改操作都是会自动提交的。
改变事务的提交模式
- SET AUTOCOMMIT=0 禁止自动提交
- SET AUTOCOMMIT=1 开启自动提交
事务开启、提交、回滚
- BEGIN 开始一个事务
- ROLLBACK 事务回滚(可以设置检查点进行回滚)
- COMMIT 提交
四大特性之一:隔离性
事务的四个隔离级别:
- 读未提交:read uncommitted
- 读已提交:read committed
- 可重复读:repeatable read
- 串行化:serializable
这四种隔离级别分别会造成的后果:
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读未提交 | ? | ? | ? |
读已提交 | ? | ? | ? |
可重复读 | ? | ? | ? |
串行化 | ? | ? | ? |
设置事务的隔离级别
1.可以在my.ini文件中使用transaction-isolation选项来设置服务器的缺省事务隔离级别。
四种级别:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERIALIZABLE
例如:
[mysqld]
transaction-isolation = READ-COMMITTED
2.通过命令动态设置隔离级别
例如:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
1.读未提交
- 事物A和事物B,事物A未提交的数据,事物B可以读取到
- 这里读取到的数据叫做“脏数据”
2.读已提交
- 事物A和事物B,事物A提交的数据,事物B才能读取到
- 这种级别可以避免“脏数据”
3.可重复读
- 事务A和事务B,如果事务B的开启是在事务A提交之前,那么就算事务A提交了,事务B也读取不到事务A提交的内容
- 也就是说,只有在事务A提交之后开启的事务,才能读取到事务A提交的内容
- Mysql默认的隔离级别
串行化
- 事务A和事务B,事务A在操作数据库时,事务B只能排队等待
- 这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发
- 这种隔离级别效率低,很少使用,
脏读、不可重复读、幻读
1.脏读:(读取未提交数据)
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2.不可重复读:(前后多次读取,数据内容不一致
事务A和事务B,事务B读取了数据,然后事务A对数据进行了修改并提交,事务B再次读取数据,发现数据发生了改变
3.幻读:(前后多次读取,数据行数量发生了变化)
事务B首先根据条件查询得到N条数据,然后事务A改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次根据此条件查询发现有了N+M条数据了,就产生了幻读
关于不可重复读和幻读
“不可重复读”关注的重点其实在于更新和删除这两种操作。
比如:事务B开启后,第一次读取到一些数据之后,就对这些数据进行加行锁,导致事务A无法修改(更新或者删除)数据,于是B事务不管怎么读,返回的都是一样的数据,这就实现了“可重复读”这个隔离级别。
可是要注意:“事务A无法修改这些数据(更新或删除)”,不代表事务A不能insert或update没有被事务B加锁的数据并提交。这样一来事务B再次查询时还是可以读取到一条之前没有出现的数据,这就产生了“幻读”。
所以说,行级锁是无法解决幻读问题的。要想解决这个问题就要实现Serializable隔离级别或者加表级锁。
事务的实现原理
redo log 与 undo log
什么是 redo log
redo log叫做重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中。
例如:
start transaction;
select balance from bank where name="zhangsan";
// 生成 重做日志 balance=600
update bank set balance = balance - 400;
// 生成 重做日志 amount=400
update finance set amount = amount + 400;
redo log作用是什么?
mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。
那么问题来了,如果还没来的同步的时候宕机或断电了怎么办?还没来得及执行上面图中红色的操作。这样会导致丢部分已提交事务的修改信息!
所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。
所以:redo log是用来恢复数据的,用于保障,已提交事务的持久化特性(记录了已经提交的操作)
什么是undo log?
undo log 叫做回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。
undo log 的作用是什么?
undo log 记录事务修改之前版本的数据信息,因此假如由于系统错误或者rollback操作而回滚的话可以根据undo log的信息来进行回滚到没被修改前的状态
所以:undo log是用来回滚数据的用于保障,未提交事务的原子性
Mysql锁
当有多个请求来读取表中的数据时可以不采取任何操作,但是多个请求里有读请求,又有修改请求时必须有一种措施来进行并发控制。不然很有可能会造成不一致。
读写锁
解决上述问题很简单,只需用两种锁的组合来对读写请求进行控制即可,这两种锁被称为:
- 共享锁(shared lock),又叫做"读锁"
读锁是可以共享的,或者说多个读请求可以共享一把锁读数据,不会造成阻塞。 - 排他锁(exclusive lock),又叫做"写锁"
写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。
MVCC简述
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),没开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID.