Pwner's Blog

能全力以赴不尽力而为

0%

MySQL 基础 (5)- 事务处理与并发控制

事务

概念

用户定义的一个不可分割的数据库操作序列。这些操作要么全做,要么不做。

一个事务可以是一条 SQL 语句,可以实一组 SQL 语句,也可以是整个程序。事务的开始和结束可以由用户显式控制,若用户未显式定义事务,则由 DBMS 按默认规定自动划分事务。

事务是恢复和并发控制的基本单位。

事务的定义

事务的开始

1
BEGIN TRANSACTION; /*事务开始*/

事务的结束

提交或者回滚。

1
2
COMMIT;   /*奖事务中的所有对数据库的更新写回到磁盘上的物理数据库中*/
ROLLBACK; /*系统将所有已完成的操作全部撤销,回滚到事务开始时的状态*/

ACID 属性

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持续性(Durability)/ 永久性(Permanence)

其中一致性原子性密切相关,一般情况下,事务的原子性不正常(事务被迫中断),就会导致不一致性状态。

隔离性是指一个事务的执行不能被其他食物干扰。即一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个十五之间不能相互干扰。

事务管理

保证事务的 ACID 属性是事务管理的重要任务。

事务 ACID 属性可能遭到破坏的因素:

  • 多个事务并行运行时,不同事务的操作交叉进行
  • 事务在运行过程中被强行停止

并发控制

背景

多处理机系统中,每个处理及可以运行一个事务,多个处理及可以同时运行多个事务,实现多个事务真正的并行运行。这种并行执行方式称为同时并发运行(simultaneous concurrency)。并发操作可能会破坏数据的不一致性:

  • 丢失修改(lost update)

  • 不可重复读(non-repeatable read)

其中,不可重复读包括并发事务的修改删除插入操作。事务 T2T1 读取某些数据库后删除或者插入一些记录,导致 T1 重复读取数据发现数据不一致,也成为幻影(phantom row)现象

  • 读脏数据(dirty read)

产生上述三类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发操作就是要用正确的方式调度并发操作,是一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。当然,为了加强数据的一致性而调度并发操作,通常会增大系统的开销,最好就是做到一致性要求内系统开销最小。

技术

并发控制的主要技术有:

  • 封锁(locking)
  • 时间戳(timestamp)
  • 乐观控制法(optimistic scheduler)
  • 多版本并发控制(multi-version concurrency control, MVCC)

封锁

锁的种类

  • 排他锁(exclusive locks),简称 X锁,即写锁
  • 共享锁(share locks),简称 S锁,即读锁

封锁类型的相容矩阵

封锁协议

不同级别的封锁协议的一致性保证

使用封锁机制解决三种数据不一致性的示例

上述三级协议的主要区别在于什么操作需要申请封锁,以及何时释放锁(即持锁时间)。不同的封锁协议使事务达到的一致性级别是不同的,封锁协议级别越高,一致性程度越高。

活锁和死锁

活锁死锁示例

活锁

如果事务 T1 封锁了数据 R,事务 T2 又请求封锁 R,于是 T2 等待;T3 也请求封锁 R,当 T1 释放了 R 上的封锁之后系统首先批准了 T3 的请求,T2 仍然等待;然后 T4 又请求封锁 R,当 T3 释放了 R 上的封锁之后系统又批准了 T4 的请求……T2 有可能永远等待,形成活锁。

解决活锁的简单方法

先来先服务

死锁

如果事务 T1 封锁了数据 R1, T2 封锁了数据 R2,然后 T1 又请求封锁 R2,因 T2 已封锁了 R2,于是 T1 等待 T2 释放 R2 上的锁;接着 T2 又申请封锁 R1,因 T1 已封锁了 R1,T2 也能等待 T1 释放 R1 上的锁。这样就出现了 T1 在等待 T2,而 T2 又在等待 T1 的局面,T1T2 两个事务永远不能结束,形成死锁。

解决死锁的简单方法

1. 预防

  • 一次封锁法:要求每个事务必须一次将所有要使用的数据全部加锁(扩大封锁范围会降低系统并发度)
  • 顺序封锁法:预先对数据对象规定一个封锁顺序(数据随时变化,维护难度大;事先规定的顺序难以实际施加封锁)

2. 诊断与解除

  • 超时法:以超时为标志判定死锁(容易误判;时限设置太长不容易发现思索)
  • 等待图法:并发控制子系统周期性生成十五等待图,并进行检测,如果图中存在回路则表示出现死锁。

通常选择一个处理死锁代价最小的事务将其撤销,释放此事务的所有锁,使得其他事务得以继续运行下去,同时对撤销的事务所执行的数据修改操作必须加以恢复。

如果文章对你有用,可以请我喝杯咖啡~
  • 本文作者: Pwner
  • 本文链接: https://pwner.cn/posts/fc6759ab.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!