事务和事务隔离级别

115 阅读7分钟

事务

一组逻辑操作单元,使数据从一种状态变换到另一种状态

事务处理的原则

保证所有事务都作为一个工作单元来执行,即使出现故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交,那么这些修改就 永久地保持下来,要么数据库管理系统将放弃所作的所有修改,整个事务回滚到最初状态

事务的特性

  1. 原子性
  2. 一致性:事务执行前后,数据从一个合法性状态变换到宁外一个合法性状态。
  3. 隔离性:事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务的隔离的,并发执行的各个事务之间不能互相干扰。
  4. 持久性

事务的状态

活动的

事务对应的数据库操作正在执行过程中时,我们就说该事务正在活动的状态。

部分提交的

当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。

失败的

当事务处在活动的或者部分提交的状态时,可能遇到了某些错误而无法继续执行,或者认为的停止当前事务的执行,我们就说该事务处在失败的状态

中止的

如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。即回滚

提交的

当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。

事务的隔离级别

脏写

对于两个事务A、B,如果事务A修改了另一个未提交事务B修改过的数据,那就以为这发生了脏写。

脏读

事务A读取了已经被事务B更新但还没有被提交的字段,之后若事务B回滚,事务A读取的内容就是临时且无效的。

不可重复度

事务A读取了一个字段,然后事务B更新了该字段,之后事务A再次读取同一个字段,值不同了,即发生了不可重复读

幻读

事务A从一个表中读取数据,然后事务B在该表中插入了一些新的行,之后,如果事务A再次读取同一个表,就会多出几行。

Sql中的四种隔离级别

  1. read uncommitted:读未提交,在该隔离界别,所有事务都可以看到其他未提交事务的执行结果,不能避免脏读,不可重复读,幻读。
  2. read committed:读已提交,它满足了隔离的简单定义,一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统默认隔离级别,mysql除外。可以避免脏读,但不可重复读、幻读问题仍然存在。
  3. repeatable read:可重复读,事务A在读到一条数据后,此时事务B对该数据进行了修改并提交。南无事务A再读该数据,读到的还是原来的内容,可以避免脏读,不可重复读,但幻读仍然存在,这个mysql的默认隔离级别
  4. serializable:可串行化,确保事务可以从一个表中读取相同的行,再这个事务持续期间,禁止其他食物对该表执行插入,更新和删除操作。所有的并发问题都可以避免,但性能十分低下,能避免脏读、不可重复读和幻读。

查看mysql隔离级别命令

show variables like 'transciont_isolation'或'tx_isolation'

设置mysql隔离级别

set global|session transaction isolation level serializable 或 set global|session transaction_isolation= 'serializable'

global和session二选一,global表示全局范围影响,session表示会话范围影响。

查询是否自动提交命令

show variables like '%autocommit%'

关闭自动提交命令

set autocommit=0

MVCC多版本控制

快照都与当前读

MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,页能做到不加锁,非阻塞并发读,而这个读指的就是快照读,而非当前读,当前读实际上时一种加锁的操作,时悲观锁的实现,而mvcc本质时采用乐观锁思想的一种方式。

快照读

快照读又叫一致性读,读取的时快照数据,不加锁的简单的select都属于快照读,即不加锁的非阻塞读,比如:

select * from player where 。。。

之所以出现快照读的情况,时基于提高并发性能的考虑,快照读的实现时基于MVCC,它在很多情况下,避免了加锁操作,降低了开销。

既然时基于多版本,那么快照读可能读到的并不一定时数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别。串行级别下的快照读退化为当前读。

当前读

image.png

版本链

每条数据除了数据信息外,另外还有两个字段,trx_id表示事务id.roll_ptr表示一个回滚指针指向undo的日志记录

image.png

ReadVuew读视图

ReadView是事务在使用MVCC机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务构造一个数组,用来记录并维护系统当前活跃事务的ID,

image.png

image.png

ReadView规则

image.png

  1. trx_ids:记录当前活跃的读写事务的id
  2. up_limit_id:最小的事务id
  3. low_limit_id:最大的事务id
  4. creator_trx_id:生成事务id

脏读问题解决方案

如上图,在执行select语句时会生成一个ReadView,ReadView的m_ids列表的内容就是【80,120】,min_trx_id为80,low_limit_id为121,create_trx_id为0(因为没有提交)。从图中可以看出,最新版本的列name的内容是'连',该版本的trx_id值为80,在m_ids列表内,所以不符合可见性要求,(trx_id属性值Readview的up_limit_id和low_limit_id之间说明创建ReadView时生成该版本事务还是活跃的,该版本不可被访问,如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问),根据roll_pointer跳到下一个版本。下一个版本列name的内容是‘马’,该版本的trx_id也是80,也不符合要求,继续跳转到下一个版本,下一个版本的列name是'李瑾',trx_id是60,小于ReadView中的up_limit_id的值,所以这个版本是符合要求的, 返回此条数据。

不可重复读解决方案

使用Repeatable read 隔离级别,在第一次读取数据时就生成一个ReadView,仅且只会生成一次,之后查询不会重复生成。以上图为例,试图为ReadView的m_ids列表的内容就是【80,120】,up_limit_id为80,low_limit_id为121,create_trx_id为0。然后从版本链中挑选可见的记录,最新版本的列name的内容时'连',该版本的trx_id值为80,在m_ids列表内,所以不符合可见性要求(trx_id属性值Readview的up_limit_id和low_limit_id之间说明创建ReadView时生成该版本事务还是活跃的,该版本不可被访问,如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问),根据roll_pointer条到下一个版本。虽然事务提交,由于用的时老试图,所以访问的还是老数据;解决不可重复读问题

image.png image.png

如何解决幻读

image.png

image.png

image.png

image.png

image.png