mysql事务小记

文章目录


参考:

Mysql事务隔离级别详细解析
数据库事务与Mysql事务总结
Mysql锁总结
Mysql中的连接、实例、会话

Mysql中的实例(instance)、会话(session)、数据库与线程之间的关系

  • Mysql是单进程多线程的,Mysql实例在系统上表现就是一个服务进程

  • 一般情况下一个实例操作一个或者多个数据库,在集群情况下多个实例操作一个或者多个数据库

  • 一个实例是由线程和内存组成的

  • 在操作系统概念里,会话就是线程

  • mysql中建立一个会话是指和某个实例建立会话,一个会话可以操作一个实例上的多个数据库

  • 一个连接是指一个客户端和专有服务器的网络连接

  • 创建一个连接实际上是在某个实例中创建一个或者多个线程

  • 一个连接可以没有会话也可以有多个会话

  • 会话与事务的关系:一个会话可以创建多个事务,一个事务只能由一个会话产生,一个事务可能会产生一个或者多个线程,一个线程在同一时间只能执行一个事务

事务特点

注:Mysql事务都是指在InnoDB引擎下,MyISAM引擎不支持事务。

事务的四个特性:ACID

原子性(Atomicity):一个事务中的操作要么全部成功提交,要么全部失败回滚,不能只执行一部分。

一致性(Consistency):数据库总是从一个一致性状态转移到另一个一致性状态。

隔离性(Isolation):通常来说,一个事务中的操作在提交之前对其他事务是不可见的。

持久性(Durability):一旦事务提交,其所做的更改将会永久保存在数据库中,即使数据库崩溃也不会消失。

事务的隔离级别

并发事务带来的问题

  • 更新丢失当多个事务选择同一行,基于最初选定的值更新改行的时候,每个事务不知道彼此的存在,最后的更新会覆盖之前的更新,也就是更新丢失问题解决方法:使得一个事务处理这一行的时候其他事务等待

  • 脏读读到了其他事务未提交的操作

  • 不可重复读(针对更新操作)一个事务两次对同一个数据读取到的值不同,也就是说其他事务提交的修改对该事务是可见的

  • 幻读(针对插入操作)假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

事务隔离级别

以下隔离级别从上到下隔离度逐渐变强,并发度逐渐变弱。

  • 读未提交(READ CUNCOMMITTED)

  • 读已提交(READ COMMITTED)

  • 可重复读(REPEATABLE READ)Mysql的默认隔离级别

  • 串行化(SERIALIZABLE)

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

多版本并发控制(MVCC)

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现。这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然存储的并不是真实的时间而是系统版本号(system version number)。

MVCC只工作在读已提交和可重复读两种隔离级别下。他是行级锁的一个变种,很多情况下避免了加锁操作。

可重复读下MVCC的工作方式

select:只查找版本号早于当前事务版本的数据行,这样可以保证事务读取的行要么是在开始事务之前存在的,要么是当前事务修改的;行的删除版本号要么未定义,要么大于当前版本号,这样可以确保事务读取到的行在事务开始之前没有被删除。

insert:为插入的每一行保存当前系统版本号作为行版本号。

delete:为删除的每一行保存当前系统版本号为删除版本号。

update:为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识。

可重复读和读已提交在MVCC工作方式上的不同

二者最主要的区别是在快照的创建上,可重复读只在事务开始时创建一次,而读已提交在每次执行语句的时候都创建一次。

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

  1. 当前事务内的更新,可以读到;

  2. 版本未提交,不能读到;

  3. 版本已提交,但是却在快照创建后提交的,不能读到;

  4. 版本已提交,且是在快照创建前提交的,可以读到;

事务日志及事务实现原理

  • 使用事务日志使得存储引擎在修改表的时候只需要修改其内存拷贝,再把该修改行为记录到硬盘的事务日志上,而不用每次都将修改的数据本身持久化到磁盘。

  • 事务日志采用追加的方式,写日志的操作时磁盘上一小块区域内的顺序IO,而不是随机IO,所以采用事务日志的方式相对来说更快。

  • 事务日志持久之后,内存中的数据会慢慢刷到磁盘。

  • 如果数据的修改以及记录到事务日志并持久化,但是数据本身没有写回到磁盘,此时系统崩溃,存储引擎在重启的时候可以自动恢复这一部分修改的日志。

mysql的事务实现原理

事务的原子性、一致性和持久性则是通过事务日志实现。说到事务日志,不得不说的就是redo和undo。

  • redolog

在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。

在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。所有的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起。如下一个简单示例:

记录1:<trx1, insert…>

记录2:<trx2, delete…>

记录3:<trx3, update…>

记录4:<trx1, update…>

记录5:<trx3, insert…>

  • undolog

undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

以下是undo+redo事务的简化过程

假设有2个数值,分别为A和B,值为1,2

  1. start transaction;

  2. 记录 A=1 到undo log;

  3. update A = 3;

  4. 记录 A=3 到redo log;

  5. 记录 B=2 到undo log;

  6. update B = 4;

  7. 记录B = 4 到redo log;

  8. 将redo log刷新到磁盘

  9. commit

在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。

redolog保障了事务的持久型和一致性,undolog保障了事务的原子性

事务的使用

设置隔离级别

设置隔离级别的语句:set [作用域] transaction isolation level [事务隔离级别],SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

其中作用域可以是当前session也可以是global全局的例如设置全局隔离级别为读已提交:

mysql> set global transaction isolation level read committed

事务的开始与提交

begin不代表事务的开始,事务开始于begin命令之后第一条语句执行的时候,以下语句中select * from xxx才是事务的开始

begin;
select * from xxx; 
commit; -- 或者 rollback;

可以查询当前有多少事务在运行

select * from information_schema.innodb_trx;
上一篇:Windows磁盘大小分区(NTFS、FAT32)


下一篇:redo丢失恢复