0
点赞
收藏
分享

微信扫一扫

MySql总结(四)----事务

进击的铁雾 2022-04-14 阅读 75
mysql

ACID四大特性

  1. 原子性:数据事务是不可分割的工作单位,要么都执行成功,有一个不成功就撤销操作。
  2. 一致性:指事务将数据库从一种状态转变为另一种一致的状态。事务从开始到结束,数据库的完整性约束没有被破坏。例如银行转账:a给b转账500块,a若减少了500块,那么b就相应的增加500块,在这个过程中,钱的总数是没变的。
  3. 隔离性:每个读写十五的对象与其他事务的操作对象能相互分离,该事务提交前,其他事务不可见。
  4. 持久性:事务一旦提交,其结果是永久的,提交后数据保存到硬盘上。

事务类型

  1. 扁平事务:使用最为频繁,由begin work开始,由commit work或rollback woke结束,处于之间的操作是原子的,要么都执行,要么都会滚。
  2. 带有保存点的扁平事务:允许事务执行过程中回滚到同一事务中较早的一个状态。保存点用save work函数建立,通知系统记录当前的处理状态。保存点可以有多个,可以作为内部重启点。(系统崩溃,所有保存点消失)
  3. 链事务:在提交事务时,释放不需要的数据对象,将处理结果传给下一个要开始的事务,提交事务操作和开始下一个要开始的事务合并为一个原子操作。(回滚仅限当前事务,只能恢复到最近的一个保存点)
  4. 嵌套事务:岑皇帝结构框架,有一个顶层事务,顶层事务之下嵌套的事务被称为子事务,其控制每一个局部的变换。子树可以是嵌套事务也可以是扁平事务,叶节点只能是扁平事务,顶层事务提交后,子事务操作才生效。(mysql不支持)
  5. 分布式事务:分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。例如:从ATM上,从招行卡转钱到工行卡上。

InnoDB执行流程

ACID特性如何实现

原子性实现原理:

undo log:意为撤销或取消,以撤销作为目的,返回指定某个状态的操作。

Undo log:数据库事务开始前,会将要修改的记录存放到undo日志中,当数据库回滚或者数据库崩溃时,可以利用undo日志,撤销未提交事务对数据库产生的影响。

undo log产生和销毁:在事务开始前产生;事务在提交事务时,并不会立即删除undo log,innodb会将该事务对应的undo log放入到删除列表中,后面会通过后台线程purge thread进行回收处理。undo log属于一个逻辑日志,记录一个变化过程。

例如:我们执行一个删除语句:delete from user where id=1时,undo会记录一条插入(insert)语句,反向操作语句。

undo log作用:

  1. 实现事务的原子性:
  2. 实现多版本并发控制(MVCC):

MVCC:

快照读:

不加锁的select,基于mvcc,避免了加锁操作,降低了开销,读到的数据可能不是最新版本。

当前读:

读取的纪录是最新版本,读取时不能修改当前记录。

undo版本链:

undo log 版本链是基于 undo log 实现的。undo log 中主要保存了数据的基本信息,比如说日志开始的位置、结束的位置,主键的长度、表id,日志编号、日志类型。

此外,undo log 还包含两个隐藏字段 trx_id 和 roll_pointer。trx_id (执行sql语句时才获取ID值)表示当前这个事务的 id,MySQL 会为每个事务分配一个 id,这个 id 是递增的。roll_pointer 是一个指针,指向这个事务之前的 undo log。

ReadView 机制

ReadView(执行select语句时才获取列表,没提交前,获取的一直都是第一次select时获取到的列表) 其实就是一个保存事务ID的list列表。记录的是本事务执行时,MySQL还有哪些事务在执行,且还没有提交。(当前系统中还有哪些活跃的读写事务)

它主要包含这样几部分:

  1. m_ids,当前有哪些事务正在执行,且还没有提交,这些事务的 id 就会存在这里;
  2. min_trx_id,是指 m_ids 里最小的值;
  3. max_trx_id,是指下一个要生成的事务 id。下一个要生成的事务 id 肯定比现在所有事务的 id 都大;
  4. creator_trx_id,每开启一个事务都会生成一个 ReadView,而 creator_trx_id 就是这个开启的事务的 id。

 如下对account表的操作(隔离级别为可重复读):

 

为什么在第三次查找的时候任然找到的是事务id为300的数据,现实的是lilei300 呢?这是因为我们的select语句一直获取的都是最开始的ReadView,,我们的select语句从begin开始就没提交过,根据事务隔离级别可重复读,此时读取不到事务id100提交的数据。但是此时数据库的值为lilei2。(幻读,加锁处理,后面说)

持久性实现原理:

Buffer pool:

访问数据库的缓冲区(个人认为,所有的缓冲区都是为了减少IO)。当操作数据时,先看缓冲区中有没有,没有就从磁盘中读取包含该数据的Page。后面对数据的操作都是在缓冲区中操作。IO线程会定期将缓冲区的数据刷入到磁盘中。(详细流程可见上面InnoDB流程图)

问题:当缓冲区中数据还没来得及完全刷入到磁盘中,Mysql宕机了,此时如何确保一致性?

redo log

当缓冲区中的数据发生修改时,redo log会按顺序记录修改的操作顺序。默认情况下,当事务提交时,会将redo log写到本地磁盘。

mysql宕机时:会读取redo log磁盘文件,将数据重新加载buffer pool中,确保了持久性实现。

隔离性实现原理(锁+MVVC)

mvvc:读不加锁

锁:共享锁(读锁)

四种隔离级别

mysql默认隔离级别:可重复读,但是在里面解决了幻读的问题。

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读不可能不可能可能
串行化不可能不可能不可能

脏读:

当前事务(A)中可以读到其他事务(B)中未提交的数据。

不可重复读(重点针对于修改操作:update):

事务A先读取一个记录,在A提交事务之前,事务B刚好对这个记录做了修改,并且提交了事务。A再次读取这个记录,读到了B更改后的记录,前后读取的数据不一致。

幻读(重点针对于删除新增操作:delete、insert):

数据A按条件查询数据库,返回若干记录数;事务B在A提交前,新增或删除了某个记录。A再按照这个条件查询,前后查询记录数不同。

InnoDB锁

  1. 表锁
    1. 读锁(排他锁):不会阻塞其他线程读操作,阻塞写操作。
    2. 写锁(共享锁):读写操作都阻塞。
  2. 行锁:基于索引加载,锁加在索引响应的行上(一行或多行),被锁定的行(记录)不能被访问,其他的行(记录可以访问)。

InnoDB默认采用行级锁。

InnoDB加锁模式:

  1. 记录锁(Record Locks):单个行记录上锁。
  2. 间隙锁(Gap Locks):锁定一个范围,但不包含记录本身。
  3. 临键锁(Next-key Locks):记录锁+间隙锁,锁定一个范围,并包含这个记录本身。

锁定一个范围,但不包含记录本身:InnoDb中数据的存储是基于B+树,每条记录都在一个范围内锁的就是这个范围(page),但是不包含查询条件本身的这个记录。

读未提交和读已提交:

都通过记录锁实现了行锁,但在MVCC中,读未提交允许读取未提交数据,所以存在脏读;读已提交允许读取提交数据,所以不存在脏读问题,但存在不可重复读问题。

可重复读:

使用临键锁实现了行锁,在MVCC中不允许读取已提交的数据,解决了不可重复读的问题。因为间隙锁会锁定一个范围,避免了在范围内的增,删操作,因此也解决了幻读的问题。

结束


死锁

可重复读中可能会出现死锁,A等B、B等A,AB-BA死锁。

解决方法:

  1. 超时机制:当两个事务相互等待时,当一个等待时间超过阈值,其中一个进行回滚,另一个就能运行了。
  2. 等待图(InnoDB采用的方式):比起超时,这是一个更为主动的死锁检测机制,在每个事务请求锁并发并且发生等待时都会判断是否存在回路,此时会选择undo量最小的事务进行回滚。(等待图:锁的信息链表和事务等待链表所构成的图)

 

 

 

 

 

 

举报

相关推荐

0 条评论