事务

事务

一个不可分割的数据库操作序列,是数据库并发控制的基本单位。

事务是逻辑上的一组操作,要么都执行,要么都不执行。

ACID

  • atomicity(原子性) :要么全执行,要么全都不执行;
  • consistency(一致性):在事务开始和完成时,数据都必须保持一致状态;
  • isolation(隔离性) :事务处理过程中的中间状态对外部是不可见的;
  • durability(持久性) :事务完成之后,它对于数据的修改是永久性的。

并发事务可能存在的问题

  • 脏读:读取未提交的事务。
  • 不可重复读:多次读取同一数据,读取的数据不一致。
  • 幻读:幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

事务隔离级别

  • READ-UNCOMMITTED:一个事务还没提交时,它做的变更就能被别的事务看到。
  • READ-COMMITTED:一个事务提交之后,它做的变更才会被其他事务看到。
  • REPEATABLE-READ:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
  • SERIALIZABLE:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

事务隔离的实现

数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。

  • 在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。

  • 在“读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的。

  • “读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;

  • “串行化”隔离级别下直接用加锁的方式来避免并行访问。

事务可见性分析(RR)

事务启动瞬间,当前正在“活跃”的所有事务ID的最小值记为低水位,最大值加1记为高水位

  • row trx_id<低水位:这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
  • 低水位<row trx_id<高水位:
    • 在活跃数组中,表示这个版本是由还没提交的事务生成的,不可见;
    • 不在活跃数组中,表示这个版本是已经提交了的事务生成的,可见。
  • row trx_id>高水位:由将来启动的事务生成的,是肯定不可见的;

也可以从事务启动时间来看:

  1. 版本未提交,不可见;
  2. 版本已提交,但是是在视图创建后提交的,不可见;
  3. 版本已提交,而且是在视图创建前提交的,可见。

更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)

select * from t where id=1加上lock in share mode 或for update,也是当前读。

Redo log

Redo log称为重做日志,用于记录事务操作变化,记录的是数据被修改之后的值。

Redo log 由两部分组成:

  • 内存中的重做日志缓冲(redo log buffer)
  • 重做日志文件(redo log file)

每次数据更新会先更新 redo log buffer,然后根据 innodb_flush_log_at_trx_commit 来控制 redo log buffer 更新到redo log file 的时机。innodb_flush_log_at_trx_commit 有三个值可选:

  • 0:表示每次事务提交时都只是把redo log留在redo log buffer中,每隔一秒把log buffer刷到文件系统中去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。
  • 1(默认值):表示每次事务提交时都将redo log刷到文件系统中,并flush到磁盘;
  • 2:表示每次事务提交时都只是把redo log刷到文件系统,但不flush到磁盘。

除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的redo log写入到磁盘中。

  • redo log buffer占用的空间即将达到 innodb_log_buffer_size一半的时候,后台线程会主动写盘。
  • 并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘。

Binlog

二进制日志(binlog)记录了所有的 DDL(数据定义语句)和 DML(数据操纵语句)

Binlog 有以下几个作用:

  • 恢复:数据恢复时可以使用二进制日志
  • 复制:通过传输二进制日志到从库,然后进行恢复,以实现主从同步
  • 审计:可以通过二进制日志进行审计数据的变更操作

sync_binlog 来控制累积多少个事务后才将二进制日志 fsync 到磁盘。

  • sync_binlog=0,表示每次提交事务都只write,不fsync。文件系统决定什么时候fsync
  • sync_binlog=1,表示每次提交事务都会执行fsync
  • sync_binlog=N,表示每次提交事务都write,累积N个事务后才fsync

binlog格式

  • statement:binlog里面记录的就是SQL语句的原文。可能会导致主备不一致。不太推荐使用
  • row:binlog里面记录了真实删除行的主键id,不会有主备删除不同行的问题。缺点是很占空间。优点利于恢复数据。
  • mixed格:MySQL自己判断SQL语句是否可能引起主备不一致,是就用row格式,否则就用statement格式。

redolog和binlog区别

  • redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。

  • redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。

  • redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

数据库突然断电不丢数据

只要 innodb_flush_log_at_trx_commit 和 sync_binlog 都为 1(通常称为:双一),就能确保MySQL 机器断电重启后,数据不丢失。

事务建议

  • 循环写入的情况,如果循环次数不是太多,建议在循环前开启一个事务,循环结束后统一提交。
  • 优化事务里的语句顺序,减少锁时间。
  • 关注不同事务访问资源的顺序,避免死锁。
  • 创建事务之前,关注事务隔离级别。
  • 不在事务中混合使用存储引擎(MyISAM无法回滚)

分布式事务

分布式事务使用两阶段提交协议:
第一阶段:所有分支事务都开始准备,告诉事务管理器自己已经准备好了;
第二阶段:确定是 rollback 还是 commit,如果有一个节点不能提交,则所有节点都要回滚。

MySQL 自带的分布式事务

xa start 'a','a_1'; //启动分支事务
xa end 'a','a_1'; //结束分支事务
xa prepare 'a','a_1'; //进入准备状态
xa commit 'a','a_1';  //提交分支事务
xa recover;  //返回当前数据库中处于 prepare 状态的分支事务的详细信息

   转载规则


《事务》 wangyixin-tom 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
MVCC MVCC
MVCC多版本并发控制。 MVCC 只在 RC 和 RR 两个隔离级别下工作。 不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。 Undo logundo
2021-03-14
下一篇 
锁
锁就是协调多个用户或者客户端并发访问某一资源的机制,保证数据并发访问时的一致性和有效性。 全局锁MySQL 全局锁会关闭所有打开的表,并使用全局读锁锁定所有表。 FLUSH TABLES WITH READ LOCK; UNLOCK TAB
2021-03-14
  目录