事务

一、事务的介绍

事务:表示操作集合,不可分割,要么全部成功,要么全部失败

事务的开始取决于一个DML语句

事务的结束:

  1、正常的commit或者rollback

  2、自动提交,一般情况下进行关闭操作,效率太低

  3、用户关闭会话之后会自动提交事务

  4、系统崩溃或断电的时候会回滚事务

注意:在mysql常用的存储引擎中只有InnoDB支持事务

二、事务的四个特性(ACID)

1、原子性:一个操作集合(事务)要么全部成功,要么全部失败

      原子性是通过undo log实现的。在操作数据之前会将数据备份到undo log中。当前行的历史版本数据在undolog中

      当delete一条数据时,undo log中会记录一条add操作;

      当add一条数据时,undo log中会记录一条delete操作;

      当update一条语句时,undo log中会记录一条相反的update语句;

2、一致性:保证数据的一致性,经过n多个操作之后,数据的状态不会改变(转账时总金额不会改变)

3、隔离性:各个事务之间不会产生影响。严格的隔离性会降低效率,某些情况下会降低隔离级别来提高效率

      隔离性中有四个隔离级别,隔离级别是通过锁机制完成的

4、持久性:所有数据的修改都必须持久化到存储介质,不会因为程序的关闭而导致数据的丢失

      持久性是通过redo log实现的。当数据发生修改的时候,innodb引擎会先将记录写道redo log中并更新内存,此时更新算是完成了,同时innodb引擎会在合适的时机将记录操作到磁盘中

  注意:数据库所有特性中都是为了保证数据的一致性

三、锁的机制

为了解决在并发访问的时候,数据不一致的问题,需要给数据加锁

加锁的同时需要考虑‘粒度’的问题:

  操作对象有:①数据库

        ②表

        ③行

  一般情况下,锁的粒度越小,效率越高,粒度越大,效率越低

四、事务的隔离级别

利用mysql数据库命令行进行模拟:

  ①开启两个命令窗口

  ②关闭事务自动提交:set autocommit = 0(select @@autocommit查看事务是否自动提交,1为开启,0为关闭)

  注意:autocommit在不考虑隔离级别的时候主要针对增删改操作,不针对表创建

     在一个事务中创建表,即使没有commit,另一个事务中也可以找到对应表名

  ③准备测试数据

    创建数据库:create database tran;

    切换数据库(两个窗口都执行):use tran;

    创建表:create table psn(id int primary key,name varchar(10)) engine=innodb;

    插入数据:insert into psn values(1,'zhangsan');

    insert into psn values(2,'lisi');

    insert into psn values(3,'wangwu');

    commit;

1、读未提交:会造成脏读

  A:set session transaction isolation level read uncommitted.

       设置当前会话    事务       隔离      级别  读未提交

    start transaction;  开启一项新的事务

 

  B:set session transaction isolation level read uncommitted.

   start transaction; 

       A:select * from psn;

  B:select * from psn;  这时两个窗口数据一致

  A:update psn set name = 'x'

  A:select * from psn;  发现数据已经修改

  B:select * from psn;  发现数据已经修改,但此时A窗口中事务未提交

解释:A事务并未提交事务,但此时B窗口已经读到未提交的事务

2、读已提交:会造成不可重复读

  A:set session transaction isolation level read committed.

       设置当前会话    事务       隔离      级别  读已提交

    start transaction;  开启一项新的事务

  B:set session transaction isolation level read committed.

   start transaction; 

       A:select * from psn;

  B:select * from psn;  这时两个窗口数据一致

  A:update psn set name = 'y' where id = 1;  此时A窗口中数据发生改变

  B:select * from psn;  此时B窗口中数据未发生改变,屏蔽了脏读的情况

  A:commit;

  B:select * from psn;  此时数据已经改变

解释:在B窗口内,出现了情况不相同的两种数据情况。

   相当于你去银行取钱,在插卡的时间内,10点显示你的余额有1000,10点05分显示你的余额有500,在这期间你的银行卡是未拔出的。这是不合理的。

  若此时B窗口在提交事务后进行查询,则会显示正常数据。

3、可重复读:会造成幻读,只在插入和删除的时候会产生

  A:set session transaction isolation level repeatable read;

       设置当前会话    事务       隔离      级别    可重复读

    start transaction;  开启一项新的事务

  B:set session transaction isolation level repeatable read;

   start transaction; 

       A:select * from psn;

  B:select * from psn;  这时两个窗口数据一致

  A:update psn set name = 'y' where id = 2;  此时A窗口中数据发生改变

   commit;

  B:select * from psn;  此时B窗口中数据未发生改变,屏蔽了不可重复读的情况

   commit;

  A:start transation;

  B:start transation;

  A:select * from psn;

  B:select * from psn;  此时数据一致性

  A:insert into psn values(4,'e');

   commit;

  B:select * from psn;  发现无id为4的数据

  B:insert into psn values(4,'e');  在B窗口中执行A窗口中的插入语句,报错主键重复

解释:在B窗口中执行A窗口中的插入语句,报错主键重复,说明此时B窗口中已经查到id为4的数据,但未显示

4、序列化

  给所有select语句中都加入了lock in share mode,性能很低

 五、保证事务的一致性-MVCC

MVCC--->多版本并发控制

本质上是保证读写操作之间没有冲突

当前读:读取为最新版本数据,共享锁、排他锁都是读取的最新版本

快照读:读取为历史版本数据。提高数据库并发查询能力

MVCC实现原理

MVCC实现原理主要依赖三个隐式字段、undolog、readView来实现

隐式字段:

  DB_TRX_ID:事务id,最近修改日志的id,与行绑定

  DB_ROLL_PTR:回滚指针,当回滚时指向上一个事务

  DB_ROW_ID:如果数据没有主键,那么innodb会生成一个6字节的row_id

DB_ROW_ID和Innodb中row_id联系:Innodb为聚簇索引,当插入一条数据时要和主键绑定,有主键用主键,无主键用唯一键,若都没有,会自动生成6字节的row_id与row_id绑定

ReadView

最大作用是做可见性判断,把各个版本的DB_TRX_ID取出

ReadView属性

trx_list:一个数值列表,用来维护ReadView生成时刻系统正在活跃的事务id

up_limit_id:记录trx_list中最小id

low_limit_id:ReadView生成时刻系统尚未分配的下一个事务id

RC与RR隔离级别在InnoDB快照读有什么不同?

RC隔离级别下:ReadView视图生成的时间是执行查询的sql语句之前,每一次进行快照读都会生成新的readview

RR隔离级别下:ReadView视图生成的时间是在事务开始后就会立刻生成,每一次进行快照读都会沿用之前的readview

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

  生成一个数据请求时间点的一致性数据快照,并用这个快照来提供一定级别的一致性读取(MVCC)

1、MVCC产生的效果:建立了一个快照,同一个事务无论查多少次都是相同的数据

2、一个事务能看到的版本

  ①第一次查询之前已经提交的事务的修改

  ②本事务的修改

3、一个事务不能看见的数据版本

  ①在本事务第一次查询之后创建的事务(事务ID比我的事务ID大)

  ②活跃的(未提交的)事务的修改

六、面试题:在InnoDB引擎下,如何在隔离级别为可重复读下,解决幻读

  在隔离级别为可重复读的情况下,使用MVCC+临键锁解决幻读

  注意:在只有快照读时不会产生幻读问题,在当前读和快照读同时出现才会出现幻读问题

事务A:select *  from tab;

事务B:select *  from tab;

    此时查询结果相同

事务B:insert into tab values(xxx);

事务A:select * from tabl;  不会查到新插入的xxx,因为事务A使用的是之前产生的快照读

 

事务A:update tab set value = yyy where id =1;  

事务A:select * from tab;  此时查询结果会出现事务B插入的xxx,因为读取的是当前读,即为最新的数据

这就模拟了幻读问题

解决方法:

  事务A:select * from tab where 条件 for update;  此时已经给索引加锁

  事务B:insert into tab values(nnn);  此时会进行等待,事务B被阻塞,只有在事务A被提交后事务B才会执行

七、如果数据库发生死锁,如何定位是哪条sql产生的死锁

posted @ 2020-08-15 13:18  showMeTheCodes  阅读(105)  评论(0编辑  收藏  举报