我的项目准备(数据库篇)

参考何人听我楚狂声的代码,深入理解数据库知识,顺便作为自己项目的准备。

什么是事务

Transaction是关系型数据库的核心组成,它将数据有条理地保存在储存介质(磁盘)中,
并在逻辑上,将数据以结构化的形态呈现给用户。支持数据的增、删、改、查,并在过程中保障数据的正确且可靠。
为了保证数据正确可靠,对应的拥有四大特性:

  • 原子性(Atomicity): 事务要么全部完成,要么全部取消。 如果事务崩溃,状态回到事务之前(事务回滚)。
  • 隔离性(Isolation): 如果2个事务 T1 和 T2 同时运行,事务 T1 和 T2 最终的结果是相同的,不管 T1和T2谁先结束。
  • 持久性(Durability): 一旦事务提交,不管发生什么(崩溃或者出错),数据要保存在数据库中。
  • 一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)才能写入数据库。

如何保证原子性

begin; -- 开始一个事务
update table set A = A - 1亿; -- 伪sql,仅作示意
update table set B = B + 1亿;
-- 其他读写操作
commit; -- 提交事务

要保证上面操作的原子性, 就得等begin和commit之间的操作全部成功完成后,才将结果统一提交给数据库保存,如果途中任意一个操作失败,就撤销前面的操作,且操作不会提交数据库保存,这样就保证了同生共死。

如何保证隔离性

引入数据的隔离机制,确保同时只能有一个事务在修改A,一个修改完了,另一个才来修改。 这需要对数据A加上互斥锁。在事务中更新某条数据获得的互斥锁,只有在事务提交或失败之后才会释放,在此之前,其他事务是只能读,不能写这条数据的。

这也就引出了隔离性的强度,四大级别。(不展开了)

如何保证持久性

如果在事务提交后,事务的数据还没有真正落到磁盘上,此时数据库奔溃了,事务对应的数据会不会丢?

事务会保证数据不会丢,当数据库因不可抗拒的原因奔溃后重启,它会保证:

  • 成功提交的事务,数据会保存到磁盘
  • 未提交的事务,相应的数据会回滚

事务日志

数据库通过事务日志来达到这个目标。 事务的每一个操作(增/删/改)产生一条日志,内容组成大概如下:

  • LSN:一个按时间顺序分配的唯一日志序列号,靠后的操作的LSN比靠前的大。
  • TransID:产生操作的事务ID。
  • PageID:被修改的数据在磁盘上的位置,数据以页为单位存储。
  • PrevLSN:同一个事务产生的上一条日志记录的指针。
  • UNDO:取消本次操作的方法,按照此方法回滚。
  • REDO:重复本次操作的方法,如有必要,重复此方法保证操作成功。

磁盘上每个页(保存数据的,不是保存日志的)都记录着最后一个修改该数据操作的LSN。数据库会通过解析事务日志,将修改真正落到磁盘上(写盘),随后清理事务日志(正常情况下)。

这也是数据库在保证数据安全和性能这两个点之前的折中办法:

  • 如果每次更新都写盘,由于数据是随机的,会造成大量的随机IO,性能会非常差
  • 如果每次更新不马上写盘,那一旦数据库崩溃,数据就会丢失

折中的办法就是:

  • 将数据的变更以事务日志的方式,按照时间先后追加到日志缓冲区,由特定算法写入事务日志,这是顺序IO,性能较好
  • 通过数据管理器解析事务日志,由特定的算法择机进行写盘

数据库恢复

当数据库从崩溃中恢复时,会有以下几个步骤:

  • 解析存在的事务日志,分析哪些事务需要回滚,哪些需要写盘(还没来得及写盘,数据库就崩溃了)。
  • Redo,进行写盘。检测对应数据所在数据页的LSN,如果数据页的LSN>=事务操作的LSN,说明已经写过盘,不然进行写盘操作。
  • Undo, 按照LSN倒序进行回滚
    经过这几个阶段,在数据库恢复后,可以达到奔溃前的状态,也保证了数据的一致性。

XID

XID是什么

MySQL Binlog 文件由 event 组成,event 有不同的类型,而XID_EVENT 表示一个事务的提交操作。

当事务提交时,在 binlog 依赖的内部 XA 中,额外添加了 Xid 结构,binlog 有多种数据类型:

  • statement 格式,记录为基本语句,包含 Commit
  • row 格式,记录为基于行
  • mixed 格式,日志记录使用混合格式

不论是 statement 还是 row 格式,binlog 都会添加一个 XID_EVENT 作为事务的结束,该事件记录了事务的 ID 也就是 Xid,在 MySQL 进行崩溃恢复时根据 binlog 中提交的情况来决定如何恢复。

XID如何生成

MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。

XID是唯一的吗

global_query_id 是一个纯内存变量,重启之后就清零了。所以在同一个数据库实例中,不同事务的 Xid 也是有可能相同的。

但是 MySQL 重启之后会重新生成新的 binlog 文件,这就保证了,同一个 binlog 文件里,Xid 一定是惟一的。

虽然 MySQL 重启不会导致同一个 binlog 里面出现两个相同的 Xid,但是如果 global_query_id 达到上限后,就会继续从 0 开始计数。从理论上讲,还是就会出现同一个 binlog 里面出现相同 Xid 的场景。

你可能感兴趣的:(我的面试准备,我的项目准备,数据库,mysql,面试)