1、数据库事务的典型场景?

2、事务的定义

事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成

3、事务的四大特性

(1)原子性

1、√
2、√
3、×

3失败了,所以要求1/2也要失败,那么怎么办呢?这个就需要undo log来实现了

(2)持久性

断电、重启
崩溃恢复的特性, redo log 来实现

(3)隔离性

show global variables like “tx_isolation”;
image.png
我读的是脏数据,你操作你的,我们操作我的。把大家的操作进行隔离。

1、如果没有隔离性,所带来的问题(事务并发带来的问题)

事务并发问题一
image.png
事务并发问题二

image.png
明明执行了同样的SQL语句却得到的结果不一样。

事务并发问题三
image.png

不可重复读和幻读都是读到了已经提交的数据,幻读只跟插入有关。
删除和查询所造成的不一样就是不可重复读。

2、事务的隔离级别

image.png
image.png

读已提交

已提交读的实现方式:

版本链 对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(row_id并不是必要的,我们创建的表中有主键或者非NULL唯一键时都不包含row_id列);

  • trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。
  • roll_pointer: 每次对某条记录进行改动时,这个隐藏列会存一个指针,可以通过这个指针找到该记录修改前的信息

ReadView 对于使用READ UNCOMMITEED隔离级别的事务来说,直接读取记录的最新版本就好了,对于使用SERIALIZABLE隔离级别的事务来说,使用加锁的方式来访问记录。对于使用READ COMMITTED和REPEATABLE READ隔离级别的事务来说,就需要用到我们上边所说的版本链了,核心问题就是:需要判断一个版本链中的那个版本是当前事务可见的。

image.png
image.png
它会生成一个版本链,然后会生成一个ReadView,会找到它的下一个已经

A事务在查的时候会生成一个readView,readView中的m_ids中记录了当前活跃的事务id,然后找到已经提交的最新值。

可重复读

我们现在要对一个行进行操作,又不想让别人看见,
如果采用对这一行进行加锁,然后加锁之后,别人也会读不到,所以这样效率就会很低。所以我们采用MVCC

MVCC总结 MVCC 指的就是在READCOMMITED、 REPEATABLE READ这两种隔离级别的事务在执行普通的SELECT操作时访问记录的版本链过程,可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。READ COMMIT、REPEATABLE READ这两个隔离级别的一个很大不同就是: 生成ReadView的时机不同,READ COMMITED在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。

image.png
如果对这一行进行加写锁,那么再来一个就会产生阻塞的现象,如果我直接执行select * from table where id =2; 直接会读取到,不会产生阻塞。
如果加了,读锁,可以再加读锁,但不能加写锁。

行锁 (锁这一行) LOCK_REC_NOT_GAP: 单个行记录上的锁 LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身,GAP锁的目的,是为了防止同一事务的两次当前读、出现幻读的情况 LOCK_ORDINARY: 锁定一个范围,并且锁定记录本身,

在读已提交的隔离级别下:
无论是进行主键索引、唯一索引、普通索引都是只能对自己查出来的哪一行进行加锁的。
(只会对查出来的数据进行加锁)
重复读的隔离级别下:
**
https://www.cnblogs.com/myseries/p/10931595.html

事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。

综上:
  因为MySQL的可重复读,对事务B进行查询时,事务A提交的更新不会影响到事务B。
  但是对事务B进行更新时,事务A提交的更新会影响到事务B。

实现原理(MVCC【多版本并发控制】)

为了修复脏读的问题,我们提出了读已提交的事务隔离级别;为了修复不可重复的问题,我们提出了可重复读事务隔离级别;为了修复幻读的问题,我们提出了串行化的事务隔离级别。注意:在MySQL中,修复幻读的时候,并没有使用到串行化的事务隔离级别,而是使用了MVCC多版本并发控制和Next key lock临键锁的方式来修复幻读问题的。

那么什么是当前读? 这个是什么意思?

当前读:
**
更深刻认识当前读:

在我们更新表中的数据的时候,有的时候一次性的更新多个列的值,这多个待更新的列之间又有业务逻辑上的关系。这个时候我们在更新的时候一定要格外的注意,更新过程中的当前读,这里的当前读是指读取当前事务中的值。

  1. CREATE TABLE `test` (
  2. `id` int(255) DEFAULT NULL,
  3. `a` int(255) DEFAULT NULL,
  4. `b` int(255) DEFAULT NULL,
  5. `c` int(255) DEFAULT NULL
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. INSERT INTO `test`(`id`, `a`, `b`, `c`) VALUES (1, 100, 100, 100);

更新语句如下:

  1. update test
  2. set a=a-1, b=a-1-1, c=a-1-1-1
  3. where id=1;

此时,你先自己口算一下,最后这个表中的这一行数据的abc三列的值分别是多少呢?答案是:a=99, b=97, c=96。你可能会好奇,为什么不是:a=99, b=98, c=97呢?因为abc原先都为100,那么a=a-1=100-1=99;b=a-1-1=100-1-1=98才对呀?为什么b的值最后是97呢?

这里我们分析一下。

在执行a=a-1这个语句块,MySQL在计算a到底需要等于多少的时候,要获取数据库中a的值,此时得到的a为100,那100-1后赋值给了a,在当前这个更新语句的事务中,a此时为99了。

在执行b=a-1-1的时候,它会获取a的值然后再去为b赋值,因为这个更新语句是一个事务,所以这个事务前面修改的a的值,当前的事务还是会承认的,所以它读到a的值为99,此时再执行b=99-1-1=97,

在执行c=a-1-1-1的时候,同样会读取当前事务中a的值为99,然后计算出c=99-1-1-1=96。

如果我们把上面的更新语句改为下面的SQL语句,结果又会如何呢?

https://blog.csdn.net/javaanddonet/article/details/110674287

3、如果要解决读一致性的问题,保证一个事务中前后两次读取数据结果一致,实现事务隔离应该怎么做?

解决方案1:
在读取数据前,对其加锁,阻止其他事务对数据进行修改(LBCC)Lock Based Concurrency Control。

解决方案2:
生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取(MVCC)Multi Version Concurrency Control。

不管别人怎么样,你都读取你自己的。

MVCC 只在RC RR中使用

读未提交,当前读。

(4)一致性

最终进行一致性。

4、开启事务方式

(1)DML 增删改 自动开启事务

show variables like ‘autocommit’

可以查看是否开启自动提交

(2)begin