数据库系统原理

数据库系统原理

事务

四大特性—ACID

  • 原子性(Atomicity)
    • 事务不可再分,要么执行成功、要么失败回滚。回滚操作采用混滚日志(Undo Log)的方式实现。
  • 一致性(Consistency)
    • 事务执行前后数据库保持一致性状态,所有事务对同一个数据的读取结果都是相同的。
  • 隔离性(Isolation)
    • 某个事务所作的修改在提交以前对其他事务不可见
  • 持久性(Durability)
    • 事务一旦提交,所作的修改将永远保存到数据库中。即使系统发生崩溃,也可以通过重做日志(Redo Log)的方式恢复。

ACID的关系

  • 事务的持久化是为了应对系统崩溃造成的数据丢失
  • 只有保证了事务的一致性,才能保证执行结果的正确性
  • 非并发状态下,事务间天然保证隔离性,因此只需要保证事务的原子性即可保证一致性
  • 并发状态下,需要严格保证事务的原子性、隔离性

并发一致性问题—并发状态下如何保证隔离性

并发状态下会出现的一致性问题

  • 丢失修改—并发修改
    • 事务A所作的修改被并发事务B的修改覆盖
  • 脏读—修改时读
    • 事务A读到了并发事务B未提交的修改
  • 不可重复读—读时修改
    • 事务A读取数据期间并发事务B对数据进行了修改,导致事务A读取前后数据不同
  • 幻读
    • 事务A读取某个范围的数据期间并发事务B插入的新数据,导致事务A前后读取的该范围数据不同

事务加锁/封锁

  • 解决问题 前三种问题(丢失修改、脏读、不可重复读)是由于并发事务对同一数据进行修改导致的,可以通过对事务加锁的方式解决

  • 封锁粒度—行锁、表锁(MySQL服务器只支持表锁,只有存储引擎入InnoDB等实现了表锁)

  • 封锁原则—满足要求的情况下锁定尽可能少的数据,但更小的锁粒度也会带来更大的锁开销

  • 封锁类型—读写锁、意向锁

    • 读写锁——写锁/互斥锁/X锁、读锁/共享锁/S锁
      • 加了S锁则只能再加S锁,且只能读取数据而不能修改
      • 加了X锁则不能再加任何所,只有持有锁的事务能够进行读取、修改
    • 意向锁——再X/S锁的基础上增加了意向表锁IX/IS, 表明想要对表加X/S锁(并不是真正加锁)
      • 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁
      • 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁
      • 事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁
      • 任意 IS/IX 锁之间都是兼容的,因为它们只表示想要对表加锁,而不是真正加锁
      • 这里兼容关系针对的是表级锁,而表级的 IX 锁和行级的 X 锁兼容,两个事务可以对两个数据行加 X 锁。(事务 T1 想要对数据行 R1 加 X 锁,事务 T2 想要对同一个表的数据行 R2 加 X 锁,两个事务都需要对该表加 IX 锁,但是 IX 锁是兼容的,并且 IX 锁与行级的 X 锁也是兼容的,因此两个事务都能加锁成功,对同一个表中的两个数据行做修改。)
  • 封锁协议

    • 三级封锁协议

      • 一级封锁(解决丢失修改)
        事务修改时加X锁,直到事务结束释放

      • 二级封锁(解决脏读)
        一级封锁基础上(修改时加X锁),读取时必须加S锁,直到事务结束立即释放

      • 三级封锁(解决不可重复读)
        二级封锁基础上,读取时必须加S锁

    • 两段锁协议

      • 加锁、释放分两个阶段进行(先全部加锁、再全部释放)
      • 串行化调度 并发事务执行结果与串行执行结果一致

隔离等级

  • 事务的隔离级别
    • 读未提交(READ UNCOMMITTED)
      事务中的修改,即使没有提交,对其它事务也是可见的。

    • 读提交(READ COMMITTED)
      一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。

    • 可重复读(REPEATABLE READ)
      保证在同一个事务中多次读取同一数据的结果是一样的。

    • 可串行化(SERIALIZABLE)
      强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。
      该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行。

多版本并发控制MVCC——实现隔离等级(读提交、可重复读)的方式

  • 写操作更新最新版本的快照,读操作读旧版本快照,一个事务只能读取已提交的快照
  • MVCC 中事务的修改操作(DELETE、INSERT、UPDATE)会为数据行新增一个版本快照
  • 版本号:
    • 系统版本号SYS_ID: 是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增
    • 事务版本号TRX_ID: 事务开始时的系统版本号
  • Undo日志:
    • 每一行记录拥有一系列快照
    • 快照中包含对该行数据的操作、版本号(对该行数据进行操作的事务版本号)、该行记录的关键字
    • 同一行记录的快照使用回滚指针(ROLL_PTR)串联, 从新的版本号(大)指向旧版本号(小)
  • ReadView:
    • 记录了当前系统内所有未提交事务的列表
  • 读快照的规则:
    • 查找要读取记录的最新事务号TRX_ID
    • 与ReadView中最小事务号TRX_ID_MIN和最大事务号TRX_ID_MAX比较,若TRX_ID < TRX_ID_MIN则可以读取
    • 若TRX_ID > TRX_ID_MAX则无法读取
    • 若TRX_ID > TRX_ID_MIN 且 TRX_ID < TRX_ID_MIN则需要参考隔离级别
      • 读提交: 如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。
      • 可重复读:都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。
  • 快照读和当前读
    • 读(Select)快照
    • 写操作永远使用加锁的方式获取最新数据

Next-Key Locks

  • RecordLocks: 锁定一个记录上的索引,而不是记录本身。如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。
  • GapLocks: 锁定索引之间的间隙,但是不包含索引本身
  • Next-KeyLocks: 它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙, 它锁定一个前开后闭区间

关系数据库设计理论

ER图

原文地址:https://www.cnblogs.com/CodeSPA/p/13552683.html