达梦数据库的表空间
本文于 406 天前发布,最后更新于 9 天前

一、简单概述

在 DM 数据库中,表空间由一个或者多个数据文件组成。DM 数据库中的所有对象在逻辑上都存放在表空间中,而物理上都存储在所属表空间的数据文件中。

在创建 DM 数据库时,会自动创建 5 个表空间:SYSTEM 表空间、ROLL 表空间、MAIN 表空间、TEMP 表空间和 HMAIN 表空间。

SYSTEM 表空间存放了有关 DM 数据库的字典信息,用户不能在 SYSTEM 表空间创建表和索引。

ROLL 表空间完全由 DM 数据库自动维护,用户无需干预。该表空间用来存放事务运行过程中执行 DML 操作之前的值,从而为访问该表的其他用户提供表数据的读一致性视图。

MAIN 表空间在初始化库的时候,就会自动创建一个大小为 128M 的数据文件MAIN.DBF。在创建用户时,如果没有指定默认表空间,则系统自动指定 MAIN 表空间为用户默认的表空间。

TEMP 表空间完全由 DM 数据库自动维护。当用户的 SQL 语句需要磁盘空间来完成某个操作时,DM 数据库会从 TEMP 表空间分配临时段。如创建索引、无法在内存中完成的排序操作、SQL 语句中间结果集以及用户创建的临时表等都会使用到 TEMP 表空间。

HMAIN 表空间属于 HTS 表空间,完全由 DM 数据库自动维护,用户无需干涉。当用户在创建 HUGE 表时,未指定 HTS 表空间的情况下,充当默认 HTS 表空间。

每一个用户都有一个默认的表空间。对于 SYS、SYSSSO、SYSAUDITOR 系统用户,默认的用户表空间是 SYSTEM,SYSDBA 的默认表空间为 MAIN,新创建的用户如果没有指定默认表空间,则系统自动指定 MAIN 表空间为用户默认的表空间。如果用户在创建表的时候,指定了存储表空间 A,并且和当前用户的默认表空间 B 不一致时,表存储在用户指定的表空间 A 中,并且默认情况下,在这张表上面建立的索引也将存储在 A 中,但是用户的默认表空间是不变的,仍为 B。

一般情况下,建议用户自己创建一个表空间来存放业务数据,或者将数据存放在默认的用户表空间 MAIN 中。

二、相关SQL

(1)创建表空间

数据文件的最小值不能低于页大小的 4096 倍,最大值是页大小乘以 2 的 31 次方-1(如果页大小为 8K,最大值为 16T)

数据库页大小 每个字符类型字段实际最大长度(字节) 每行记录最大字段外其他字段总长度(字节) 表空间单个数据文件最小 (MB)/最大 (MB)
4 KB 1938 2047 16/8388608
8 KB 3878 4095 32/16777216
16 KB 8000 8195 64/33554432
32 KB 8188 16176 128/67108864
CREATE TABLESPACE "TBS" DATAFILE '/dmdata/PROD/TBS01.DBF' SIZE 32 AUTOEXTEND ON NEXT 1 MAXSIZE 1024;

(2)修改表空间文件大小

ALTER TABLESPACE "TBS" RESIZE DATAFILE '/dmdata/PROD/TBS01.DBF' TO 64;

(3)添加数据文件

ALTER TABLESPACE "TBS" ADD DATAFILE '/dmdata/PROD/TBS02.DBF' SIZE 64 AUTOEXTEND ON NEXT 1 MAXSIZE 1024;

(4)修改数据文件扩展尺寸

ALTER TABLESPACE "TBS" DATAFILE '/dmdata/PROD/TBS01.DBF' AUTOEXTEND ON NEXT 3 MAXSIZE 1024;
--相关系统视图字段:DBA_DATA_FILES.INCREMENT_BY

(5)数据文件的迁移

ALTER TABLESPACE "TBS" OFFLINE;
ALTER TABLESPACE "TBS" RENAME DATAFILE '/dmdata/PROD/TBS01.DBF' TO '/dmdata/TBS01.DBF';
ALTER TABLESPACE "TBS" RENAME DATAFILE '/dmdata/PROD/TBS02.DBF' TO '/dmdata/TBS02.DBF';

(6)查询表空间使用率

--V$DATAFILE 和 V$TABLESPACE
SELECT
    TS.NAME                                                    表空间名字,
    DF.TOTAL_SIZE * DF.PAGE_SIZE / 1024 / 1024                 总大小M,
    (DF.TOTAL_SIZE -DF.FREE_SIZE) * DF.PAGE_SIZE / 1024 / 1024 当前使用大小M,
    DF.FREE_SIZE * DF.PAGE_SIZE / 1024 / 1024                  剩余大小M,
    ROUND((1 -DF.FREE_SIZE / DF.TOTAL_SIZE) * 100, 2) || '%'   使用百分比,
    DF.MAX_SIZE                                                上限大小M,
    DF.PATH                                                    表空间文件路径,
    DF.CLIENT_PATH                                             表空间客户端路径
FROM
    V$DATAFILE DF
    JOIN V$TABLESPACE TS ON DF.GROUP_ID = TS.ID
ORDER BY
    TS.NAME,
    DF.PATH;

--V$TABLESPACE
SELECT
    NAME AS                                                     "名称",
    (TOTAL_SIZE * PAGE) / (1024 * 1024) AS                      "大小(MB)",
    (USED_SIZE * PAGE) / (1024 * 1024) AS                       "使用大小(MB)",
    ((TOTAL_SIZE - USED_SIZE) * PAGE) / (1024 * 1024) AS        "空闲大小(MB)",
    ROUND((USED_SIZE / TOTAL_SIZE), 4) * 100 AS                 "使用率",
    (TOTAL_SIZE * PAGE) / (1024 * 1024 * 1024) AS               "大小(GB)",
    (USED_SIZE * PAGE) / (1024 * 1024 * 1024) AS                "使用大小(GB)",
    ((TOTAL_SIZE - USED_SIZE) * PAGE) / (1024 * 1024 * 1024) AS "空闲大小(GB)",
    ROUND((USED_SIZE / TOTAL_SIZE), 4) * 100 AS                 "使用率"
FROM
    V$TABLESPACE;

--DBA_FREE_SPACE 和 DBA_DATA_FILES
SELECT
    A.TABLESPACE_NAME AS                      "名称",
    TOTAL / (1024 * 1024) AS                  "大小(M)",
    (TOTAL - FREE) / (1024 * 1024) AS         "使用大小(M)",
    FREE / (1024 * 1024) AS                   "空闲大小(M)",
    TOTAL / (1024 * 1024 * 1024) AS           "大小(G)",
    (TOTAL - FREE) / (1024 * 1024 * 1024) AS  "使用大小(G)",
    FREE / (1024 * 1024 * 1024) AS            "空闲大小(G)",
    ROUND((TOTAL - FREE) / TOTAL, 4) * 100 AS "使用率 %"
FROM
    (SELECT TABLESPACE_NAME, SUM(BYTES) FREE FROM DBA_FREE_SPACE GROUP BY TABLESPACE_NAME) A,
    (SELECT TABLESPACE_NAME, SUM(BYTES) TOTAL FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME) B
WHERE
    A.TABLESPACE_NAME = B.TABLESPACE_NAME;

--V$TABLESPACE 和 V$DATA_FILE
SELECT
    C.NAME                      表空间名字,
    D.TOTAL                     总大小M,
    D.USE                       当前使用大小M,
    D.FREE                      剩余大小M,
    ROUND(D.USED_PER, 2) || '%' 使用百分比,
    E.MAX_SIZE                  上限大小M
FROM
    V$TABLESPACE C
    JOIN (SELECT
              A.ID,
              SUM(B.TOTAL_SIZE) * B.PAGE_SIZE / 1024 / 1024                                           TOTAL,
              SUM(B.FREE_SIZE) * B.PAGE_SIZE / 1024 / 1024                                            FREE,
              SUM(B.TOTAL_SIZE * B.PAGE_SIZE / 1024 / 1024 - B.FREE_SIZE * B.PAGE_SIZE / 1024 / 1024) USE,
              100 - (SUM(B.FREE_SIZE) * 100 / SUM(B.TOTAL_SIZE))                                      USED_PER
          FROM
              V$TABLESPACE A,
              V$DATAFILE B
          WHERE
              A.ID = B.GROUP_ID
          GROUP BY
              A.ID,
              B.PAGE_SIZE) D ON C.ID = D.ID
    LEFT JOIN V$DATAFILE E ON C.ID = E.GROUP_ID
ORDER BY
    ROUND(D.USED_PER, 2) DESC;

--开启自增,将自增空间计算在内
SELECT * FROM
    (SELECT
         T.NAME,
         G.TOTAL_MB,
         G.USED_MB,
         G.FREE_MB,
         G.USED_PERCENT,
         CASE
             WHEN G.USED_PERCENT <= 85 OR G.FREE_MB / 1024 > 60 THEN
                 'NORMAL'
             WHEN G.USED_PERCENT > 85 AND G.USED_PERCENT <= 90 THEN
                 'WARNING'
             WHEN G.USED_PERCENT > 90 THEN
                 'ERROR'
         END INSPECTION_RESULT
     FROM
         (SELECT
              GDF.GROUP_ID,
              SUM(GDF.FILE_SIZE_MB)                                                        TOTAL_MB,
              SUM(FILE_USED_MB)                                                            USED_MB,
              SUM(GDF.FILE_SIZE_MB) - SUM(GDF.FILE_USED_MB)                                FREE_MB,
              TO_CHAR(ROUND(SUM(FILE_USED_MB) / SUM(GDF.FILE_SIZE_MB) * 100, 2), '990.99') USED_PERCENT
          FROM
              (SELECT
                   DF.GROUP_ID,
                   round(
                   CASE
                       WHEN DF.AUTO_EXTEND = 1 THEN
                           DF.MAX_SIZE
                       ELSE
                           DF.TOTAL_SIZE * DF.PAGE_SIZE / 1024 / 1024
                   END, 1)                                                              FILE_SIZE_MB,
                   round((DF.TOTAL_SIZE -DF.FREE_SIZE) * DF.PAGE_SIZE / 1024 / 1024, 1) FILE_USED_MB
               FROM
                   V$DATAFILE DF) GDF
          GROUP BY
              GDF.GROUP_ID) G,
         V$TABLESPACE T
     WHERE
         G.GROUP_ID = T.ID
     ORDER BY
         5 DESC)
WHERE
    ROWNUM <= 40;

(7)批量关闭表空间文件自动扩展

结果集生成SQL

SELECT 'ALTER TABLESPACE ' ||TABLESPACE_NAME|| ' DATAFILE ''' ||FILE_NAME|| ''''||' AUTOEXTEND OFF;' AS SQL
FROM DBA_DATA_FILES ORDER BY TABLESPACE_NAME, FILE_NAME;

执行结果集SQL,例如:

ALTER TABLESPACE TBS1 DATAFILE '/dmdata/5236/DMDB/TBS101.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE TBS2 DATAFILE '/dmdata/5236/DMDB/TBS201.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE IDX_TBS1 DATAFILE '/dmdata/5236/DMDB/IDX_TBS101.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE IDX_TBS2 DATAFILE '/dmdata/5236/DMDB/IDX_TBS201.DBF' AUTOEXTEND OFF;

(8)修改表空间扩展上限

ALTER TABLESPACE "表空间名" DATAFILE '表空间文件绝对路径' AUTOEXTEND ON NEXT 100 MAXSIZE 512000;

(9)查询表空间扩展尺寸

SELECT
    FILE_NAME,
    TABLESPACE_NAME,
    AUTOEXTENSIBLE AS "是否开启自动扩展",
    INCREMENT_BY AS   "每次扩展大小(MB)"
FROM
    DBA_DATA_FILES;

(10)表空间数据文件中各表数据页位置分布

DECLARE
  V_TABLESPACE_NAME  VARCHAR2(128) := 'PMSNB%';  --要查询的表空间名,可以使用通配符%,不区分大小写
  V_TABLE_NAME       VARCHAR2(128) := '%';     --要查询的表名,可以使用通配符%,不区分大小写
  ---------------------------------------------------
  TYPE TR_DATAPOS IS RECORD
  (
    TS_ID            INT,
    TS_NAME          VARCHAR2(128),
    FL_ID            INT,
    FL_PATH          VARCHAR2(1000),
    USER_ID          INT,
    USER_NAME        VARCHAR2(128),
    SCHEMA_ID        INT,
    SCHEMA_NAME      VARCHAR2(128),
    TABLE_ID         INT,
    TABLE_NAME       VARCHAR2(128),
    TAB_SIZE         NUMBER(18,0),
    INDEX_ID         INT,
    INDEX_NAME       VARCHAR2(128),
    INDEX_SIZE       NUMBER(18,0),
    INDEX_INFO       VARCHAR2(400),
    DIAGRAM          VARCHAR2(600),
    LOB_SIZE         NUMBER(18,0),
    SEG_ID           INT,
    SEG_TYPE         VARCHAR2(128),
    SEG_SIZE         NUMBER(18,0),
    EXTENTS          INT,
    EXTENT_SIZE      NUMBER(18,0),
    PAGE_COUNT       INT,
    MIN_PAGE_NO      INT,
    MIN_PAGE_POS_MB  NUMBER(18,2),
    MAX_PAGE_NO      INT,
    MAX_PAGE_POS_MB  NUMBER(18,2)
  );
  TYPE TT_DATAPOS IS TABLE OF TR_DATAPOS;
  
  V_DATAPOS TT_DATAPOS := TT_DATAPOS();
  
  TYPE TR_SEGS IS RECORD
  (
    SEG_ID    INT,
    SEG_TYPE  VARCHAR2(128)
  );
  TYPE TT_SEGS IS TABLE OF TR_SEGS;
  V_SEGS TT_SEGS := TT_SEGS();
  
  TYPE TR_FLS IS RECORD
  (
    FL_ID            INT,
    FILEPATH         VARCHAR2(1000),
    TOTAL_SIZE        BIGINT,
    MIN_PAGE_NO      INT,
    MIN_PAGE_POS_MB  NUMBER(18,2),
    MAX_PAGE_NO      INT,
    MAX_PAGE_POS_MB  NUMBER(18,2),
    PAGE_COUNT       INT,
    SEG_SIZE         NUMBER(18,2),
    EXTENTS          INT
  );
  TYPE TT_FLS IS TABLE OF TR_FLS;
  V_FLS TT_FLS := TT_FLS();
  
  V_ROW              INT := 0;
  V_DIAGRAM          VARCHAR2(600);
  V_PREV_IDX_ID      INT := 0;
  V_SEG_ID INT;  

  FUNCTION BINARY_TO_VB(PBIN VARCHAR2) 
  RETURN VARBINARY(16) DETERMINISTIC
  IS  
     V_LEN   INT := LENGTH (PBIN);  
     V_NUM   SMALLINT := 0;
     V_LOOP  INT := V_LEN / 8;
     V_RET   VARBINARY(32767);  
  BEGIN
    FOR I IN 1 .. V_LOOP LOOP
      V_NUM := 0;
      FOR J IN 1..8 LOOP  
        IF SUBSTR(PBIN,(I - 1) * 8 + J, 1) = '1' THEN  
          V_NUM := V_NUM + POWER(2, 8 - J);
        END IF;  
      END LOOP;  
      V_RET := V_RET || SUBSTRBB(CAST(V_NUM AS VARBINARY),2,1);
    END LOOP;  
    RETURN V_RET;  
  END BINARY_TO_VB; 

  FUNCTION VB_TO_BINARY(V_DATA VARBINARY(16))
  RETURN VARCHAR DETERMINISTIC
  IS
    V_NUM  SMALLINT;
    V_LEN  INT;
    V_RET  VARCHAR2(1000);
    V_BYTE VARCHAR2(100);
  BEGIN
    V_LEN := LENGTHB(V_DATA);
    FOR I IN 1 .. V_LEN LOOP
      V_NUM := CAST(SUBSTRBB(V_DATA,I,1) AS SMALLINT);
      V_BYTE := '';
      FOR J IN 1 .. 8 LOOP
        V_BYTE := MOD(V_NUM,2)||V_BYTE;
        V_NUM := TRUNC(V_NUM / 2);
      END LOOP;
      V_RET := V_RET || V_BYTE;
    END LOOP;
    RETURN V_RET;
  END VB_TO_BINARY;  
BEGIN
  FOR C1 IN (SELECT TS.ID      AS TS_ID
                   ,TS.NAME    AS TS_NAME
                   ,USR.ID     AS USER_ID
                   ,USR.NAME   AS USER_NAME
                   ,SCH.ID     AS SCHEMA_ID
                   ,SCH.NAME   AS SCHEMA_NAME
                   ,TAB.ID     AS TABLE_ID
                   ,TAB.NAME   AS TABLE_NAME
                   ,CAST(TABLE_USED_SPACE(SCH.NAME,TAB.NAME) AS NUMBER) * PAGE AS TAB_SIZE
                   ,IDX.ID     AS INDEX_ID
                   ,IDX.NAME   AS INDEX_NAME
                   ,CAST(INDEX_USED_SPACE(IDX.ID) AS NUMBER) * PAGE AS INDEX_SIZE
                   ,RTRIM(  DECODE(IDXINFO.FLAG & 0x01,0x00,'正常索引;')
                          ||DECODE(IDXINFO.FLAG & 0x01,0x01,'系统索引;')
                          ||DECODE(IDXINFO.FLAG & 0x02,0x02,'虚索引(VIRTUAL);')
                          ||DECODE(IDXINFO.FLAG & 0x04,0x04,'主键;')
                          ||DECODE(IDXINFO.FLAG & 0x08,0x08,'在临时表上;')
                          ||DECODE(IDXINFO.FLAG & 0x10,0x10,'无效索引;')
                          ||DECODE(IDXINFO.FLAG & 0x20,0x20,'fast pool;')
                          ||DECODE(IDXINFO.XTYPE & 0x01,0x00,CASE WHEN (TAB.INFO3 & 0x3F) IN (0x13, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27) THEN 'HUGE表聚集索引(HUGE CLUSTER);' ELSE '聚集索引(CLUSTER);' END)
                          ||DECODE(IDXINFO.XTYPE & 0x01,0x01,'二级索引;')
                          ||DECODE(IDXINFO.XTYPE & 0x02,0x02,'函数索引(FUNCTION-BASED NORMAL);')
                          ||DECODE(IDXINFO.XTYPE & 0x04,0x04,'全局索引在水平分区子表上;')
                          ||DECODE(IDXINFO.XTYPE & 0x08,0x08,'全局索引在水平分区主表上;')
                          ||DECODE(IDXINFO.XTYPE & 0x10,0x10,'唯一索引;')
                          ||DECODE(IDXINFO.XTYPE & 0x20,0x20,'扁平索引(FLAT);')
                          ||DECODE(IDXINFO.XTYPE & 0x40,0x40,'数组索引;')
                          ||DECODE(IDXINFO.XTYPE & 0x800,0x800,'该位图索引是由改造后创建;')
                          ||DECODE(IDXINFO.XTYPE & 0x1000,0x1000,'位图索引(BITMAP);')
                          ||DECODE(IDXINFO.XTYPE & 0x2000,0x2000,'位图连接索引(BITMAP);')
                          ||DECODE(IDXINFO.XTYPE & 0x4000,0x4000,'位图连接索引虚索引(BITMAP);')
                          ||DECODE(IDXINFO.XTYPE & 0x8000,0x8000,'空间索引;')
                          ||DECODE(IDXINFO.XTYPE & 0x10000,0x00,'','索引不可见;')
                          ||DECODE(IDXINFO.XTYPE & 0x20000,0x20000,'标识 DPC全局索引的根索引;')
                          ||DECODE(IDXINFO.XTYPE & 0x40000,0x40000,'标识 DPC全局索引的子索引;')
                         ,';') AS INDEX_INFO
                   ,CASE WHEN IDXINFO.XTYPE & 0x01 = 0x00 THEN 1 ELSE 0 END AS IS_CLUSTER
                   ,CAST(TABLE_USED_LOB_PAGES(SCH.NAME,TAB.NAME) AS NUMBER) * PAGE AS LOB_SIZE
               FROM SYSOBJECTS IDX
                   ,SYSINDEXES IDXINFO
                   ,SYSOBJECTS TAB
                   ,SYSOBJECTS SCH
                   ,SYSOBJECTS USR
                   ,V$TABLESPACE TS
              WHERE IDX.ID = IDXINFO.ID
                AND TAB.ID = IDX.PID
                AND SCH.ID = TAB.SCHID
                AND USR.ID = SCH.PID 
                AND USR.TYPE$ = 'UR'
                AND IDXINFO.GROUPID = TS.ID
                AND UPPER(TS.NAME) LIKE UPPER(V_TABLESPACE_NAME)
                AND UPPER(TAB.NAME) LIKE UPPER(V_TABLE_NAME)
                AND TS.STATUS$ = 0
              ORDER BY SCHEMA_NAME,TABLE_NAME,INDEX_ID
              ) LOOP
    V_SEGS.DELETE;
    SELECT SEG_ID,TYPE AS SEG_TYPE
      BULK COLLECT INTO V_SEGS
      FROM V$SEGMENT_INFOS 
     WHERE OBJ_ID = C1.INDEX_ID
       AND TS_ID = C1.TS_ID
     GROUP BY SEG_ID,TYPE
     ORDER BY SEG_ID;
   
    IF C1.IS_CLUSTER = 1 THEN
      --在某些版本或环境中,V$LOB_SEG 查询无结果时会抛异常,所以只能用循环加异常捕获方式来处理
      BEGIN
        V_SEG_ID := NULL;
        SELECT SEG_ID
          INTO V_SEG_ID
          FROM V$LOB_SEG
         WHERE TAB_ID = C1.TABLE_ID;
        IF V_SEG_ID IS NOT NULL THEN
          V_SEGS.EXTEND;
          V_SEGS(V_SEGS.COUNT).SEG_ID := V_SEG_ID;
          V_SEGS(V_SEGS.COUNT).SEG_TYPE := 'LOB_SEG';
        END IF;
      EXCEPTION WHEN OTHERS THEN
        NULL;
      END;
    END IF;
    FOR I_SEG IN 1 .. V_SEGS.COUNT LOOP
      V_FLS.DELETE;
      --在某些版本或环境中,V$SEGMENT_PAGES 查询无结果时会抛异常,所以只能用循环加异常捕获方式来处理
      BEGIN
        SELECT FL.ID,FL.PATH AS FILEPATH,FL.TOTAL_SIZE
              ,PGS.MIN_PAGE_NO
              ,ROUND(CAST(PGS.MIN_PAGE_NO AS NUMBER) * FL.PAGE_SIZE / 1024 / 1024,2) AS MAX_PAGE_POS_MB
              ,PGS.MAX_PAGE_NO
              ,ROUND(CAST(PGS.MAX_PAGE_NO AS NUMBER) * FL.PAGE_SIZE / 1024 / 1024,2) AS MAX_PAGE_POS_MB
              ,PGS.PAGE_COUNT
              ,CAST(PGS.PAGE_COUNT AS NUMBER) * FL.PAGE_SIZE AS SEG_SIZE
              ,NVL(EXTS.EXTENTS,0) AS EXTENTS
          BULK COLLECT INTO V_FLS
          FROM V$DATAFILE FL
          JOIN (SELECT FILE_ID
                      ,MIN(PAGE_NO) AS MIN_PAGE_NO
                      ,MAX(PAGE_NO) AS MAX_PAGE_NO
                      ,COUNT(1) AS PAGE_COUNT
                  FROM V$SEGMENT_PAGES 
                 WHERE GROUP_ID = C1.TS_ID
                   AND SEG_ID = V_SEGS(I_SEG).SEG_ID
                 GROUP BY FILE_ID
               ) PGS
            ON PGS.FILE_ID = FL.ID
          LEFT JOIN (SELECT FILE_ID,COUNT(1) AS EXTENTS
                       FROM V$EXTENTS
                      WHERE TS_ID = C1.TS_ID
                        AND SEG_ID = V_SEGS(I_SEG).SEG_ID
                      GROUP BY FILE_ID
                    ) EXTS
                 ON EXTS.FILE_ID = FL.ID
                       
         WHERE FL.GROUP_ID = C1.TS_ID;
      EXCEPTION WHEN OTHERS THEN
        NULL;
      END;
      FOR I_FL IN 1 .. V_FLS.COUNT LOOP
        SELECT '['||LISTAGG(NVL2(B.PCT,'▓',' ')) WITHIN GROUP (ORDER BY A.PCT)||']'
          INTO V_DIAGRAM
          FROM (SELECT LEVEL AS PCT CONNECT BY LEVEL <= 100) A
          LEFT JOIN (SELECT FILE_ID,CEIL(CAST(PAGE_NO AS NUMBER) / V_FLS(I_FL).TOTAL_SIZE * 100) AS PCT
                       FROM V$SEGMENT_PAGES 
                      WHERE GROUP_ID = C1.TS_ID 
                        AND SEG_ID = V_SEGS(I_SEG).SEG_ID
                      GROUP BY FILE_ID,CEIL(CAST(PAGE_NO AS NUMBER) / V_FLS(I_FL).TOTAL_SIZE * 100)
                     HAVING FILE_ID = V_FLS(I_FL).FL_ID
                    ) B
                 ON B.PCT = A.PCT;
         V_DATAPOS.EXTEND;
         V_ROW := V_DATAPOS.COUNT;

         V_DATAPOS(V_ROW).TS_ID            = C1.TS_ID;
         V_DATAPOS(V_ROW).TS_NAME          = C1.TS_NAME;
         V_DATAPOS(V_ROW).FL_ID            = V_FLS(I_FL).FL_ID;
         V_DATAPOS(V_ROW).FL_PATH          = V_FLS(I_FL).FILEPATH;
         V_DATAPOS(V_ROW).USER_ID          = C1.USER_ID;
         V_DATAPOS(V_ROW).USER_NAME        = C1.USER_NAME;
         V_DATAPOS(V_ROW).SCHEMA_ID        = C1.SCHEMA_ID;
         V_DATAPOS(V_ROW).SCHEMA_NAME      = C1.SCHEMA_NAME;
         V_DATAPOS(V_ROW).TABLE_ID         = C1.TABLE_ID;
         V_DATAPOS(V_ROW).TABLE_NAME       = C1.TABLE_NAME;
         V_DATAPOS(V_ROW).TAB_SIZE         = C1.TAB_SIZE;
         
         IF V_SEGS(I_SEG).SEG_TYPE = 'LOB_SEG' THEN
           V_DATAPOS(V_ROW).LOB_SIZE         = C1.LOB_SIZE;
           V_DATAPOS(V_ROW).INDEX_INFO       = 'LOB数据';
         ELSE
           V_DATAPOS(V_ROW).INDEX_ID         = C1.INDEX_ID;
           V_DATAPOS(V_ROW).INDEX_NAME       = C1.INDEX_NAME;
           IF V_PREV_IDX_ID <> C1.INDEX_ID THEN
             V_PREV_IDX_ID := C1.INDEX_ID;
             V_DATAPOS(V_ROW).INDEX_SIZE       = C1.INDEX_SIZE;
           END IF;
           V_DATAPOS(V_ROW).INDEX_INFO       = C1.INDEX_INFO;
         END IF;
         
         V_DATAPOS(V_ROW).SEG_ID           = V_SEGS(I_SEG).SEG_ID;
         V_DATAPOS(V_ROW).SEG_TYPE         = V_SEGS(I_SEG).SEG_TYPE;
         V_DATAPOS(V_ROW).SEG_SIZE         = V_FLS(I_FL).SEG_SIZE;
         V_DATAPOS(V_ROW).PAGE_COUNT       = V_FLS(I_FL).PAGE_COUNT;
         V_DATAPOS(V_ROW).EXTENTS          = V_FLS(I_FL).EXTENTS;--= V_SEGS(I_SEG).EXTENTS;
         V_DATAPOS(V_ROW).EXTENT_SIZE      = CAST(V_FLS(I_FL).EXTENTS AS NUMBER) * SF_GET_EXTENT_SIZE * PAGE;--CAST(V_SEGS(I_SEG).EXTENTS AS NUMBER) * SF_GET_EXTENT_SIZE * PAGE;
         
         V_DATAPOS(V_ROW).MIN_PAGE_NO      = V_FLS(I_FL).MIN_PAGE_NO;
         V_DATAPOS(V_ROW).MIN_PAGE_POS_MB  = V_FLS(I_FL).MIN_PAGE_POS_MB;
         V_DATAPOS(V_ROW).MAX_PAGE_NO      = V_FLS(I_FL).MAX_PAGE_NO;
         V_DATAPOS(V_ROW).MAX_PAGE_POS_MB  = V_FLS(I_FL).MAX_PAGE_POS_MB;
         V_DATAPOS(V_ROW).DIAGRAM          = V_DIAGRAM;
         
      END LOOP;
    END LOOP;
  END LOOP;
  
  --列出指定表空间数据文件使用情况
  SELECT TS.NAME AS TABLESPACE_NAME
        ,DF.PATH AS FILE_PATH
        ,ROUND(CAST(DF.TOTAL_SIZE * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS TOTAL_SIZE_MB
        ,ROUND(CAST((DF.TOTAL_SIZE - DF.FREE_SIZE) * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS USED_SIZE_MB
        ,ROUND(CAST(DF.FREE_SIZE * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS FREE_SIZE_MB
        ,CEIL(CAST(DF.FREE_PAGE_NO * PAGE AS NUMBER) / 1024.0 / 1024.0) AS LEAST_FILE_SIZE_MB
        --250423版本之后可用
        --,DT.DIAGRAM AS "DIAGRAM(with pred)"
        ,DT.USED_SIZE_MB AS "USED_SIZE_MB(with pred)"
        ,DT.MIN_PAGE_POSITION_MB
        ,DT.MAX_PAGE_POSITION_MB
    FROM (SELECT TS_ID
                ,FL_ID
                --250423版本之后可用
                --,'['||REPLACE(REPLACE(SUBSTR(VB_TO_BINARY(BIT_OR(BINARY_TO_VB(REPLACE(REPLACE(SUBSTR(DIAGRAM,2,100),'▓','1'),' ','0')||'0000'))),1,100),'0',' '),'1','▓')||']' AS DIAGRAM
                ,ROUND(CAST(SUM(EXTENT_SIZE) AS NUMBER) / 1024 / 1024,2) AS USED_SIZE_MB
                ,MIN(MIN_PAGE_POS_MB) AS MIN_PAGE_POSITION_MB
                ,MAX(MAX_PAGE_POS_MB) AS MAX_PAGE_POSITION_MB
            FROM TABLE(V_DATAPOS)
           GROUP BY TS_ID,FL_ID 
         ) DT
        ,V$DATAFILE DF
        ,V$TABLESPACE TS
   WHERE DF.GROUP_ID = DT.TS_ID
     AND DF.ID = DT.FL_ID
     AND TS.ID = DT.TS_ID
   ORDER BY TS.NAME,DF.PATH,MAX_PAGE_POSITION_MB DESC;

  --列出指定表空间内,指定表名范围的各表数据页在数据文件里的分布情况
  SELECT TS_NAME
        ,FL_PATH
        ,SCHEMA_NAME
        ,TABLE_NAME
        --250423版本之后可用
        --,'['||REPLACE(REPLACE(SUBSTR(VB_TO_BINARY(BIT_OR(BINARY_TO_VB(REPLACE(REPLACE(SUBSTR(DIAGRAM,2,100),'▓','1'),' ','0')||'0000'))),1,100),'0',' '),'1','▓')||']' AS DIAGRAM
        ,ROUND(CAST(SUM(EXTENT_SIZE) AS NUMBER) / 1024 / 1024,2) AS USED_SIZE_MB
        ,MIN(MIN_PAGE_POS_MB) AS MIN_PAGE_POSITION_MB
        ,MAX(MAX_PAGE_POS_MB) AS MAX_PAGE_POSITION_MB
    FROM TABLE(V_DATAPOS)
   GROUP BY TS_NAME,FL_PATH,SCHEMA_NAME,TABLE_NAME 
   ORDER BY TS_NAME,FL_PATH,MAX_PAGE_POSITION_MB DESC;
  --列出指定表空间内,指定表名范围的各表各种段的数据页分布情况
  SELECT * FROM TABLE(V_DATAPOS) ORDER BY TS_ID,FL_ID,MAX_PAGE_POS_MB DESC;
END;

这个SQL文件里是一个脚本,非侵入性的,也就是不会在库里创建对象或修改数据,只是做查询。

注意,因为 V$LOB_SEG、V$SEGMENT_PAGES 等系统动态视图查询时会扫描数据页,可能会耗时比较长。

这个脚本前面我已经修改表空间名为你用的这个PMSNB,脚本执行后会列出三个结果集:
结果集1:表空间下各数据文件的存储内容占用位置情况
结果集2:表空间下各表相关数据页面在数据文件中的位置占用情况
结果集3:表空间下各表、各数据段在数据文件中的位置占用情况

先看看这个脚本是否能正常执行,如果能正常跑,就看一下输出内容,关键看这三个结果集最后的 MAX_PAGE_POS/POSITION_MB,也就是数据页面在数据文件中所处的绝对位置最大值。

因为达梦是使用类似Oracle的数据文件存储方式,而不是每个表单独文件存储,所以当某个数据页面占用了数据文件的尾部位置,就可能会引起收缩失败。通过查询结果集的位置最大值,能够分析出是哪张表以及是哪个索引占用了数据文件尾部位置。

如果处于尾部的表不多,且表不是很大的话,可以测试下用ALTER TABLE MOVE TABLESPACE的方法把这个表的数据先挪到其他表空间,等当前表空间收缩成功后再移动回来。

或者,实在不行,就用dexp/dimp工具,把数据导入到其他表空间里。

另附

表空间相关视图

  • DBA_DATA_FILES
  • DBA_TABLESPACES
  • DBA_FREE_SPACE
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇