Transaction事务是指一个逻辑单元,执行一系列操做的SQL语句。html
事务中一组的SQL语句,要么所有执行,要么所有回退。在Oracle数据库中有个名字,叫作transaction IDsql
在关系型数据库中,事务必须ACID的特性。数据库
目前重点讨论隔离性。数据库一共有四个隔离级别安全
未提交读(RU,Read Uncommitted)。它能读到一个事物的中间状态,不符合业务中安全性的保证,违背 了ACID特性,存在脏读的问题,基本不会用到,能够忽略session
提交读(RC,Read Committed)。顾名思义,事务提交以后,那么咱们能够看到。这是一种最广泛的适用的事务级别。咱们生产环境经常使用的使用级别。并发
可重复读(RR,Repeatable Read)。是目前被使用得最多的一种级别。其特色是有GAP锁,目前仍是默认级别,这个级别下会常常发生死锁,低并发等问题。高并发
可串行化,这种实现方式,其实已是不是多版本了,而是单版本的状态,由于它全部的实现都是经过锁来实现的。性能
所以目前数据库主流经常使用的是RC
和RR
隔离级别。spa
隔离性的实现方式,咱们一般用Read View表示一个事务的可见性。版本控制
RC级别,事务可见性比较高,它能够看到已提交的事务的全部修改。所以在**提交读(RC,Read Committed)**隔离级别下,每一次select语句,都会获取一次Read View,获得数据库最新的事务提交状态。所以对于数据库,并发性能也最好。
RR级别,则不是。它为了不幻读和不可重复读。保证在一个事务内先后数据读取的一致。其可见性视图Read View只有在本身当前事务提交以后,才会更新。
那如何保证数据的一致性?其核心是经过redo log和undo log来保证的。
而在数据库中,为了实现这种高并发访问,就须要对数据库进行多版本控制,经过事务的可见性来保证事务看到本身想看到的那个数据版本(或者是最新的Read View亦或者是老的Read View)。这种技术叫作MVCC
多版本是如何实现的?经过undo日志来保证。每一次数据库的修改,undo日志会存储以前的修改记录值。若是事务未提交,会回滚至老版本的数据。其MVCC的核心原理,之后详谈
举例论证:
## 开启事务 MariaDB [scott]> begin; Query OK, 0 rows affected (0.000 sec) ##查看当前的数据 MariaDB [scott]> select * from dept; +--------+------------+----------+ | deptno | dname | loc | +--------+------------+----------+ | 10 | ACCOUNTING | beijing | | 20 | RESEARCH | DALLAS | | 30 | SALES | CHICAGO | | 40 | OPERATIONS | beijing | | 50 | security | beijing | | 60 | security | nanchang | +--------+------------+----------+ 6 rows in set (0.001 sec) ##更新数据 MariaDB [scott]> update dept set loc ='beijing' where deptno = 20; Query OK, 1 row affected (0.001 sec) ## 其行记录| 20 | RESEARCH | DALLAS |已经被放置在undo日志中,目前最新的记录被改成'beijing': MariaDB [scott]> select * from dept; +--------+------------+----------+ | deptno | dname | loc | +--------+------------+----------+ | 10 | ACCOUNTING | beijing | | 20 | RESEARCH | beijing | | 30 | SALES | CHICAGO | | 40 | OPERATIONS | beijing | | 50 | security | beijing | | 60 | security | nanchang | +--------+------------+----------+ ##事务不提交,回滚。数据回滚至老版本的数据。 MariaDB [scott]> rollback; Query OK, 0 rows affected (0.004 sec) MariaDB [scott]> select * from dept; +--------+------------+----------+ | deptno | dname | loc | +--------+------------+----------+ | 10 | ACCOUNTING | beijing | | 20 | RESEARCH | DALLAS | | 30 | SALES | CHICAGO | | 40 | OPERATIONS | beijing | | 50 | security | beijing | | 60 | security | nanchang | +--------+------------+----------+ 6 rows in set (0.000 sec)
由于MVCC,让数据库有了很强的并发能力。随着数据库并发事务处理能力大大加强,从而提升了数据库系统的事务吞吐量,能够支持更多的用户并发访问。但并发访问,会出现带来一系列问题。以下:
数据库并发带来的问题 | 概述解释 |
---|---|
脏读(Dirty Reads) | 当一个事务A正在访问数据,而且对数据进行了修改,而这种修改尚未提交到数据库中,这时,另一个事务B也访问这同一个数据,如不控制,事务B会读取这些"脏"数据,并可能作进一步的处理。这种现象被称为"脏读"(Dirty Reads) |
不可重复读(Non-Repeatable Reads) | 指在一个事务A内,屡次读同一数据。在这个事务尚未结束时,另一个事务B也访问该同一数据。那么,在事务A的两次读数据之间,因为第二个事务B的修改,那么第一个事务两次读到的的数据多是不同的 。出现了"不可重复读"(Non-Repeatable Reads)的现象 |
幻读(Phantom Reads) | 指在一个事务A内,按相同的查询条件从新检索之前检索过的数据,同时发现有其余事务插入了数据,其插入的数据知足事务A的查询条件。所以查询出了新的数据,这种现象就称为"幻读"(Phantom Reads) |
隔离级别和上述现象之间的联系。
隔离级别有:未提交读(RU,Read Uncommitted),提交读(RC,Read Committed),可重复读(RR,Repeatable Read),可串行化(Serializable)
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(RU,Read Uncommitted) | 可能 | 可能 | 可能 |
提交读(RC,Read Committed) | 不可能 | 可能 | 可能 |
可重复读(RR,Repeatable Read) | 不可能 | 不可能 | 可能<br />(间隙锁解决) |
可串行化(Serializable) | 不可能 | 不可能 | 不可能 |
举例在隔离级别RR
和RC
下,说明“不可重复读”问题。
MySQL的默认级别是Repeatable Read
,以下:
MariaDB [(none)]> select @@global.tx_isolation; +-----------------------+ | @@global.tx_isolation | +-----------------------+ | REPEATABLE-READ | +-----------------------+ 1 row in set (0.000 sec)
这里修改当前会话级别为Read Committed
MariaDB [scott]> set session transaction isolation level read committed; Query OK, 0 rows affected (0.001 sec) MariaDB [scott]> select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | READ-COMMITTED | +----------------+ 1 row in set (0.000 sec)
在隔离级别已**提交读(RC,Read Committed)**下,出现了不可重复读的现象。在事务A中能够读取事务B中的数据。
在隔离级别可重复读(RR,Repeatable Read),不会出现不可重复读现象,举例以下:
举例说明“幻读”的现象。
行锁能够防止不一样事务版本的数据在修改(update)提交时形成数据冲突的问题。可是插入数据如何避免呢?
在RC隔离级别下,其余事务的插入数据,会出现**幻读(Phantom Reads)**的现象。
而在RR隔离级别下,会经过Gap锁,锁住其余事务的insert操做,避免"幻读"的发生。
所以,在MySQL事务中,锁的实现方式与隔离级别有关,如上述实验所示。在RR隔离级别下,MySQL为了解决幻读的问题,已牺牲并行度为代价,经过Gap锁来防止数据的写入。这种锁,并行度差,冲突多。容易引起死锁。
目前流行的Row模式能够避免不少冲突和死锁问题,所以建议数据库使用**ROW+RC(Read Committed)**模式隔离级别,很大程度上提升数据库的读写并行度,提升数据库的性能。
原文出处:https://www.cnblogs.com/zhangshengdong/p/11912229.html