MySql教程

MySQL事务

本文主要是介绍MySQL事务,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一.什么是MySQL事务?

start transaction
要执行的sql语句
commit

一个事务包含多个sql语句,事务保证这些sql语句要么都执行成功,要不都失败

二.事务四大特性

A - 原子性
C - 一致性
I - 隔离性
D - 持久性

三.事务特性的底层实现原理

1.原子性: 一个事务是最小的操作单元,不可分割,,事务中事务sql要么全成功,,要么都失败

其原理体现在(有一个sql失败,会执行rollback回滚,回滚到事务之前的状态):Undo log实现
当开始事务后,innodb会把每条sql的执行信息记录在undolog中,如果有sql执行失败,innodb会根据undolog中记录的内容去执行相反的操作:
原来insert,,那我就delete
原来delete,那我就insert
原来update,,那我就操作相反的update,把数据再改回去
所以,,,Undo log日志实现了事务的原子性

2.持久性: 事务一旦提交成功,则对数据库的更改是永久性的

通过redo log实现
mysql的数据是存在磁盘中,每次读取需要通过io,效率很低, 所以innodb提供一个缓存buffer,这个缓存buffer中包含磁盘数据页的映射
当从数据库读数据时,会先从buffer中取, buffer中如果没有,再去磁盘读,读到后会放到buffer
当向数据库写数据时,也是先写到buffer中,,然后定期将buffer中数据刷新到磁盘上,,做一个持久化操作

这样会有一个问题: buffer的数据还没有同步到磁盘,,此时mysql宕机了, 这样buffer中数据就丢失了,,这就没法保证持久性了?
redo log是一个预写式日志: 数据库的每次操作,,他会先写到redo log中,,在更新到buffer中
redo log也是把日志写到磁盘,,它比直接将buffer数据写到磁盘快的原因:
1).buffer的持久化是随机写的io(每次修改的数据位置是随机的),redo log的持久化是追加模式(在文件尾部追加,属于顺序io操作)
2).buffer的持久化是以数据页page为单位,mysql默认的配置页是16k,,一个数据页上小小的一处修改都需要把整个数据页写入,,而redo log 只需要写入真正修改的部分, 这样无效的io就大大减少了

redo log 持久性3种策略:
0-表示当提交事务时,,并不将redolog缓冲区的日志写入磁盘文件,,而是等待主线程每秒刷新
1-在事务提交时将edolog缓冲区的日志同步写入磁盘,,保证一定会写入成功(推荐)
2-在事务提交时将edolog缓冲区的日志异步写入磁盘,,即不能完全保证commit时肯定会写入redolog日志

3.隔离性: 事务之间相互不影响

1)数据库存在的并发操作使用的事务控制

mysql事务的隔离性是通过mvcc+锁来实现控制的...

数据库并发场景有三种,分别为:

  读-读:不存在任何问题,也不需要并发控制
  读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读...MVCC控制
  写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失...锁控制

2)锁的概念:

事务在修改数据之前,这个事务要获取相应的锁,只有拿到锁才能操作数据.....
如果其他事务想操作这个数据,必须等当前事务提交或回滚后释放锁以后, 再去竞争这个锁, 拿到锁后才能执行其自身事务

innodb下有行锁(默认, 但需要索引触发), 表锁, 间隙锁(范围概念,,锁多行)
myisam默认支持表锁

3)mvcc概念:

mvcc: 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制, 主要是为了提高数据库的读写并发性能.
mvcc可以更好的处理读请求,做到发生读写请求冲突时不用加锁...这里的读指的是快照读,,而不是当前读(当前读是一种加锁操作,,是悲观锁)...

4)啥是当前读?

它读取的数据库记录是当前最新的版本,,,会对当前读取的数据进行加锁,,,防止其他事务修改数据,,典型的悲观锁操作

以下操作触发当前读:
select * from .....lock in share mode(共享锁)
select * from .....for update(排它锁)
update(排它锁)
insert(排它锁)
delete(排它锁)
串行化事务隔离级别(串行化指的就是事务一个接一个执行,类似于单线程)

这里解释下数据库的共享锁和排它锁:
共享锁也叫读锁, 是指读可以并发,但是写会阻塞
排它锁也叫写锁, 无论读写都会阻塞

5)啥是快照读?

innodb下普通的select语句都是快照读
它读取的版本可能不是最新版本, 可能是历史版本...
快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读

说白了MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读

6)详解mvcc快照读

先说几个概念:
我们在数据库表中看到的一行记录实际上有多个版本,每个版本的记录除了有数据本身外,包含事务id trx_id(自增的) 和回滚指针roll_pointer, 如此记录的多个版本之间构成版本链, 每个版本记录通过回滚指针指向上一个版本记

  ReadView(快照读对象)实现快照读:它让你知道版本链中哪个版本是可用的
  ReadView4个属性:
  m_ids: 生成ReadView时当前系统中未提交的事务id列表,,
  min_trx_id: 最小的事务id,
  max_trx_id: 下一个需要分配的事务id,,
  creator_trx_id: 生成该ReadView的事务id

对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:

当前事务内的更新,可以读到;
版本未提交,不能读到;
版本已提交,但是却在快照创建后提交的,不能读到;
版本已提交,且是在快照创建前提交的,可以读到;

7)事务四种隔离级别

读未提交, 读已提交, 可重复读, 串行化

8)总结: MySQL是如何实现事务隔离的

读未提交 它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决

读已提交 是每次执行select语句的时候都重新生成一次快照

可重复读 是在事务开始的时候生成一个当前事务全局性的快照

两者主要的区别就是在快照的创建上,可重复读仅在事务开始是创建一次,而读提交每次执行select语句的时候都要重新创建一次

串行化 是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束
读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读
InnoDB 默默的把所有纯 SELECT 语句都转成了 SELECT ... FOR SHARE ,也就默认都加读锁

特别注意:
MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的

4.一致性

事务执行前后数据是完整一致的, 它是事务的最终目的

 

这篇关于MySQL事务的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!