数据库事务
事务定义
事务是数据库管理系统DBMS执行过程中的逻辑单位,由一个有限的数据库操作序列构成。
-
所谓的逻辑单位,以为着它是数据库最小的工作单元,是不可以再分的
-
它可能包含了一个或者一些列的DML语句,包括 insert,update,delete。
单条DDL(create,drop)和DCL(grant,revoke也会有事务)
MySQL中支持事务的引擎
MySQL的引擎中,只有InnoDB支持事务
事务的四大特性
原子性 atomicity
意味着数据库的一系列操作,要么都是成功的,要么都是失败的,不可能出现部分成功或者部分失败的情况。
原子性在InnoDB中是通过undo log来实现的,它记录了数据修改之前的值(逻辑日志),一旦发生异常,可以用undo log来实现回滚。
隔离性 Isolation
在数据库里会有很多的事务同时去操作同一张表或者同一行数据,必然会产生一些并发或者干扰的操作。
对隔离性的定义,就是这写很多个的事务,对表或者行进行并发操作,应该是透明的,互相不干扰的。
持久性 Durability
我们对数据库的任意的操作,增删改,只要事务提交成功,那么结果就是永久性的,不可能因为数据库掉电、宕机、意外重启又变成原来的状态。
持久性是通过redo log和double write buffer 来实现的,操作数据的时候,会先写到内存的buffer pool中,同时记录redo log,如果刷盘前出现异常,重启的时候可以读取redo log的内容,写入到磁盘,保证数据的持久性。
恢复成功的前提是数据源本身没有被破坏,是完整的,通过双写缓冲保证。
一致性 consistent
指的是数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。
数据库什么时候出现事务
update student set name = 'young' where id = 1;
事实上,执行这条SQL的时候。他不仅开启了一个事务,而且还自动提交了,最终写入了磁盘。
增删改的语句会自动开启事务,一条SQL一个事务。每个事务都是有编号的,这个编号是个证书,有递增的特性。
如果要把多条SQL放在一个事务里,则需要进行手动开启事务。手动开启事务的方式有两种,一种是用begin;
,一种是用start transaction
。
结束事务的方式也有两种,一种是回滚事务rollback
,一种是提交事务commit
。
InnoDB中有一个autocommit的参数,分为两个级别,session级别和global级别。
show variables like 'autocommit';
默认是开启的(ON),表示开启自动提交事务,如果将它设置为false/off,那么数据库的事务就需要进行手动的rollback或者commit。
当客户端断开连接时,事务也会结束。
事务并发带来的问题
脏读
假设有两个事务,一个事务编号是1,一个事务编号是2,当事务1通过条件where id = 1 去查询行,返回age = 10,此时事务2通过update 语句,将id=1这条数据的age值改成了12,但是没有提交。
此时,如果事务1再去查询where id=1,它拿到的数据中age变成了12。这种情况下,在一个事务中,由于其他事务修改了数据并且没有提交,导致前后两次查询返回数据不一致,这种情况就叫做脏读。
不可重复读
假设有两个事务,一个事务编号是1,一个事务编号是2,当事务1通过条件where id = 1 去查询行,返回age = 10,此时事务2通过update 语句,将id=1这条数据的age值改成了12,然后commit提交事务。
此时第一个事务读取到了其他事务已经提交的数据,导致前后两次数据不一致的情况,叫做不可重复读。
幻读
假设有两个事务,一个事务编号是1,一个事务编号是2,事务1进行了一个范围查询,此时满足条件的数据只有1条。事务2此时进行了一个插入(insert)操作,并且commit提交事务。在事务1中再次进行查询,发现多了一行数据。
一个事务前后两次读取数据不一致,是由于其他事务插入导致的,这种情况叫做幻读
不可重复读和幻读的区别
修改或者删除导致的读不一致叫做不可重复的,插入导致的读不一致叫做幻读。
事务隔离级别定义
由美国国家标准协会(ANSI)指定的SQL标准,定义了4种事务隔离级别
-
读未提交(Read Uncommitted)
一个事务可以读取到其他事务未提交的数据,会出现脏读,它没有解决任何问题
-
读已提交(Read Committed)
事务只能读取其他事务已经提交的数据,不能读取到其他事务未提交的数据,解决了脏读问题,但是会出现不可重复读问题
-
可重复读(Repeatable Read)
解决了不可重复读的问题,也就是在一个事务中,多次读取同样的数据,结果是一样的,在这个级别下,没有定义解决幻读的问题
-
串行化(Serializable)
在这个隔离级别中,所有的事务都是串行执行的,也就是对数据的操作需要排队,不存在并发操作,所以解决了所有问题。
事务隔离级别是可以修改的
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;
InnoDB对隔离级别的支持
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 |
可重复的 | 不可能 | 不可能 | InnoDB不可能 |
串行化 | 不可能 | 不可能 | 不可能 |
InnoDB解决了幻读问题。
读一致性的解决方案
LBCC
基于锁的并发控制Lock Base Concurency Control。
如果仅仅是基于锁来实现事务隔离,一个事务读取的时候不允许其他事务修改,那就意味着不支持并发读写操作,这样会极大的影响操作数据库的效率。
MVCC
如果要让一个事务前后两次读取的数据保持一致,那么我们可以在修改数据的之前给它简历一个备份或者快照,后面再来读取这个快照就行了。这种方案叫做多版本的并发控制Mulit Version Concurrency Control
MVCC的原则
一个事务能看到的数据版本:
- 第一次查询之前已经提交的是事务的修改
- 本事务的修改
一个事务不能看见的数据版本
- 在本事务第一次查询之后创建的事务(事务ID比自身的事务ID大)
- 活跃的(未提交的)事务的修改
MVCC的效果
我可以查到在我这个事务开始前已近存在的数据,即时它在后面被修改或者删除了。而在我这个事务之后新增的数据,是看不到的。
所以把它叫做快照,不管别的事务做任何操作,他只能看到第一次查询时看到的数据版本。