参考何人听我楚狂声的代码,深入理解数据库知识,顺便作为自己项目的准备。
Transaction是关系型数据库的核心组成,它将数据有条理地保存在储存介质(磁盘)中,
并在逻辑上,将数据以结构化的形态呈现给用户。支持数据的增、删、改、查,并在过程中保障数据的正确且可靠。
为了保证数据正确可靠,对应的拥有四大特性:
begin; -- 开始一个事务
update table set A = A - 1亿; -- 伪sql,仅作示意
update table set B = B + 1亿;
-- 其他读写操作
commit; -- 提交事务
要保证上面操作的原子性, 就得等begin和commit之间的操作全部成功完成后,才将结果统一提交给数据库保存,如果途中任意一个操作失败,就撤销前面的操作,且操作不会提交数据库保存,这样就保证了同生共死。
引入数据的隔离机制,确保同时只能有一个事务在修改A,一个修改完了,另一个才来修改。 这需要对数据A加上互斥锁。在事务中更新某条数据获得的互斥锁,只有在事务提交或失败之后才会释放,在此之前,其他事务是只能读,不能写这条数据的。
这也就引出了隔离性的强度,四大级别。(不展开了)
如果在事务提交后,事务的数据还没有真正落到磁盘上,此时数据库奔溃了,事务对应的数据会不会丢?
事务会保证数据不会丢,当数据库因不可抗拒的原因奔溃后重启,它会保证:
数据库通过事务日志来达到这个目标。 事务的每一个操作(增/删/改)产生一条日志,内容组成大概如下:
磁盘上每个页(保存数据的,不是保存日志的)都记录着最后一个修改该数据操作的LSN。数据库会通过解析事务日志,将修改真正落到磁盘上(写盘),随后清理事务日志(正常情况下)。
这也是数据库在保证数据安全和性能这两个点之前的折中办法:
折中的办法就是:
当数据库从崩溃中恢复时,会有以下几个步骤:
MySQL Binlog 文件由 event 组成,event 有不同的类型,而XID_EVENT 表示一个事务的提交操作。
当事务提交时,在 binlog 依赖的内部 XA 中,额外添加了 Xid 结构,binlog 有多种数据类型:
不论是 statement 还是 row 格式,binlog 都会添加一个 XID_EVENT 作为事务的结束,该事件记录了事务的 ID 也就是 Xid,在 MySQL 进行崩溃恢复时根据 binlog 中提交的情况来决定如何恢复。
MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。
global_query_id 是一个纯内存变量,重启之后就清零了。所以在同一个数据库实例中,不同事务的 Xid 也是有可能相同的。
但是 MySQL 重启之后会重新生成新的 binlog 文件,这就保证了,同一个 binlog 文件里,Xid 一定是惟一的。
虽然 MySQL 重启不会导致同一个 binlog 里面出现两个相同的 Xid,但是如果 global_query_id 达到上限后,就会继续从 0 开始计数。从理论上讲,还是就会出现同一个 binlog 里面出现相同 Xid 的场景。