达梦数据库导出某张表的物理数据页

介绍

对于一张表的数据,数据库是如何寻址并读取到其真实的数据,这便是寻址方式, 寻找到表数据的物理地址后dump出相关的内容。

一、模拟数据

CREATE TABLESPACE "DMUPUSER.DBF" DATAFILE 'DMUPUSER.DBF' SIZE 128 CACHE = NORMAL;
create user "DMUPUSER" identified by "123456789"
default tablespace "DMUPUSER.DBF"
default index tablespace "DMUPUSER.DBF";
grant "PUBLIC","SOI" to "DMUPUSER";

---建表测试表PAGETEST、插入数据
create table "DMUPUSER"."PAGETEST"
(
	"C1" INT,
	"C2" VARCHAR(50),
	"C3" INT
)
storage(initial 1, next 1, minextents 1, fillfactor 0, on "DMUPUSER.DBF")
;

-- 建表PAGETEST_DEL并插入数据在删除
create table "DMUPUSER"."PAGETEST_DEL"
(
	"C1" INT,
	"C2" VARCHAR(50),
	"C3" INT
)
storage(initial 1, next 1, minextents 1, fillfactor 0, on "DMUPUSER.DBF")
;
INSERT INTO "DMUPUSER"."PAGETEST_DEL" SELECT LEVEL,LEVEL,LEVEL FROM DUAL CONNECT BY LEVEL <1000;
DROP TABLE "DMUPUSER"."PAGETEST_DEL";


INSERT INTO "DMUPUSER"."PAGETEST" SELECT LEVEL,LEVEL,LEVEL FROM DUAL CONNECT BY LEVEL <100000000;
commit;
select checkpoint(100);

二、查找表的物理地址并导出数据页

说明

  1. 达梦的表是索引组织表(Index-Organized Table),主要特点是按照一个主键值的顺序进行存储,同时支持基于范围的查询。
  2. 在达梦中表(列存储表和堆表除外)都是使用 B 树索引结构管理的,每一个普通表都有一个聚集索引,数据通过聚集索引键排序,根据聚集索引键可以快速查询任何记录。
  3. 因导出表的数据页,需要先找到表在数据文件中物理地址。
  4. 寻找表的物理地址有两个思路,第一个通过系统视图SYSOBJECTS表找到表的根页号随后导出表的数据页信息。第二个是找到表的聚集索引的ID,然后查找该索引的所有段信息随后dump出数据页信息。
  5. 采用方法二的话,可以从表的聚集索引中看到该表用了多少个页,可根据实际需求单独导出表的某个页分析处理。

方法一、通过SYSOBJECTS表定位_(错误)

思路:主要是操作SYSOBJECTS表,该表记录系统中所有对象的信息,可以找到表的根页号从而导出表的数据页。这种方式导出数据页,可能是错误的。因为表在物理上不一定是连续的,也会出现跨段等情况。

  1. 因多个模式下可能存在相同名字的表,首先先获取模式对应的ID号
-- 以DMUPUSER用户为例
select ID from SYSOBJECTS where  name = 'PAGETEST' AND TYPE$ = 'SCH';
  1. 获取模式ID下数据表的ID
--  以DMUPUSER用户的PAGETEST表为例
SELECT ID FROM SYSOBJECTS WHERE NAME='PAGETEST' AND SCHID = (select ID from SYSOBJECTS where  name = 'DMUPUSER' AND TYPE$ = 'SCH')
  1. 因达梦是索引组织表,根据表ID查询SYSINDEXES可获取表在文件中的偏移量
-- 以DMUPUSER用户的DMUP_TEST01表为例
SELECT *
          FROM SYSINDEXES
         WHERE ID =(SELECT ID
                   FROM SYSOBJECTS
                  WHERE PID=(SELECT ID
                           FROM SYSOBJECTS
                          WHERE NAME='PAGETEST'
                            AND SCHID = (select ID from SYSOBJECTS where name = 'DMUPUSER' AND TYPE$ = 'SCH'))
               ORDER BY ID LIMIT 1)

SYSINDEXES表的字段介绍
达梦数据库导出某张表的物理数据页_第1张图片

  1. 根据GROUPID与ROOTFILE字段查询V$DATAFILE表 可以获取到数据是在哪个表空间下
 select * from V$DATAFILE  WHERE GROUPID = :GROUP_ID AND ID = :ROOTFILE

以上组合后的语句

-- 以DMUPUSER用户的DMUP_TEST01表为例
select * , ( select CLIENT_PATH
          from V$DATAFILE
         WHERE GROUP_ID = t.GROUPID
           AND ID = t.ROOTFILE) TABLESPACES_NAME
  from ( SELECT *
          FROM SYSINDEXES
         WHERE ID =(SELECT ID
                   FROM SYSOBJECTS
                  WHERE PID=(SELECT ID
                           FROM SYSOBJECTS
                          WHERE NAME='PAGETEST'
                            AND SCHID = (select ID from SYSOBJECTS where name = 'DMUPUSER' AND TYPE$ = 'SCH'))
               ORDER BY ID LIMIT 1)) t

查询结果说明:DMUPUSER用户的PAGETEST表在数据文件DMUPUSER.DBF中,数据的根页为16 (ROOTPAGE字段)。
达梦数据库导出某张表的物理数据页_第2张图片

  1. 根据表数据的根页号导出相关的数据页,使用如下命令并注意命令的值修改。
dd if=DMUPUSER.DBF  of=test1.txt skip=`echo 16*8192|bc` ibs=1 count=8192;

注意:

  1. if 的值为数据所在的表空间文件也就是TABLESPACES_NAME字段
  2. skip 中的 16*8192 ,这里的16为查询dump表的页号(ROOTPAGE字段),8192表示数据库页的大小.(8k页=8192 32k页=32768)
  3. count参数为导出的块个数

命令解析

dd 是 Linux 系统中的一个命令,用于进行数据转换和复制。

  • if= 用于指定输入的文件(即 source,源文件)。
  • of= 用于指定输出的文件(即 destination,目标文件)。
  • skip= 用于指定从输入文件的哪个位置开始读取数据(即跳过前面的数据)。
  • ibs= 用于指定每次读取多少个字节(即输入块大小,input block size)。这里表示每次读取一个字节。
  • count= 用于指定要读取多少个块(即读取的块数)。8192 表示要读取 8192 个块,每个块大小为 1 个字节,即总共读取 8192 个字节。
  1. xdd命令或hexdump命令查看导出的数据页
## 1. 导出数据执行
[root@localhost DAMENG]# dd if=DMUPUSER.DBF  of=test1.txt skip=`echo 32*8192|bc` ibs=1 count=8192;
记录了8192+0 的读入
记录了16+0 的写出
8192字节(8.2 kB,8.0 KiB)已复制,0.00365255 s,2.2 MB/s
## 2. 使用xxd命令查看二进制文件,可以观察到insert的DUMP_STR可以在二进制中看到
[root@localhost DAMENG]# xxd test1.txt 
00000000: 0b00 0000 2000 0000 ffff ffff ffff ffff  .... ...........
00000010: ffff ffff 1500 0000 75c1 2341 abc4 f400  ........u.#A....
00000020: 0000 0000 0400 371c 0000 0000 0200 8000  ......7.........
00000030: 5200 5a00 0000 9000 0300 f70f 0002 0b00  R.Z.............
00000040: 0000 0800 0000 0c01 0b00 0000 0800 0000  ................
00000050: c400 4f43 0a00 0000 0000 ffff ffff ffff  ..OC............
00000060: ffff 000f 0080 5d0a 0000 0091 d4ab 0200  ......].........
00000070: 0000 0f00 1cc3 0d00 0000 0e0e 5205 0000  ............R...
00000080: 000f 008f 000a 0000 00fc 8ba7 0200 0000  ................
00000090: 0f00 9e00 0a00 0000 751e a602 0000 000f  ........u.......
000000a0: 00ad 000a 0000 00ee b0a4 0200 0000 0f00  ................
000000b0: bc00 0a00 0000 6743 a302 0000 000f 00cb  ......gC........
000000c0: 000a 0000 00e0 d5a1 0200 0000 0f00 da00  ................
000000d0: 0a00 0000 5968 a002 0000 000f 00e9 000a  ....Yh..........
000000e0: 0000 00d2 fa9e 0200 0000 0f00 f800 0a00  ................
000000f0: 0000 4b8d 9d02 0000 000f 0007 010a 0000  ..K.............
00000100: 00c4 1f9c 0200 0000 0f00 1601 0a00 0000  ................
00000110: 3db2 9a02 0000 000f 0025 010a 0000 00b6  =........%......
00000120: 4499 0200 0000 0f00 3401 0a00 0000 2fd7  D.......4...../.
00000130: 9702 0000 000f 0043 010a 0000 00a8 6996  .......C......i.
00000140: 0200 0000 0f00 5201 0a00 0000 21fc 9402  ......R.....!...
00000150: 0000 000f 0061 010a 0000 009a 8e93 0200  .....a..........
00000160: 0000 0f00 7001 0a00 0000 1321 9202 0000  ....p......!....
00000170: 000f 007f 010a 0000 008c b390 0200 0000  ................
00000180: 0f00 8e01 0a00 0000 0546 8f02 0000 000f  .........F......
00000190: 009d 010a 0000 007e d88d 0200 0000 0f00  .......~........
000001a0: ac01 0a00 0000 f76a 8c02 0000 000f 00bb  .......j........
000001b0: 010a 0000 0070 fd8a 0200 0000 0f00 ca01  .....p..........
000001c0: 0a00 0000 e98f 8902 0000 000f 00d9 010a  ................
000001d0: 0000 0062 2288 0200 0000 0f00 e801 0a00  ...b"...........
000001e0: 0000 dbb4 8602 0000 000f 00f7 010a 0000  ................
000001f0: 0054 4785 0200 0000 0f00 0602 0a00 0000  .TG.............
00000200: cdd9 8302 0000 000f 0015 020a 0000 0046  ...............F
00000210: 6c82 0200 0000 0f00 2402 0a00 0000 bffe  l.......$.......
00000220: 8002 0000 000f 0033 020a 0000 0038 917f  .......3.....8..
00000230: 0200 0000 0f00 4202 0a00 0000 b123 7e02  ......B......#~.
00000240: 0000 000f 0051 020a 0000 002a b67c 0200  .....Q.....*.|..

方法二、通过表的聚集索引定位

思路:查询表的聚集索引的ID,通过v$segmentinfo表查看该索引ID所有的段ID以及物理页号。

  1. 找到要导出表的聚集索引ID
 select OBJECT_ID,INDEX_NAME,INDEX_TYPE,OBJECT_TYPE,INDEX_TYPE,
UNIQUENESS from dba_indexes a,dba_objects b where TABLE_NAME='PAGETEST' and a.INDEX_NAME=B.OBJECT_NAME and b.owner = 'DMUPUSER' AND INDEX_TYPE = 'CLUSTER' AND INDEX_NAME LIKE 'INDEX%';
  1. 根据聚集索引ID在视图v$segmentinfo中找到对应的段ID以及物理页号
-- 以DMUPUSER用户的PAGETEST表为例
select * from v$segmentinfo where index_id=( select OBJECT_ID from dba_indexes a,dba_objects b where TABLE_NAME='PAGETEST' and a.INDEX_NAME=B.OBJECT_NAME and b.owner = 'DMUPUSER' AND INDEX_TYPE = 'CLUSTER' AND INDEX_NAME LIKE 'INDEX%' ); 

-- 如需要确认数据表所在的数据文件,可根据查询结果的FIL_ID字段查询V$DATAFILE表查看表空间名称
-- 批量生成dd命令的脚本请查看附录

查询结果说明

  1. DMUPUSER用户的PAGETEST表在数据文件DMUPUSER.DBF中,数据的根页为16 (ROOTPAGE字段)。
  2. 根据v$segmentinfo表可以看到该数据段中第一个页是16,根据字段PHY_FIRST_PAGE_IN_EXTENT与PHY_PAGE_NO可以看到数据存储的页号应该是96。
  3. 如果有导出所有页的需求就需要逐条单个dd命令导出了,根据PHY_PAGE_NO的结果集中看到规律,页号有连续的。也可以通过修改dd命令的count参数导出多个页。

达梦数据库导出某张表的物理数据页_第3张图片
达梦数据库导出某张表的物理数据页_第4张图片

  1. 根据表数据的根页号导出相关的数据页,使用如下命令并注意命令的值修改。

使用如下命令

dd if=DMUPUSER.DBF  of=test1.txt skip=`echo 96*8192|bc` ibs=1 count=8192;

注意:

  1. if 的值为数据所在的表空间文件也就是TABLESPACES_NAME字段
  2. skip 中的 96*8192 这里的 96为查询dump表的页号(ROOTPAGE字段),8192表示数据库页的大小,8k页=8192 32k页=32768
  3. count参数为导出的块个数

命令解析

dd 是 Linux 系统中的一个命令,用于进行数据转换和复制。

  • if= 用于指定输入的文件(即 source,源文件)。
  • of= 用于指定输出的文件(即 destination,目标文件)。
  • skip= 用于指定从输入文件的哪个位置开始读取数据(即跳过前面的数据)。
  • ibs= 用于指定每次读取多少个字节(即输入块大小,input block size)。这里表示每次读取一个字节。
  • count= 用于指定要读取多少个块(即读取的块数)。8192 表示要读取 8192 个块,每个块大小为 1 个字节,即总共读取 8192 个字节。
  1. xdd命令或hexdump命令查看导出的数据页
## 1. 导出数据执行
[root@localhost DAMENG]# dd if=DMUPUSER.DBF  of=test1.txt skip=`echo 4528*8192|bc` ibs=1 count=8192;
记录了8192+0 的读入
记录了16+0 的写出
8192字节(8.2 kB,8.0 KiB)已复制,0.0036946 s,2.2 MB/s

## 2. 使用xxd命令查看二进制文件,可以观察到insert的DUMP_STR可以在二进制中看到
[root@localhost DAMENG]# xxd test1.txt 
00000000: 0b00 0000 b011 0000 ffff ffff ffff 0000  ................
00000010: b111 0000 1400 0000 b9a2 a3d0 bbcd cf00  ................
00000020: 0000 0000 e500 1c1e 0000 0000 e300 ffff  ................
00000030: 5200 5a00 0000 ee1f 0000 f70f 0002 0000  R.Z.............
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 ffff ffff ffff  ................
00000060: ffff 0020 0001 0000 0001 0000 0081 3101  ... ..........1.
00000070: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000080: 0000 0020 0002 0000 0002 0000 0081 3202  ... ..........2.
00000090: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
000000a0: 0000 0020 0003 0000 0003 0000 0081 3303  ... ..........3.
000000b0: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
000000c0: 0000 0020 0004 0000 0004 0000 0081 3404  ... ..........4.
000000d0: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
000000e0: 0000 0020 0005 0000 0005 0000 0081 3505  ... ..........5.
000000f0: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000100: 0000 0020 0006 0000 0006 0000 0081 3606  ... ..........6.
00000110: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000120: 0000 0020 0007 0000 0007 0000 0081 3707  ... ..........7.
00000130: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000140: 0000 0020 0008 0000 0008 0000 0081 3808  ... ..........8.
00000150: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000160: 0000 0020 0009 0000 0009 0000 0081 3909  ... ..........9.
00000170: 0000 0000 00ff ffff ff7f ffff e829 1100  .............)..
00000180: 0000 0021 000a 0000 000a 0000 0082 3130  ...!..........10
00000190: 0a00 0000 0000 ffff ffff 7fff ffe8 2911  ..............).
000001a0: 0000 0000 2100 0b00 0000 0b00 0000 8231  ....!..........1
000001b0: 310b 0000 0000 00ff ffff ff7f ffff e829  1..............)
000001c0: 1100 0000 0021 000c 0000 000c 0000 0082  .....!..........
000001d0: 3132 0c00 0000 0000 ffff ffff 7fff ffe8  12..............
000001e0: 2911 0000 0000 2100 0d00 0000 0d00 0000  ).....!.........
000001f0: 8231 330d 0000 0000 00ff ffff ff7f ffff  .13.............
00000200: e829 1100 0000 0021 000e 0000 000e 0000  .).....!........
00000210: 0082 3134 0e00 0000 0000 ffff ffff 7fff  ..14............
00000220: ffe8 2911 0000 0000 2100 0f00 0000 0f00  ..).....!.......
00000230: 0000 8231 350f 0000 0000 00ff ffff ff7f  ...15...........
00000240: ffff e829 1100 0000 0021 0010 0000 0010  ...).....!......
00000250: 0000 0082 3136 1000 0000 0000 ffff ffff  ....16..........
00000260: 7fff ffe8 2911 0000 0000 2100 1100 0000  ....).....!.....
00000270: 1100 0000 8231 3711 0000 0000 00ff ffff  .....17.........
00000280: ff7f ffff e829 1100 0000 0021 0012 0000  .....).....!....
00000290: 0012 0000 0082 3138 1200 0000 0000 ffff  ......18........
000002a0: ffff 7fff ffe8 2911 0000 0000 2100 1300  ......).....!...
000002b0: 0000 1300 0000 8231 3913 0000 0000 00ff  .......19.......
000002c0: ffff ff7f ffff e829 1100 0000 0021 0014  .......).....!..
000002d0: 0000 0014 0000 0082 3230 1400 0000 0000  ........20......
000002e0: ffff ffff 7fff ffe8 2911 0000 0000 2100  ........).....!.

附录

SYSOBJECTS表与聚集索引方式比较测试

达梦数据库导出某张表的物理数据页_第5张图片
SYSOBJECTS表的方式,导出10个数据块

dd if=DMUPUSER.DBF  of=test1.txt skip=`echo 16*8192|bc` ibs=1 count=81920;

v$segmentinfo表的方式,导出10个数据块

使用脚本生成的命令
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 16*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 97*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 98*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 99*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 100*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 101*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 102*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 103*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 104*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 105*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# dd if=DMUPUSER.DBF of=test2.txt skip=`echo 106*8192|bc` ibs=1 count=8192 conv=notrunc oflag=append;
[root@localhost DAMENG]# xxd test2.txt | grep -v '0000 0000 0000 0000 0000 0000 0000 0000' |head -n 1100| tail -n 100

两个文本比较,查看第1000行对比如下:

  1. 左侧的是SYSOBJECTS表导出的数据页是无序的,右侧v$segmentinfo的方式是有序且有规律的。
  2. 当表中数据物理页不连续时,采用SYSOBJECTS表的方式是错误的。

达梦数据库导出某张表的物理数据页_第6张图片

批量导出数据页的脚本

/**
修改三个参数
table_owner 模式名
table_name  表名
print_count  要打印的页数
**/
declare
    table_owner   varchar(256);
    table_name    varchar(256);
    datafile_name varchar(256);
    sql_str       varchar(512);
    page_size     int;
    i             NUMBER := 0;
    print_count   int;
    PHY_PAGE_NO   int;
    CURSOR sel_cur;
    begin
        table_owner := 'DMUPUSER';
        table_name  := 'PAGETEST';
        print_count := 10;
        -- 查询出表所在的表空间
        sql_str:= 'select tablespace_name from dba_indexes where table_owner =''' || table_owner || '''  AND INDEX_TYPE = ''CLUSTER''' || ' AND INDEX_NAME LIKE ''INDEX%''' || 'AND TABLE_NAME = ''' || table_name||'''';
        --print 'sql_str' || sql_str;
        execute immediate sql_str into datafile_name;
        -- 查询出当前页大小
        select page into page_size from dual;
        --PRINT 'datafile_name=' || datafile_name;
        -- 查询块sql
        sql_str:= 'select PHY_PAGE_NO from v$segmentinfo where index_id=( select OBJECT_ID from dba_indexes a,' || 'dba_objects b  where TABLE_NAME=''' ||table_name|| ''' and a.INDEX_NAME=B.OBJECT_NAME  and b.owner = ''' || table_owner || '''  AND INDEX_TYPE = ''CLUSTER'' AND INDEX_NAME LIKE ''INDEX%'' )';
        --PRINT 'sql_str ' || sql_str;
        open sel_cur for sql_str;
        LOOP
            FETCH sel_cur INTO PHY_PAGE_NO;
             
            EXIT WHEN sel_cur%NOTFOUND OR i >print_count ;
            i := i + 1;
            print 'dd if=' || datafile_name || ' of=test1.txt skip=`echo '||PHY_PAGE_NO||'*'||page_size||'|bc` ibs=1 count='||page_size||' conv=notrunc oflag=append;';
        END LOOP;
        CLOSE sel_cur;
    end;

dd两个命令介绍,使用下方命令参数可将源文件的内容追加到目标文件的末尾,并保留目标文件原有的内容。

  • conv=notrunc:表示不截断目标文件。
  • oflag=append:表示在目标文件末尾追加数据。

达梦数据库导出某张表的物理数据页_第7张图片

xxd的命令

## 备用命令
## 1. 查看文件时,去除空数据
[root@localhost DAMENG]# xxd test1.txt | grep -v '0000 0000 0000 0000 0000 0000 0000 0000' |head -n 100
## 2. 查看前几行
[root@localhost DAMENG]# xxd test1.txt|head -n 50
## 3. hexdump命令
[root@localhost DAMENG]# hexdump -C test1.txt 
## hexdump -C 参数是 hexdump 命令的一个选项,表示以十六进制和 ASCII 的形式显示文件内容,每行显示对应的 ASCII 字符。

你可能感兴趣的:(达梦数据库,数据库,java,sql,达梦数据库,DM8)