mysql总结(二)-事务

事务隔离级别

  1. 读未提交是指,一个事务还没提交时,它作的变动就能被别的事务看到。
  2. 读提交是指,一个事务提交以后,它作的变动才会被其余事务看到。
  3. 可重复读是指,一个事务执行过程当中看到的数据,老是跟这个事务在启动时看到的数据是一致的。固然在可重复读隔离级别下,未提交变动对其余事务也是不可见的。
  4. 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
// session A 
begin;
select * from t1 where id = 1 //

//session B 
begin;
select * from t1 where id = 1
update t1 set amonut=amount+1 where id = 1

//session A 
select * from t1 where id = 1 //v1

// session B 
commit 

// session A 
select * from t1 where id = 1 //v2
commit 
select * from t1 where id = 1 //v3

复制代码

上面的查询分析来分析每一个事务隔离级别下v1,v2,v3不一样的显示mysql

  1. 若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然尚未提交,可是结果已经被 A 看到了。所以,V二、V3 也都是 2
  2. 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。因此, V3 的值也是 2。
  3. 若隔离级别是“可重复读”,则 V一、V2 是 1,V3 是 2。之因此 V2 仍是 1,遵循的就是这个要求:事务在执行期间看到的数据先后必须是一致的。
  4. 若隔离级别是“串行化”,则在事务 B 执行“将 1 改为 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才能够继续执行。因此从 A 的角度看, V一、V2 值是 1,V3 的值是 2。

事务隔离的实现

MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操做。记录上的最新值,经过回滚操做,均可以获得前一个状态的值sql

假设一个值从1被按顺序的改成2,3,4,在回滚日志里面就会有下面的记录数据库

read-view A -> 将2改成1 
read-view B -> 将3改成2 将4改成3 
read-view C -> 当前值4 
复制代码

经过不一样时刻启动不一样的read-view,在视图A,B,C里面,这个记录的值分别为1,2,4,同一记录存在多个版本,就是数据库的多版本(MVCC).对于 read-view A,要获得 1,就必须将当前值依次执行图中全部的回滚操做获得.同时你会发现,即便如今有另一个事务正在将 4 改为 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的session

事务日志的删除和长事务

在不须要的时候才删除。也就是说,系统会判断,当没有事务再须要用到这些回滚日志时,回滚日志会被删除,当系统里没有比这个回滚日志更早的 read-view 的时候就会删除.spa

长事务意味着系统里面会存在很老的事务视图。因为这些事务随时可能访问数据库里面的任何数据,因此这个事务提交以前,数据库里面它可能用到的回滚记录都必须保留,这就会致使大量占用存储空间.==长事务还占用锁资源,也可能拖垮整个库==日志

事务MVCC的简单分析

  1. 每一个事务都有一个事务ID,叫作transaction id(递增)code

  2. 事务在启动时,找到已提交的最大事务ID记为up_limit_id。事务

  3. 事务在更新一条语句时,好比id=1改成了id=2.会把id=1和该行以前的row trx_id写到undo log里,而且在数据页上把id的值改成2,而且把修改这条语句的transactionid记在该行行头资源

  4. 一个事务要查看一条数据时,必须先用该事务的up_limit_id与该行的transaction id作比对,若是up_limit_id>=transactionid,那么能够看.若是up_limit_id<transaction id,则只能去undo log里去取。去undo log查找数据的时候,也须要作比对,必须up_limit_id>transaction id,才返回数据it

事务的可见性

// session A 
start transaction with consistent snapshot   //立刻启动事务

// session B 
start transaction with consistent snapshot

// session C 
UPDATE t set k=k+1 where id =1 

// session B 
UPDATE t set k=k+1 where id =1 
SELECT k from t where id = 1  //查询结果 3 

// session A 
SELECT k from t where id = 1 //查询结果 1 
commit 

// session B 
comit 


复制代码
  1. A的查询语句是1 由于他的查询是在B视图查询以后,可重复读的特性是在其余事务的更改对当前事务部可见
  2. B的查询语句是3 所以B的查询语句是在C的提交以后,在当前事务进行的更改,未提交对当前事务也是可见的,B的查询是3是由于更新的时候须要去读一次,而C已经进行语句提交了,所以B的更新语句先在当前事务查询到k的值为2,再进行更新一次为3.所以==更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)==

一个数据版本,对于一个事务视图来讲,除了本身的更新老是可见之外,有三种状况

  1. 版本未提交,不可见;
  2. 版本已提交,可是是在视图建立后提交的,不可见;
  3. 版本已提交,并且是在视图建立前提交的,可见。

事务和锁

上面的例子 咱们将事务C也进行声明事务,接下来看看会怎么样

// session A 
start transaction with consistent snapshot   //立刻启动事务

// session B 
start transaction with consistent snapshot

// session C 
start transaction with consistent snapshot // C也启动事务
UPDATE t set k=k+1 where id =1 

// session B 
UPDATE t set k=k+1 where id =1 
SELECT k from t where id = 1  //查询结果 3 

// session C
commit 

// session A 
SELECT k from t where id = 1 //查询结果 1 
commit 

// session B 
comit 

复制代码

相对于上个版原本说,事务B的更新的时候,事务C还未进行提交,这样的话当前A,B的查询结果会是什么样子呢?

上一篇文章中提到的“两阶段锁协议”就要上场了。事务 C’没提交,也就是说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必需要读最新版本,并且必须加锁,所以就被锁住了,==必须等到事务 C’释放这个锁==,才能继续它的当前读

可重复读的核心就是==一致性读==;而事务更新数据的时候,只能用当前读。若是当前的记录的行锁被其余事务占用的话,就须要进入锁等待。而读提交的逻辑和可重复读的逻辑相似,它们最主要的区别是:

  1. 在可重复读隔离级别下,只须要在事务开始的时候建立一致性视图,以后事务里的其余查询都共用这个一致性视图;
  2. 在读提交隔离级别下,每个语句执行前都会从新算出一个新的视图。
相关文章
相关标签/搜索