暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 逻辑存储架构

DBA随笔记 2024-12-21
87

1. Tablespace 表空间

1.1 TBS 的分类

    PERMANENT	#永久 TBS
    UNDO #撤销 TBS
    TEMPORARY #临时 TBS

    1.2 TBS 的管理方式

    重点是段的管理方式和区的管理方式是在建立 TBS 时确定的。

    1.2.1 段管理方式

    AUTO
    MANUAL
    两种

    1.2.2 区管理方式

    本地管理(locally managed
    )

      SQL> select tablespace_name,contents,extent_management,segment_space_management from dba_tablespaces;
      TABLESPACE_NAME CONTENTS EXTENT_MAN SEGMEN
      --------------- ----------- ---------- -------
      SYSTEM PERMANENT DICTIONARY MANUAL
      SYSAUX PERMANENT LOCAL AUTO
      TEMP TEMPORARY LOCAL MANUAL
      USERS PERMANENT LOCAL AUTO
      EXAMPLE PERMANENT LOCAL AUTO
      UNDO_TBS01 UNDO LOCAL MANUAL
      TMP01 TEMPORARY LOCAL MANUAL
      TBS_16K PERMANENT LOCAL AUTO


      1.3 创建 TBS

      1.3.1 建立缺省值的 TBS

        SQL> create tablespace a datafile '/u01/app/oracle/oradata/orcl/a01.dbf' size 10m;
        -- 利用oracle的dbms_metadata.get_ddl包查看缺省值
        SQL> set serverout on
        SQL> declare aa varchar2(2000);
        begin
        select dbms_metadata.get_ddl('TABLESPACE','A'into aa FROM dual;
        dbms_output.put_line(aa);
        end;
        /
        -- 结果:
        CREATE TABLESPACE "A" DATAFILE '/u01/app/oracle/oradata/orcl/a01.dbf' SIZE 10485760
        LOGGING ONLINE PERMANENT BLOCKSIZE 8192
        EXTENT
        MANAGEMENT LOCAL AUTOALLOCATE DEFAULT
        NOCompress SEGMENT SPACE MANAGEMENT AUTO


        PL/SQL procedure successfully completed.
        段自动管理 区本地管理且自动分配空间

        1.3.2 建立手动管理的 TBS

          SQL> create tablespace b datafile '/u01/app/oracle/oradata/orcl/b01.dbf' size 10m extent management local uniform size 128k segment space management manual;
          -- 调dbms_metadata.get_ddl包看oracle对该语句的ddl操作是:
          set serverout on
          SQL> declare aa varchar2(2000);
          begin
          select dbms_metadata.get_ddl('TABLESPACE','B'into aa FROM dual;
          dbms_output.put_line(aa);
          end;
          /
          CREATE TABLESPACE "B" DATAFILE
          '/u01/app/oracle/oradata/orcl/a01.dbf' SIZE 10485760
          LOGGING ONLINE PERMANENT BLOCKSIZE 8192
          EXTENT
          MANAGEMENT LOCAL UNIFORM SIZE 131072 DEFAULT
          NOCompress SEGMENT SPACE MANAGEMENT MANUAL
          -- 信息说明是:  区本地管理且统一分配128K, 段手动管理.

          1.4 查看 Table 对应的 TBS

            SQL> select table_name,tablespace_name from user_tables where table_name='EMP';
            TABLE_NAME TABLESPACE_NAME
            --------- ---------------
            EMP USERS

            1.5 查看 TBS 对应的数据文件

              SQL> col tablespace_name format a10
              SQL> col file_name format a45
              SQL> select tablespace_name, file_id,file_name, round(bytes/(1024*1024),0) total_space from dba_data_files order by tablespace_name;
              TABLESPACE_NAME FILE_ID FILE_NAME TOTAL_SPACE
              --------------- ------ ------------------------------------------ -------------
              EXAMPLE 5 /u01/app/oracle/oradata/orcl/example01.dbf 1244
              KLAUS 8 /u01/app/oracle/oradata/orcl/klaus01.dbf 100
              SYSAUX 3 /u01/app/oracle/oradata/orcl/sysaux01.dbf 990
              SYSTEM 1 /u01/app/oracle/oradata/orcl/system01.dbf 810
              TBS_16K 7 /u01/app/oracle/oradata/orcl/tbs16k01.dbf 10
              TEST 2 /u01/app/oracle/oradata/orcl/test01.dbf 10
              UNDOTBS1 4 /u01/app/oracle/oradata/orcl/undotbs01.dbf 100
              USERS 6 /u01/app/oracle/oradata/orcl/users01.dbf 620

              1.6 删除 TBS

                SQL> drop tablespace tbs_name including contents and datafiles;

                查看 TBS 空闲大小

                  SQL> select TABLESPACE_NAME,sum(bytes)/1024/1024 from dba_free_space group by tablespace_name;
                  TABLESPACE_NAME SUM(BYTES)/1024/1024
                  -------------- ------------------
                  UNDOTBS1 98.4375
                  SYSAUX 14.625
                  USERS 48.1875
                  SYSTEM 1.875
                  EXAMPLE 31.25

                  1.7 大文件 (bigfile) TBS (默认 small file)

                    -- small file	:在一个 TBS可以建立多个数据文件
                    -- bigfile :在一个 TBS只能建立一个数据文件  (8k的block ,datafile maxsize 可以 32T),可以简化对数据文件管理
                    SQL> create bigfile tablespace big_tbs datafile '/u01/app/oracle/oradata/orcl/bigtbs01.dbf' size 100m;
                    -- 试图在该 TBS下增加一个数据文件会报错
                    SQL> alter tablespace big_tbs add datafile '/u01/app/oracle/oradata/orcl/bigtbs02.dbf' size 100m;
                    alter tablespace big_tbs add datafile '/u01/app/oracle/oradata/orcl/bigtbs02.dbf' size 100m
                    *
                    ERROR at line 1:
                    ORA-32771:   cannot add file to bigfile tablespace
                    -- 查看大文件 TBS:
                    SQL> select name,bigfile from v$tablespace;
                    NAME BIG
                    --------- --------
                    SYSTEM NO
                    UNDOTBS1 NO
                    SYSAUX NO
                    USERS NO
                    TEMP NO
                    EXAMPLE NO
                    TBS_16K NO
                    BIG_TBS YES

                    2. SEGMENT (段)

                    2.1 SEGMENT 概述

                    TBS 在逻辑上可以对应多个段,物理上可以对应多个数据文件,一个段SEGMENT
                    比较大时可以跨多个数据文件 Data File。

                    创建一个表,ORACLE 为表创建一个 (或多个) 段,在一个段中保存该表的所有表数据(表数据不能跨段)。

                    段中至少有一个初始的区extent
                    。当 SEGMENT 的数据增加,区extent
                    不够时,将为这个段SEGMENT
                    分配新的区。

                    2.2 段的两种管理方式

                    ASSM自动管理方式(Auto Segment Space Management
                    ) (缺省)

                    2.3 表和段 (segment) 的关系

                    一般一个单纯的表就分配一个段,但往往表上经常会有主键约束,那么就会有索引,索引有索引段,还有分区表,每个分区会有独立的段。再有就是 oracle 的大对象, 如果表里引用blob
                    clob
                    ,那么这个表就又被分出多个段来。

                      SQL> conn / as sysdba
                      SQL> create user klaus identified by klaus;
                      SQL> grant connect,resource to klaus;
                      SQL> conn klaus/klaus
                      SQL> select * from user_segments;
                      no rows selected
                      SQL> create table t1 (id int);

                      从 11GR2 开始默认创建的表不会立即分配segment
                      ,不会占用磁盘空间,当第一条数据insert
                      时才会分配空间。

                        SQL> select segment_name from user_segments;
                        no rows selected
                        SQL> conn / as sysdba
                        SQL> grant insert on klaus.t1 to klaus;
                        Grant succeeded.
                        SQL> grant unlimited tablespace to klaus;
                        Grant succeeded.
                        SQL> conn klaus/klaus
                        SQL> insert into t1 values(1);
                        SQL> select segment_name from user_segments; -- 只建立一个表时,一个表对应一个段
                        SEGMENT_NAME
                        -----------------------
                        T1
                        SQL> create table t2 (id int constraint pk_t2 primary key, b blob, c clob);
                        SQL> insert into t2 values(1,rawtohex('klaus'),rawtohex('yao'));
                        SQL> select segment_name, segment_type from user_segments; -- 表添加了主键约束,有了索引,添加了大对象,则分配多个段
                        SEGMENT_NAME SEGMENT_TYPE
                        ----------------------- -------------------------
                        PK_T2 INDEX
                        SYS_IL0000095333C00002$$ LOBINDEX
                        SYS_IL0000095333C00003$$ LOBINDEX
                        SYS_LOB0000095333C00002$$ LOBSEGMENT
                        SYS_LOB0000095333C00003$$ LOBSEGMENT -- 大对象特殊,有两个段
                        T2 TABLE
                        T1 TABLE

                        2.4 延迟段创建

                        2.4.1 概述

                        Oracle 11gR2 又增加了一个新的初始化参数DEFERRED_SEGMENT_CREATION
                         (仅适用未分区的heap table
                        ),此参数设为TRUE
                        后,create table
                        后并不马上分配segment
                        ,实际的表段 (Table Segement
                        ) 被延迟到第一行数据插入时创建。延迟段创建可以节省空间,加快初始化过程,是面向性能和资源的一个优化,这对于应用程序的部署可能有些好处。也可以使局部设置改变这一功能 (覆盖DEFERRED_SEGMENT_CREATION
                        ),在create table
                        语句时加上SEGMENT CREATION
                        子句指定。如:

                          -- 指定表空间
                          create table scott.t1(id int,name char(10)) SEGMENT CREATION IMMEDIATE TABLESPACE TB1;
                          -- 默认表空间
                          -- 或延迟创建
                          create table scott.t1(id int,name char(10)) SEGMENT CREATION IMMEDIATE;
                          -- 缺省在11gR2
                          create table scott.t1(id int,name char(10)) SEGMENT CREATION DEFERRED;

                          2.4.2 产生的问题及解决

                          该新特性会带来一个问题:在使用exp/imp
                          进行导出导入时,不会包含这些空表 (数据泵expdp
                          不存在这个问题,expdp
                          可以导出空表),所以,在迁移数据的时候可能会导致遗漏部分空表。针对这个问题的解决方法有:

                          2.4.3.1 Insert:

                          INSERT
                          一行,再ROLLBACK
                          或者删除就可以产生SEGMENT
                          了。该方法是在空表中插入数据,再删除,则会产生SEGMENT
                          ,此时再导出时就可以导出空表。

                          2.4.3.2 设置 DEFERRED_SEGMENT_CREATION 参数

                          设置DEFERRED_SEGMENT_CREATION
                          参数为FALSE
                          来禁用 ”延迟段创建”,无论是空表还是非空表,都会分配SEGMENT

                          提示需要注意的是,该值设置后只对后面新增的表产生作用,对于之前建立的空表 (已经存在的) 不起作用,仍不能导出。

                          2.4.3.3 使用 ALLOCATE EXTENT

                          使用ALLOCATE EXTENT
                          可以为数据库的每一张表分配EXTENT
                          。批量生成脚本:

                            -- 注意修改用户名 
                            SELECT 'ALTER TABLE '||D.OWNER||'.'||D.TABLE_NAME||' ALLOCATE EXTENT;' 
                            EXEC_SQL FROM DBA_TABLES D WHERE D.SEGMENT_CREATED='NO' AND D.OWNER IN ('BDC','ABC');

                            执行以上 SQL 产生的脚本后即可为每一个空表分配段,然后执行 exp 命令即可。

                            2.5 立即分配段

                              -- 指定表空间
                              create table scott.t1(id int,name char(10)) SEGMENT CREATION IMMEDIATE TABLESPACE TB1;
                              -- 默认表空间
                              create table scott.t1(id int,name char(10)) SEGMENT CREATION IMMEDIATE;

                              3. EXTENT (区)

                              3.1 EXTENT (区) 的概述

                                区是 ORACLE 进行存储空间分配的最小单位。
                                是由一系列逻辑上连续的 Oracle 数据块组成的逻辑存储结构。
                                段中第一个区叫初始区,随后分配的区叫后续区。当段中所有的空间使用完后,ORACLE 自动为该段分配新的区。
                                区不可以跨越多个数据文件或者段。
                                Data blocks in an extent are logically contiguous but can be non-contiguous on disk:逻辑块连续、物理块可以是非连续的。
                                The blocks of a newly allocated extent,although free,may have been used before.

                                3.2 区的管理方式

                                本地管理:在每个数据文件中使用位图管理空间的分配。TBS 中所有区 (extent
                                ) 的分配信息都保存在该 TBS 对应的数据文件的头部。
                                优点:速度快,存储空间的分配和回收只是简单地改变数据文件中的位图,而不像字典管理方式还需要修改数据库。无碎片,更易于 DBA 维护。

                                3.3 表和区 (extent) 的关系

                                建立表的时候建立段 (11gR2 默认延迟创建段),然后自动分配相应的extent(1 个或多个),亦可以手工提前分配extent(用于需大量插入数据的表)。也就是说,在这个对象创建以后,ORACLE 至少给它分配一个区,初始大小是 64K,一个标准块的大小是 8K,刚好是 8 个BLOCK。

                                3.4 查看段的初始区

                                3.4.1 初始区 ID 为 0

                                  -- sys:
                                  SQL> drop tablespace test including contents and datafiles;
                                  SQL> drop table scott.t1 purge;
                                  SQL> create tablespace test datafile '/u01/app/oracle/oradata/orcl/test01.dbf' size 10m;
                                  SQL> create table scott.t1 tablespace test as select * from scott.dept;
                                  SQL> col segment_name for a15;
                                  SQL> select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T1';
                                  SEGMENT_NAME FILE_ID EXTENT_ID BYTES
                                  ------------ ------- --------- --------
                                  T1 6 0 65536
                                  T1 6 0 65536
                                  -- 可以看到段T1的初始区ID为0,大小为 65536 bytes;

                                  3.4.2 分配更多的区

                                    -- 向表中插入数据,看Oracle为该段分配更多的区
                                    SQL> insert into scott.t1 select * from scott.t1;
                                    /
                                    /
                                    /

                                    2048 rows created.
                                    SQL> select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T1';
                                    SEGMENT_NAME FILE_ID EXTENT_ID BYTES
                                    ------------ ------- --------- --------
                                    T1 6 0 65536
                                    T1 2 0 65536
                                    T1 2 1 65536
                                    T1 2 2 65536
                                    -- 此时看到随着数据的插入,T1段动态扩展为四个区;

                                    3.4.3 delete 表数据 extent 仍存在

                                      SQL> delete scott.t1;
                                      4096 rows deleted.
                                      SQL> select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T1';

                                      此时表段的数据已经删除,但所有extent
                                      依然健在,无法回收 T1 段的所有区。

                                      3.4.4 要求预分配的区空间

                                      可以要求一个预分配所需要的空间 (但要注意,所要的空间 一定是在 TBS 可达到的size
                                      范围内)。

                                        -- 如EXTENT_ID:3-7是预分配的空间
                                        SQL> alter table scott.t1 allocate extent (datafile '/u01/app/oracle/oradata/orcl/test01.dbf' size 5m);
                                        SQL> select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T1';
                                        SEGMENT_NAME FILE_ID EXTENT_ID BYTES
                                        ------------ ------- --------- --------
                                        T1 6 0 65536
                                        T1 2 0 65536
                                        T1 2 1 65536
                                        T1 2 2 65536
                                        T1 2 3 1048576
                                        T1 2 4 1048576
                                        T1 2 5 1048576
                                        T1 2 6 1048576
                                        T1 2 7 1048576

                                        3.4.5 回收未使用过的 extent

                                        回收free extent
                                        , 使用deallocate
                                        只能收回从未使用的 extent

                                          SQL> alter table scott.t1 deallocate unused;
                                          Table altered.
                                          SQL> select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T1';
                                          SEGMENT_NAME FILE_ID EXTENT_ID BYTES
                                          ------------ ------- --------- --------
                                          T1 6 0 65536
                                          T1 2 0 65536
                                          T1 2 1 65536
                                          T1 2 2 65536

                                          3.5 表对应的数据文件和 TBS

                                            -- 如何查看一个表所对应的数据文件及 TBS ?
                                            -- 抓住上面dba_extents中的file_id字段(user_extents里没有这个字段),然后:
                                            SQL> col file_name for a45;
                                            SQL> select file_id,file_name,tablespace_name from dba_data_files;
                                            FILE_ID FILE_NAME TABLESPACE_NAME
                                            --------- ------------------------------------------ ---------------
                                                    1 /u01/app/oracle/oradata/orcl/system01.dbf SYSTEM
                                                    3 /u01/app/oracle/oradata/orcl/sysaux01.dbf SYSAUX
                                                    4 /u01/app/oracle/oradata/orcl/undotbs01.dbf UNDOTBS1
                                                    6 /u01/app/oracle/oradata/orcl/users01.dbf USERS
                                                    5 /u01/app/oracle/oradata/orcl/example01.dbf EXAMPLE
                                                    7 /u01/app/oracle/oradata/orcl/tbs16k01.dbf TBS_16K
                                                    8 /u01/app/oracle/oradata/orcl/a01.dbf A
                                                    9 /u01/app/oracle/oradata/orcl/b01.dbf B
                                                    2 /u01/app/oracle/oradata/orcl/test01.dbf TEST

                                            4. BLOCK (数据块)

                                            4.1 BLOCK (数据块) 概述

                                            BLOCK是 Oracle 进行存储空间IO操作的最小单位; 

                                            data block:Oracle 11g 标准块:8k,支持 2-32k,由(block header+free space+data)组成。 

                                            BLOCK的管理方法是区的管理和段管理的具体体现: 

                                            4.2 BLOCK 管理方式

                                            4.2.1 ASSM (位图)自动管理方式

                                            如创建TBS
                                            时区为本地管理方式,并且将段的存储空间方式设置为AUTO
                                            (即ASSM
                                            ),该TBS
                                            的所有块均采用位图自动管理方式,系统默认方式。

                                            4.3 数据块头部

                                              ITL		#:事务槽,可以有多个ITL以支持并发事务
                                              # 每当一个事务要更新数据块里的数据时,必须先得到一个ITL槽,
                                              # 然后将当前事务ID,事务所用的undo数据块地址,SCN号,当前事务是否提交等信息写到ITL槽里
                                              Initrans #:初始化事务槽的个数,表默认1,index 默认为2;
                                              Maxtrans #:最大的事务槽个数(默认255)
                                              ROW DIR #:行目录,指向空闲行起始和结束的偏移量.
                                              NOTE #:使块头增加的可能情况是,row entries增加,增加更多的ITL空间.

                                              4.4 空闲列表方式的数据块管理

                                              4.4.1 Freelist

                                              空闲列表中登记了可以插入数据的可用块,位置在段头,插入表行数据时首先查找该列表。

                                              4.4.2 Pctfree

                                              用来为一个块保留的空间百分比,以防止在今后的更新操作中增加一列或多列值的长度。达到该值,从freelist清除该块信息。

                                              4.4.3 Pctused

                                              一个块的使用水位的百分比,这个水位将使该块返回到可用列表中去等待更多的插入操作。达到该值,该块信息加入 freelist。Pctused 参数在 ASSM 下不使用。

                                              1.建表时,注意 PCTFREE 参数的作用

                                              PCTFREE:为一个块保留的空间百分比,表示数据块在什么情况下可以被insert,默认是 10,表示当数据块的可用空间低于 10% 后,就不可以被insert了,只能被用于update;

                                              即:当使用一个 block 时,在达到pctfree之前,该block是一直可以被插入的,这个时候处在上升期

                                              PCTUSED:是指当块里的数据低于多少百分比时,又可以重新被insert,一般默认是 40,即 40%,即:当数据低于 40% 时,又可以写入新的数据,这个时候处在下降期。

                                              2.举例说明1 

                                              假设你一个块可以存放 100 个数据,而且PCTFREE是 10,PCTUSED是 40,则:不断的向块中插入数据,如果当存放到 90 个时,就不能存放新的数据,这是受pctfree来控制,预留的空间是给UPDATE用的。

                                              当删除一个数据后,再想插入个新数据行不行?不行,必须是删除 41 个,即低于 40 个以后才能插入新的数据的,这是受pctused来控制的。 

                                              3.举例说明2 

                                              注意:如果  TBS 上启用了ASSM,在建立表的时候,只能指定 PCTFREE,否则可用指定PCTFREE和PCTUSED。 

                                              举例:比如一个块的pctfree定为 10%,pctused定为 40%(oracle 默认设置)。那么一个块的使用率达到90% (即1-10%) 的时候,oracle将该块从freelist中移除,停止使用该块来插入数据(但可更新)。

                                              后来该块上发生了一些删除操作,使得该块的利用率下降,当使用率下降到 40% 以下的时候,oracle 重新将该块加入freelist,可用于新的插入

                                              4.4.4 行链接 (row chaining)

                                              指一行存储在多个块中的情况,这是因为该行的长度超过了一个块的可用空间大小,即行链接是跨越多块的行。

                                              4.4.5 行迁移 (row migration)

                                              指一个数据行由于update
                                              语句导致当前块被重新定位到另一个块(那里有充足的空间),但在原始块中保留一个指针的情形。原始块中的指针是必需的,因为索引的ROWID
                                              项仍然指向原始位置。

                                              4.4.6 如何鉴定行链接或行迁移?

                                              查看dba_tables
                                              AVG_ROW_LEN
                                              列和CHAIN_CNT
                                              列,当CHAIN_CNT
                                              有值时,看AVG_ROW_LEN
                                              ,它表示行的平均长度(byte
                                              )。

                                              如果AVG_ROW_LEN
                                              <块大小,则是迁移行,如果AVG_ROW_LEN
                                              >块大小,则是链接行。

                                                SQL> create table t1(c1 varchar2(20));
                                                SQL>
                                                begin
                                                for i in 1..1000 loop insert into t1 values(null)
                                                end loop;
                                                end;
                                                /
                                                -- 分析t1表确定无行迁移
                                                SQL> analyze table t1 compute statistics;
                                                SQL> select pct_free,pct_used,avg_row_len,chain_cnt from user_tables where table_name='T1';
                                                PCT_FREE PCT_USED AVG_ROW_LEN CHAIN_CNT
                                                -------- -------- ----------- ---------
                                                10 3 0
                                                -- 填充这些空列,再分析t1,有了行迁移
                                                SQL> update t1 set c1='klausyao is my name';
                                                SQL> analyze table t1 compute statistics;
                                                SQL> select pct_free,pct_used,avg_row_len,chain_cnt from user_tables where table_name='T1';
                                                PCT_FREE PCT_USED AVG_ROW_LEN CHAIN_CNT
                                                -------- -------- ----------- ---------
                                                10 28 886
                                                -- move表,再分析t1,行迁移消失.
                                                -- 思考: 段重组对于行链接有效吗?
                                                SQL> alter table t1 move;
                                                SQL> analyze table t1 compute statistics;
                                                SQL> select pct_free,pct_used,avg_row_len,chain_cnt from user_tables where table_name='T1';
                                                PCT_FREE PCT_USED AVG_ROW_LEN CHAIN_CNT
                                                -------- -------- ----------- ---------
                                                10 21 0

                                                逻辑结构:tablespace  Segment  extent  block

                                                文章转载自DBA随笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                评论