1.数据库老哥我们的世界很大,住着很多人,形形色色,各有所长。不过公认最有魅力的还是数据库老头。他年纪大了,一天要好几次炫耀他的关系数据库。理论有如此扎实的数学基础,关系运算如此优雅,事务管理如此强大。多强大,多稳重,不然怎么可能活这么久等等。老头有自己的本钱,因为我们系统的核心数据都存储在老头里面,比如用户,订单,交易……我问过别人,这些数据都已经存储在老头里面了。它已经积累了20多年。最早的时候是Dephi、PowerBuider等古老软件写的系统访问。后来慢慢转到网上,先是用PHP访问,后来改成Java。看来最宝贵的是数据。外部系统可以更改,但数据不能丢失。老爷子守护着这份财产,活了一代人,估计又要死掉一代人。2.营业日,老人又在那里喝酒,“吹嘘”他的企业管理,说了一些我们听不懂的外语,比如ACID。经常和数据库打交道的小伙子就是Tomcat。对于老头子,他知道的比我们多一点点。Tomcat端着一杯啤酒,来到老爷子的桌子前:“嘿,老爷子,我知道你说的事务有一个重要的特性:原子性,也就是说一个事务中无论执行多少操作,都是他们必须完成。或者根本不做,那是对的!“那是自然的!”“我很好奇,如果系统还没有完成操作就崩溃了,或者断电了,你怎么办?保证原子性?”听到Tomcat问了一个关键问题,小酒馆里的CPU阿甘,Ngnix,Spring,MyBatis都围了过来,想听听老爷子的意见。“如果我还没有完成,系统就崩溃了,那我得等系统重启后,再做一次恢复操作。”“如何恢复?”雄猫追了上去。“举个例子,我帮你分析一下。”老爷子环顾四周,见周围围满了人,顿时来了点兴趣,“这些孩子怎么会懂理论?”“好吧,比如王财有200元,小强有50元,现在王财要转钱给小强,比如说转100元,你说说,你是怎么实现的,或者什么都不做,或者全部都做”(1)开始交易T1(假设T1是交易的内部编号)(2)旺财余额=旺财余额-100(3)小强余额=小强余额+100(4)提交交易T1CPU阿甘插嘴说:“虽然这些都是我在做计算,但是这些计算的结果是先存到内存里的,内存小子一断电就什么都忘了。如果在执行第三步之前就断电了,就结束了。”数据库老哥尔说:“虽然数据是先在内存中计算的,但我想写到数据库文件上硬盘,你懂吗?”“数据缓冲区是什么意思?”人群中有人问道。“老套!我什至不知道数据缓冲区!告诉你吧,那个硬盘太慢了,比内存慢几万倍。你以为我每次操作都能写入硬盘吗?绝对不可能,所以我会把CPU阿甘算出来的数据放到数据缓冲区里,我会在合适的时候把数据缓冲区的内容写到硬盘上的数据文件中。”“什么是合适的时间?”“那是我的缓冲经理有事要听吗?我再花两天时间告诉你!”3.Undolog“算了,假设数据缓冲区可以和硬盘上的数据文件同步,返回至于刚才的问题,旺财正在转账给小强。第二步执行后,旺财的余额变成了100元(200-100)。假设硬盘文件已经写好了,现在断电了,小强的余额如果不加的话,系统里的100块钱就白白消失了,数据不一致,怎么办?”Tomcat把话题改回来了。“你放心,我会记录日志的,我有一个名为Undo的日志文件,就是为了解决这个问题。”档案记录交易开始前他们的账户余额:【交易T1,旺财原余额,200】【交易T1,小强原余额,50】如果交易进行到一半,断电关闭,重启数据库,以后我就按照undolog文件来恢复。”“那要是在系统恢复的过程中又断电了,又得重新恢复系统,岂不是数据变得一团糟?”CPU阿甘对停电心有余悸。“对对对。”周围的人附和道。balance和小强的余额重复100次,会发生什么?很多!”雄猫恍然大悟。“这就叫操作的幂等性知道吗?我可以一直做recovery,而且我不怕recovery过程中掉电,只要recovery完成就行。”“在恢复数据的时候,怎么知道交易没有完成呢?” Tomcat接着问道。数据库老头似乎对这个问题很满意,花了点时间写了几行:【开始交易T1】【交易T1,旺财的原始余额,200】【交易T1,小强的原始余额,50】[提交事务T1]》撤销日志文件不仅有余额,还记录了事务的开始和结束。如果我在日志文件中看到[提交事务T1],或者[回滚事务T1],我就知道这个事务已经完成了,结束了,别理它,更不用说恢复了。如果我只看到[starttransactionT1],却找不到commit或者rollback,那我就得恢复了,比如下面的:”【开始交易T1】【交易T1,旺财原余额,200】【交易T1,小强原余额,50】“特别是,”老爷子补充道,“我恢复之后,需要加一个行到日志文件[rollbacktransactionT1],这样我就不必为下一次恢复考虑T1事务。“4大绝招”不对,你的undolog文件会面临和数据文件一样的问题,需要加载到内存中才能读写,否则太慢了。如果日志文件写入之前就断电了,那不是游戏就结束了吗?” Ngnix深入挖掘。这是一个很好的问题。这一次,他能够把他打倒在地。老者不动声色,没有回答。相反,他问大家一个问题:“大家想一想,什么时候记录Undolog,什么时候把Undolog写入文件?”“你必须在事务开始时将Undolog写入文件,这是最安全的方式!”老者笑道:“白痴!一开始我都不知道程序要操作哪个字段,如何记录Undolog,如何写入硬盘文件?”“那我该怎么办?”“这是我的绝招,我给你举个例子,你要仔细看,我也把日志记录放在内存的Undologbuffer里,伺机写入硬盘。”“我不不知道你们小子有没有看懂小把戏?”老头 问。“让我考虑一下” Ngnix说,“如果系统在第4步和第5步之间崩溃了,旺财的余额已经写入硬盘,但是小强的还没有写入,所以Undolog是这样的:【开始交易T1】【交易T1,旺财原余额,200】因为找不到交易结束Log,你将执行恢复操作,恢复旺财原来的余额。”Tomcat接过来说:“如果在第7步和第8步之间系统崩溃,旺财和小强的最新余额被写入硬盘,但是事务没有提交,那么Undolog是这样的:[开始交易T1][交易T1,旺财原余额,200][交易T1,小强原余额,50]由于没有交易结束的日志,所以还需要恢复旺财和原余额小强200和50”CPU阿甘不甘示弱表示:“如果系统在第8步和第9步之间崩溃,将旺财和小强的最新余额写入硬盘,交易也提交了,但是交易操作没有写入Undolog,所以Undolog还是这样:“【开始交易T1】【交易T1,旺财原余额,200】【交易T1,小强原余额,50】既然有是事务结束没有日志,你还是n需要把旺财和小强原来的余额恢复到200和50。”数据库老头笑着听着大家的分析,似乎很享受:“是不是可以应付各种情况了?啊?”所有人都赞叹的点了点头。Tomcat不再叫老头,而是老头:“老头,似乎有什么内在规律,请说说!”数据库老头高兴的说,“这是我的独门秘籍,其实很简单,就两件事:1.在你把最新的余额写入硬盘之前,首先要将相关的Undo日志记录写入到硬盘。例如,[交易T1,旺财的原始余额,200]必须在Wangcai=100的新余额写入硬盘之前写入。2.[提交交易T1]这样的Undo日志记录毕竟必须写入新的余额写入硬盘,有了这两个保证,我就可以高枕无忧了!比如改变操作顺序就没有问题了:》【本文为专栏作家“刘欣”的原创稿件,转载请通过作者微信获取授权公众号coderising】点此查看作者更多好文章
