事务

2020-01-27  本文已影响0人  kar_joe

事务基本概念

事务特性

ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)

问题

脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)

隔离级别

事务隔离的实现

在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。
通过多版本并发控制MVCC实现


image.png

通过undolog,可以实现获取指定版本的数据。InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。

为什么尽量不要使用长事务。
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间;又因为指定版本的数据,是通过最新版本然后倒序执行所有undolog获取到的,所以长事务的select语句可能耗时很久

举例1

可重复读隔离级别


image.png
image.png

一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:

若改为当前读


image.png

更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。select 语句如果加锁,也是当前读。所以,如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update。

假设事务 C 不是马上提交的,而是变成了下面的事务 C’,会怎么样呢


image.png

事务 C’没提交,也就是说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必须要读最新版本,而且必须加锁,因此就被阻塞了,必须等到事务 C’释放这个锁,才能继续它的当前读。

假如隔离级别是读提交,A读到的又是什么呢?

举例2

当前库存:num=200
假如多线程并发,AB同时开启事务,A先请求到行锁,
A:
start transaction;
select num from t where num>0;先查询当前库存值(num>0)
update t set num=num-200; 库存减量

B:
start transaction;
select num from t where num>0;先查询当前库存值(num>0)
update t set num=num-200; 库存减量
----结果---
A:查询到num=200,做了库存减量成了0
B:事务启动后,查询到也是200,等 A 释放了行锁,B进行update,直接变成 -200
但是 B 查询时,时有库存的,因此才减库存,结果变成负的。

  1. 问题分析
    问题主要是快照读与当前读数据不一致导致的

  2. 如何解决
    查询采用当前读;或者更新时加where条件,要求库存大于200

上一篇 下一篇

猜你喜欢

热点阅读