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

文章详细介绍了如何在达梦数据库中通过系统视图和索引定位表数据的物理地址,并使用Linux的dd命令导出数据页。首先创建模拟数据,然后通过SYSOBJECTS表和聚集索引两种方式查找表的物理地址,最后使用dd命令导出数据页并用xxd命令查看二进制内容。文章强调了当表数据非连续时,通过SYSOBJECTS表可能得到错误的结果,而聚集索引的方式更为准确。
摘要由CSDN通过智能技术生成

介绍

对于一张表的数据,数据库是如何寻址并读取到其真实的数据,这便是寻址方式, 寻找到表数据的物理地址后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表的字段介绍
image.png

  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字段)。
image.png

  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参数导出多个页。

image.png
image.png

  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表与聚集索引方式比较测试

image.png
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表的方式是错误的。

image.png

批量导出数据页的脚本

/**
修改三个参数
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:表示在目标文件末尾追加数据。

image.png

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 字符。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值