爱炫耀的数据库老头儿

作者是工作15年的前IBM架构师,用自己踩过的坑,走过的弯路,分享编程和职场的经验教训。

数据库老头儿

我们这个世界很大,生活着很多人,形形色色,各怀绝技。但是被公认为最拽的一个却是数据库老头儿,年龄挺大,每天都要炫耀几遍他那关系型数据库,那理论有着多么坚实的数学基础,那关系运算是多么地优雅,事务管理是多么强大,还有他是多么地稳定,没有他怎么能活这么久等等。

老头儿有他拽的资本,因为我们这个系统的核心数据都是在老头儿那里存放着,例如用户了,订单了,交易了……我向别人打听过,这些数据已经在老头儿那里积累了20多年了,最早的时候是Dphi,PowrBuidr这些上古的软件写的系统访问,后来慢慢转到互联网,先用PHP访问,再后来变为Java。

看来最宝贵的东西是数据,外界系统可以变,但是数据不能丢,老头儿守着这份财产,生生地熬死了一代人,估计还会再熬死一代人。

事务

这一天老头儿又在那里一边喝酒,一边“吹嘘”他的事务管理,说些ACID之类我们都听不懂的外国话,和数据库经常打交道的是年轻的小伙子Tomcat,对于老头儿,他比我们多了解那么一点点。

Tomcat端了一杯啤酒来到老头儿桌前:“嗨,老头儿,我知道你说的事务有个重要的特性:原子性,就是说在一个事务中不管有多少操作,都是要么全做,要么全不做,是这样吧!”

“那是自然!”

“我很好奇,在执行的操作过程中,如果还没做完系统就崩溃了,或者断电了,你怎么办啊?你怎么保证原子性?”

听到Tomcat问了一个关键的问题,酒馆里的CPU阿甘,Ngnix,Spring,MyBatis都围了过来,都想听听老头儿的高见。

“如果我还没做完,系统就崩溃了,那系统重启以后我就得做恢复操作啊。”

“怎么恢复啊?”Tomcat穷追不舍。

“你举个例子,我给你分析分析”老头环顾四周,看到人们都围了上来,也来了一点兴致,“光讲理论,这些小朋友们怎么听得懂?”

“好啊,比如旺财有块钱,小强有50块钱,现在旺财要给小强转账,假设转块吧,你说说,你是怎么实现要么不做,要么全做的。”

开始事务T1(假设T1是个事务的内部编号)

旺财余额=旺财余额-

小强余额=小强余额+

提交事务T1

CPU阿甘插了一嘴说:“虽然这些都是我在做计算,但是这些计算结果都是先存在内存中的,内存那家伙一断电啥都忘了,如果第三步还没执行完就断电了,那不就完蛋了。”

数据库老头儿说:“虽然数据先在内存中被计算出来,但是我是要写入硬盘的数据库文件的,知道不?”

“你的数据缓冲区是啥意思?”人群里有人问道。

“老土!连数据缓冲区都不知道!我告诉你吧,那个硬盘啊速度太慢了,比内存慢个几万倍,你说我能每次操作都去写硬盘吗?绝对不可能,所以CPU阿甘计算出的数据我会放到数据缓冲区里,我会在合适的时候把数据缓冲区的内容写入硬盘的数据文件。”

“什么合适的时候?”

“那是我的缓冲管理器要做的事情了,想听吗?我再花两天给你讲讲!”

Undo日志

“算了算了,我们还是先假定数据缓冲区能和硬盘的数据文件同步吧,回到刚才的问题,旺财在给小强转账,第二步执行完了,旺财的余额变成了块(-),假设已经写入了硬盘文件,现在断电了,小强的余额有没有加上,系统的钱白白消失了块,数据已经不一致了,你怎么办?”Tomcat把话题转移回来。

“放心吧,我会在记录日志的,有个叫做Undo的日志文件,就是为了解决这个问题的”,老头儿喝了一口酒,准备开始放大招,“比如你说的情况,我会在我的日志文件中记录下事务开始之前的他俩账号余额:

[事务T1,旺财原有余额,]

[事务T1,小强原有余额,50]

如果事务执行到一半,就断电了,那数据库重启以后我就根据Undo的日志文件来恢复。”

“嗯,那要是系统恢复的过程中又断电了,还得再次恢复,那数据岂不变得一团糟?”CPU阿甘对断电心有余悸。

“对啊对啊”,周边的人附和到。

“你们这些年轻人啊,还是toosimpl,你们仔细想想,即使我把旺财的余额和小强的余额恢复了次,会有什么结果?”

“如果每次都试图把旺财的余额设为,小强余额设为50,做多少次都没问题,因为他俩原来的余额就是那么多!”Tomcat恍然大悟。

“这就叫做操作的幂等性,知道不?我可以一直做恢复,恢复过程中断电也不怕,只要把恢复做完就行。”老头儿看到时机一到,立刻上升为理论。

“恢复数据的时候,那你怎么才能知道一个事务没有完成呢?”Tomcat接着问道。

数据库老头儿对这个问题似乎很满意,专门花点时间写了几行字:

[开始事务T1]

[事务T1,旺财原有余额,]

[事务T1,小强原有余额,50]

[提交事务T1]

“Undo日志文件中不仅仅只有余额,事务的开始和结束也会记录,如果我在日志文件中看到了[提交事务T1],或者[回滚事务T1],我就知道这个事务已经结束,不用再去理会它了,更不用去恢复。如果我只看到[开始事务T1],而找不到提交或回滚,那我就得恢复。比如下面这个:”

[开始事务T1]

[事务T1,旺财原有余额,]

[事务T1,小强原有余额,50]

“特别是,”老头补充道,“我恢复以后,需要在日志文件中加上一行[回滚事务T1],这样下一次恢复我就不用再考虑T1这个事务了。”

独门绝技

“不对吧,你这个Undo日志文件会面临和数据文件一样的问题,都是需要加载到内存才能读写,要不然会太慢。那要是连日志文件还没写好就断电了,那不还是玩完?”Ngnix目光如炬,向深层次挖掘。

这是个绝佳的问题,大家纷纷把目光杀向数据库老头儿,希望这一次能把他打翻在地。

老头儿镇定自若,没有回答,反而给大家抛出一个问题:“你们想想,我什么时候应该记录Undo日志,什么时候把Undo日志写入文件呢?”

“事务一开始的时候就得把Undo日志写入文件,这样最安全!”

老头儿笑了笑说:“蠢材!一开始的时候我都不知道程序到底要操作那个字段,怎么记录Undo日志,怎么写入硬盘文件?”

“那怎么办啊?”

“这就是我的独门秘籍了,我给大家举个例子,你们可要看仔细了,我把日志记录也放到了内存的Undo日志缓冲区,伺机写入硬盘。”

“不知道你们这些小朋友看出一点门道儿没有?”老头儿问道。

“让我想想”Ngnix说,“如果系统在第4步和第5步之间崩溃,旺财的余额写入了硬盘,但是小强的还没写入,那Undo日志看起来是这样的:

[开始事务T1]

[事务T1,旺财原有余额,]

由于找不到事务结束的日志,你会进行恢复操作,把旺财的原有余额给恢复了。”

Tomcat接过来说:“如果是在第7步和第8步之间系统崩溃,旺财和小强的最新余额都写入了硬盘,但是没有提交事务,那Undo日志看起来是这样的:

[开始事务T1]

[事务T1,旺财原有余额,]

[事务T1,小强原有余额,50]

由于没有事务结束的日志,你也需要进行恢复,把旺财和小强的原有余额恢复成和50”

CPU阿甘也不甘示弱说:“如果是在第8步和第9步之间系统崩溃,旺财和小强的最新余额都写入了硬盘,也提交了事务,但是提交事务的操作没有写入Undo日志,所以Undo日志还是这样:”

[开始事务T1]

[事务T1,旺财原有余额,]

[事务T1,小强原有余额,50]

由于没有事务结束的日志,你还得需要进行恢复,把旺财和小强的原有余额恢复成和50”

数据库老头笑眯眯的听着大家的分析,似乎非常的享受:“是不是可以应付各种情况啊?啊?”

大家纷纷点头,佩服无比。

Tomcat都不叫老头儿,改称为老先生了:“老先生,好像是有什么内在的规律,您给说说!”

数据库老头儿无比得意,兴致勃勃地说,“这就是我的独门秘籍了,其实很简单了,就两条:

在你把最新余额写入硬盘之前,一定要先把相关的Undo日志记录写入硬盘。例如[事务T1,旺财原有余额,]一定要在旺财的新余额=写入硬盘之前写入。

[提交事务T1]这样的Undo日志记录一定要在所有的新余额写入硬盘之后再写入。有了这两条的保证,我就可以高枕无忧了!比如说,换个操作次序也没有问题:”

大家仔细一想,果真如此啊,看来数据库老头儿,不,数据库老先生经常自夸还真有道理,人家真的有干货啊。

-END-

vNxt好文推荐:

我所尊敬的三位女程序员

学习面向对象的令狐冲

小心,别被今日头条给困住了!

欢迎订阅vNxt编程

长按







































白癜风人民大会堂活动
白癜风治疗的较好办法



转载请注明:http://www.xcqg58.com/xxzl/xxzl/8087.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了