前阵子有人在讨论数据块出现块断裂情况Oracle内部是如何自动恢复的。最后的讨论结果是,Oracle的DB CACHE和脏块处理的方法是无法自动恢复块断裂的。这是事实,这也是在ONLINE 拷贝时需要BEGIN BACKUP的原因了。当BEGIN BACKUP开始后,所有数据块的第一次修改都会把整块写入REDO LOG,从而避免块断裂导致数据块无法恢复的情况。讨论时我就说过,Oracle是依靠从内存到OS到HBA卡/SCSI卡,到存储的一整条链条的保证,来确保所写入的数据不会出现块断裂的。但是任何一个环节都是不可能完全不出问题的,所以说DBA们也经常会遇到ORA-600 [4194]之类的问题了。如果真的出了问题,除了采取丢失部分数据强行修复数据块的方式,还可以利用DATAGUARD/RMAN备份等方式来恢复数据库或者数据块。
说白了,Oracle就像当今老美防疫一样,靠着自己的医疗资源高端硬抗,大不了老子弄20万张ICU床位出来,这一点别人学不来。Mysql等开源数据库都无法像Oracle一样依赖高端的硬件来避免大多数的问题,于是采用DOUBLE WRITE BUFFER的机制来避免块断裂。原理比较简单,就是在数据块写入数据文件之前,先把这个数据块写入到一个文件缓冲区中(不是内存,是文件,在一个独立的文件区域中开一个2M的缓冲,用于写入最近的数据块),然后再写入真的文件。这样做就能够有效的避免块断裂了,如果写入缓冲出错,没关系,真实的文件还没写呢;如果写真实文件的时候出问题了,那么到DOUBLE WRITE BUFFER中去找到这个块来恢复就行了。很完美的解决方案,不过这里就有一个代价,所有缓冲区写入文件都要写两遍,这会带来巨大的IO开销。当然DOUBLE WRITE BUFFER的写入是顺序写,比随机写开销小很多,不过这部分开销对于大并发量的系统来说,还是不小的开销。
实际上,很多数据库都有类似的机制来解决块断裂的问题,华为Gaussdb 100也有类似的double write buffer(自动存放于system表空间中);PostgreSQL是依靠暴力的把整块数据页写入WAL来解决这个问题的,这会导致WAL暴涨,因此有些用户通过采用WAL压缩来解决这个问题,也有人写了一个插件,在PG中实现类似MYSQL的DOUBLE WRITE BUFFER的机制来替代整个页面写入WAL。现在也有不少从数据库到OS到硬件的整体解决方案,来实现写原子化或者通过文件系统的写日志保证写事务可回滚,从而避免块断裂的问题(最典型的是ZFS)。如果的MYSQL之类的跑在这样的系统上,那么你不打开DOUBLE WRITE BUFFER也是没问题的。
其实解决这个问题不难,而且有许多种渠道,除了OS与文件系统支持原子写操作外,如果你的数据库本身就是高可用的,比如有类似oracle adg,mysql半同步复制集群之类的机制,那么你就不需要打开double write buffer了。
另外一个思路是把double write buffer变成独立的文件,这样就可以把这个文件放在一个比较快的独立设备上了(比如放到INTEL 傲腾内存盘上)。不过目前MYSQL,高斯等好像还不支持独立放置,只能放在系统表空间中。