MySQL事务

  • 四大特性
    • 原子性:事务中包含的各操作要么都做,要么都不做
    • 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。
    • 隔离性:一个事务的执行不能其它事务干扰
    • 持久性:事务一旦提交,它对数据库中的数据的改变就应该是永久性的
  • SQL 标准的事务隔离级别包括
    • 读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read,默认的隔离级别)和串行化(serializable )
  • 隔离级别操作
    • 查看当前会话隔离级别
      • select @@tx_isolation;
    • 查看系统当前隔离级别
      • select @@global.tx_isolation;
    • 设置当前会话隔离级别
      • set session transaction isolatin level repeatable read;
    • 设置系统当前隔离级别
      • set global transaction isolation level repeatable read;
  • 事务
    • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
    • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
    • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
    • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

分析

事务A 事务B
启动事务查询得到值1 启动事务
查询得到值1
将1改为2
查询得到值v1
提交事务
查询得到值v2
提交事务A
查询得到值v3
  • 若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
  • 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
  • 若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
  • 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。
  • “快照”在 MVCC 里是怎么工作的?参考
    InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。
    而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。
  • MVCC
    每一个版本的数据行都具有一个唯一的时间戳,当有读事务请求时,数据库程序会直接从多个版本的数据项中具有最大时间戳的返回。
    更新操作就稍微有些复杂了,事务会先读取最新版本的数据计算出数据更新后的结果,然后创建一个新版本的数据,新数据的时间戳是目前数据行的最大版本 +1:

  • 事务的启动方式

    • 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
  • 脏读
    • 当数据库中一个事务A正在修改一个数据但是还未提交或者回滚,另一个事务B 来读取了修改后的内容并且使用了,之后事务A提交了,此时就引起了脏读。此情况仅会发生在: 读未提交的的隔离级别
  • 不可重复读
    • 在一个事务A中多次操作数据,在事务操作过程中(未最终提交),事务B也才做了处理,并且该值发生了改变,这时候就会导致A在事务操作的时候,发现数据与第一次不一样了。 就是不可重复读。此情况仅会发生在:读未提交、读提交的隔离级别.
  • 幻读
    • 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。此情况会回发生在:读未提交、读提交、可重复读的隔离级别


事务的两阶段提交

指的就是在事务提交的时候,分成 prepare 和 commit 两个阶段。redo log 先 prepare 完成,再写 binlog,最后才进入 redo log commit 阶段。

为什么事务的隔离级别是RR

在MySQL的早期版本(大概是5.1)中,binlog的默认格式是语句格式。这时候如果启用了RC的隔离级别,binlog记录的顺序可能与实际不一致。所以系统做了一个判断,如果隔离级别为RC,则binlog格式必须要是Mix或者row。

性能,问题是有可能出现幻读,mysql联合使用next-key locking解决幻读。

RR为了实现真正的“可重复读”,引入了间隙锁(Gap lock),不仅锁住了行,而且锁住了行与行之间的间隙,避免出现幻读。