简单总结《改变未来的九大算法》(四)——数据库如何实例一致性

作者:杨润炜
日期:2022/2/20 23:10

最近刚好在学习操作系统中的持久化实现,即文件系统。它与数据库的一些理念是相通的,所以将这篇算法复习一下,结合之后再总结出来。

作为一名应用开发人员,必定会接触到数据库,也一定知道数据库能够保证数据的可靠性,一致性是其中最重要的,且往往能高效地处理数据。接下来将根据《改变未来的九大算法》书中所说明的内容来进行阐述。
该书说明了数据库使用了两种方式来实现数据库的一致性:

  • 预写日志(待办事项)
  • 两阶段提交(预备提交)

预写日志(待办事项)

用过数据库的,肯定知道事务。我们知道它能保证多个操作是原子性的,事务的原子性是指事务中的操作全部只有两种状态,要不同时完成,要不同时失败。
相对预定日志,“待办事项”更能说明这一机制的工作原理。它将多个操作比作待办列表中的事项,能够记录完成状态,如果中途离开(机器宕机、数据库崩溃)也能依据该列表的信息回到之前的工作状态。

事务的某个操作中途失败怎样办?

有两种选择:

  • 一种是回滚,即回撤之前执行的操作
  • 一种是恢复,即继续执行操作

怎样实现回滚?

日志里会包含操作的数据内容,只要将操作反转一下就是回滚操作了。

怎样保证回滚时不会损坏现有数据呢?

操作前是加锁的,即在事务完成或回滚前,其它人(数据库线程、事务)是不能改动原有数据的。

事务恢复时如何处理已经执行成功的操作?

恢复时不需要关注已经执行成功的,只要从头开始执行一遍就行,因为操作已经保证了幂等性。
例如,A向B的转账200元的操作日志内容为:A的600元改为400元,B的300元改为500元。
这样就算同个操作执行了多次,对数据的影响是一样的。

万一预写日志失败怎样办?

事务不会开始,所以不会怎样。

操作系统也借鉴了数据库的预定日志机制

预定日志这种方法,能把多个操作封装为原子操作,操作系统实现文件系统时也借鉴了此机制。
例如,文件系统要写入一个文件时,会涉及到多个写入IO操作:写文件数据、更新inode位图、更新数据位图,如果不完整地执行所有操作,将产生数据损坏或数据不一致的故障。
为了让这多个写IO变成一个原子IO操作,文件系统也引入了预定日志,先将这些IO操作通过日志记录下来,数据可以放日志,也可以不放(元数据日志方法),万一因为机器崩溃导致某些操作中途失败,在机器重启后,文件系统会根据日志继续完成,以实现数据的一致性。

为什么预写日志能实现多个操作的一致性?

首先要说明预写日志也可能要多步,且可能会中途失败,所以它也要保证一致性(或者说原子操作)。
我先以文件系统为例来说明吧。实际写文件的一致性需要保证写文件数据、更新inode位图、更新数据位图,这里面涉及到inode位图、数据位图、数据块等不同磁盘区域,如果其中一个失败不仅当前写的文件损坏,且可能影响其它文件,而且不同区域的写协同成本更高。而预写日志可以固定在磁盘某区域,虽然日志数据可能也要分多步写入,但在文件系统中可以使用磁盘原子操作(写512字节)、写屏障、校验和的方式来保证预写日志的一致性。对于区域单一、较为独立的预写日志数据,要保证一致性比较简单。
数据库也是一样,如果要对某个表写入数据,首先就要定位、加锁、更新索引、写入磁盘等多个复杂IO操作,而预写日志则可以在独立的区域存储较为单一的结构,实现保证一致性较简单。

两阶段提交(预备提交)

数据库往往要求多副本多节点,甚至节点的异地多活,这时候怎样保证副本的数据复制的一致性呢?这就要求需要有个协同的机制,保证大家(所有节点)的数据相同。

约球的例子

书中用了个生活的例子来解释这一机制,十分生动形象。
故事是这样的:

  1. 周末小明想约2个朋友小智、小烈打球。
  2. 他先打电话给小智约今天下午4点半打球,小智说可以,小明说他跟小烈确认后再找他;
  3. 他打电话给小烈约今天下午4点半打球,小烈答应了,小明就跟他正式约定了。
  4. 他再打电话跟小智说确认4点半打球,最后他跟小智也正式确认了约定。

为啥约个球要两次确认?

因为跟别人协同一致的时间点。如果要约的时间别人不同意,就得取消后再改个时间重新审议。所以过程中有个“预备”的过程,即提出打球时间(预案),然后大家确认时间OK就确认(提交),如果不OK则可能要重新开始整个过程,或者取消。
(有人可能要抬杠说微信多人通话,但其实都是要有个“预备”的提案,大家都确认后,再统一“提交”约定,这样也是个两阶段提交。)

实际在数据库中是怎样的表现?

主从节点数据同步

还是以书中的例子来说明。
two-stage-commit

mysql的redo与binlog

redo和binlog都记录了数据的操作,异常中断时可以从其中一个恢复数据,即在正常动作时需要保证两者数据一致性。
mysql的具体实现时通过redo来协调,它有两种状态,prepare和commit。当redo记录完成时,redo状态为prepare,然后记录binlog,当binlog完成后,redo状态改为commit。这里的prepare和commit状态就对应了预备和提交的实现。

机制的普适性

对于跨系统间的数据一致性协同问题,两阶段提交是个常用的解决方案。

最后简单说下高效性

关系数据库、虚表都是为了数据库的高效性而设计的。

  • 关系数据库可以将不同维度的信息隔离到不同的表,并用“键”关联,使数据的存储结构设计符合现实特性、更具人性面。
  • 虚表是为查数据高效而生的,多个表用“键”关联后,可在数据库中生成虚拟的数据表,可以用于更多条件的组装和查找。

参考

敬请期待下篇 图形识别

感谢您的阅读!
如果看完后有任何疑问,欢迎拍砖。
欢迎转载,转载请注明出处:http://www.yangrunwei.com/a/125.html
邮箱:glowrypauky@gmail.com
QQ: 892413924