回答Oracle的Double Write问题

我原来写了一篇我理解的Oracle Double Write(partial page)问题的解答,然后被问到了一些问题,因为呢我写的是我理解的,我觉得你认为有帮助就看看,没有帮助就算了,其实不想说太多,然后他最近又回复了我,让我看一篇文章。然后根据文章的结论如下:Oracle 中不是没有partial write的问题,而是Oracle 本身具有很多数据块的完整校验机制,写失败就直接回滚掉了,甚至在Oracle 11gR2版本还有写数据文件发现IO异常直接crash 实例的特性(当然是为了更好的保护数据库的完整性)。

我仔细的读了一下他推荐的文章,文章本身写的很精彩,我也学习到了很多东西,毕竟oracle这个数据库不是开源的,所以我觉得他写的也可能是对的,原作者应该技术也很好。

那么我说下我对我自己的理解的解释,我记得我的观点不是ORACLE的REDO没有页断裂的问题,通过CheckPoint避免了REDO发生页断裂造成的影响,为什么我这么考虑呢,因为解决页断裂在不考虑性能的前提下,很简单,对于一个做了数据这么多年的企业来说,我觉得不太会不解决这个问题,那下面我论证一下我对这个问题的最笨的解决方案。

首先考虑一个问题,假如实现一个数据库表,那么每个逻辑结构之间应该用什么数据结构,由于数组是连续的,而我们不能保证我们的数据是连续插入的,所以我觉得总体上的数据结构应该为一个链表形式,所以我根据链表来说一下我的理解:

下面这段程序是我模拟Oracle写入Redo一块数据,因为只要保证Redo写入成功且不发生块断裂,就解决了Double Write问题

import java.io.*;
import java.util.LinkedList;
import java.util.List;

public class Test {

    // 假如每次
    public static class TestObject implements Serializable {
        private byte[] value = new byte[4096];


    }
    public void test1(){
        List<Object> list = new LinkedList();
        for (int i = 0; i < 2; i++) {
            TestObject object = new TestObject();
            list.add(object);
        }
        File file = new File("g:/object.adt");
        try {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
            TestObject[] obj = new TestObject[list.size()];
            list.toArray(obj);
            //执行序列化存储
            out.writeObject(obj);
            out.getFD().sync();
        } catch (IOException e) {
            e.printStackTrace();
        }catch (SyncFailedException e) {
        e.printStackTrace();
   		}
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.test1();
    }
}

上面是我写的一个程序,很简单,就是将一个对象写到磁盘,在windows上写入之后为9KB,上面文章说的是linux只支持4K大小的数据进行原子写入,这个本身没有任何问题,所以我这么写代码肯定可能出现页断裂问题。但是现在的问题是,这个页断裂问题是不是那么难以解决,以至于ORACLE放弃了解决这个问题。

根据上面程序假如我在

out.writeObject(obj);

这行代码中运行了一半,系统退出,那么就发生了页断裂,也就是REDO写入失败,然后造成数据库出现问题,但是这个真的不能解决吗?

虽然我们无法获取是否操作系统是否真的将文件全部刷入到磁盘,但我可以写一个比较蠢的方式来验证。

public void test1(){
        List<Object> list = new LinkedList();
        for (int i = 0; i < 2; i++) {
            TestObject object = new TestObject();
            list.add(object);
        }
        TestObject[] obj = new TestObject[list.size()];
        File file = new File("g:/object.adt");
        try {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));

            list.toArray(obj);
            //执行序列化存储
            out.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            ObjectInputStream  in = new ObjectInputStream(new FileInputStream(file));
            TestObject[] inObject = (TestObject[])in.readObject();
            if(obj.length==inObject.length){
                System.out.println("插入成功");
            }else{
                throw new RuntimeException("发生页断裂");
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

我们将上面的类进行了下修改,当文件写入完成后我再次进行读取,看看这俩个list的size是不是相等,这样就解决了页断裂问题,所以我认为Oracle是解决了Redo页断裂问题的,因为解决这个问题的难度只在于如何提高性能,不在于能不能解决。

引用上面一篇文章

上面的红色部分是这段话的关键所在。从上面的内容可以看出,Oracle 通过在数据块(index
block也类似)尾部写入一个tailchk的方式来判断这个block是否完整;但是仅仅判断这个值,并不能确保整个block的数据是完好的,不过可以通过db_block_checksum来进一步进行检查(在读写时)。

但是在最后,看Oracle的说法是仍然无法彻底避免这个问题,毕竟最终数据落盘是由OS层来完成的,而不是Oracle自己。

根据我的代码,当我判断这个块是完整的时候,那么这个块肯定是已经完整的写入了磁盘,并不需要操作系统来保证4K以上数据的原子写。

通过简单分析,也能说明为什么很多存储复制软件来做Oracle容灾,在关键时刻备库不一定能正常打开就的原因就是这样。

其实基于复制的软件出现问题,不光这数据块的原子写问题,其实最大还是基于CAP定理所产生的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值