Oralce學習筆記(六)

邏輯體系結構與Data file sql

 

 邏輯體系結構關係圖oop

 

 邏輯體系結構最小單位BLOCK的研究 性能

 block能裝多少行數據:測試

drop table test_block_num purge;
create table test_block_num (id varchar2(1));

begin
 for i  in 1..8000 loop
  insert into test_block_num values('a');
 end loop;
 commit;
end;
/

--測試發現,不行。
select f, b, count(*)
  from (select dbms_rowid.rowid_relative_fno(rowid) f,
               dbms_rowid.rowid_block_number(rowid) b
          from test_block_num)
 group by f, b;


根本緣由:每行的其餘開銷致使每行的最小長度在11個字節左右,因此一個8K的塊的行理論上最多可用存儲8096/11=736行

-------------------------------------------------------------------------------------------------------------
SQL> select f, b, count(*)
  2    from (select dbms_rowid.rowid_relative_fno(rowid) f,
  3                 dbms_rowid.rowid_block_number(rowid) b
  4            from test_block_num)
  5   group by f, b;

         F          B   COUNT(*)
---------- ---------- ----------
        11        197        660
        11        198        660
         6       1957        660
         6       1955        660
         6       1956        660
        11        194        660
        11        196        660
        11        193        660
        11        192        660
        11        195         80
         6       1958        660
        11        199        660
         6       1959        660

  行遷移的成因和優化:大數據

成因:當行被Update時,若是Update更新的行大於數據塊得PCTFREE值,就須要申請第2個塊,從而造成行遷移。
後果:致使應用須要訪問更多的數據塊,性能降低。
預防:1. 將數據塊的PCTFREE調大;2. 針對表空間擴大數據塊大小
檢查:analyze table 表名 validate structure cascade into chained_rows;優化

 

操做:(以EMPLOYEES表爲例,若是涉及到該表有主鍵,而且有別的表的外鍵REFERENCE關聯到本表,必需要執行步驟2和步驟7,不然沒必要執行):
1. 執行腳本建立chained_rows表。
2. 禁用全部其它表上關聯到此表上的全部限制(假想EMPLOYEES表有主鍵PK_EMPLOYEES_ID,假想test表有外鍵f_employees_id關聯reference到employees表)。
select index_name,index_type,table_name from user_indexes where table_name='EMPLOYEES';
select CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME from USER_CONSTRAINTS where R_CONSTRAINT_NAME='PK_EMPLOYEES_ID';
alter table test disable constraint f_employees_id;
3. 將存在有行遷移的表(用table_name代替)中的產生行遷移的行的rowid放入到chained_rows表中。
4. 將表中的行遷移的row id放入臨時表中保存。
5. 刪除原來表中存在的行遷移的記錄行。
6. 從臨時表中取出並從新插入那些被刪除了的數據到原來的表中,並刪除臨時表。
7. 啓用全部其它表上關聯到此表上的全部限制。
alter table test enable constraint f_employees_id;
此外還能夠採用move和exp/imp的方式(特別注意move會致使索引失效,須要重建索引)。spa

--- PCTFREE試驗準備之建表
DROP TABLE EMPLOYEES PURGE;
CREATE TABLE EMPLOYEES AS SELECT * FROM HR.EMPLOYEES ;
desc EMPLOYEES;
create index idx_emp_id on employees(employee_id);

--- PCTFREE試驗準備之擴大字段
alter table EMPLOYEES modify FIRST_NAME VARCHAR2(1000);
alter table EMPLOYEES modify LAST_NAME  VARCHAR2(1000);
alter table EMPLOYEES modify EMAIL VARCHAR2(1000);
alter table EMPLOYEES modify PHONE_NUMBER  VARCHAR2(1000);

--- PCTFREE試驗準備之更新表
UPDATE EMPLOYEES
  SET FIRST_NAME = LPAD('1', 1000, '*'), LAST_NAME = LPAD('1', 1000, '*'), EMAIL = LPAD('1', 1000, '*'),
  PHONE_NUMBER = LPAD('1', 1000, '*');
COMMIT;

---行遷移優化前,先看看該語句邏輯讀狀況(執行計劃及代價都同樣)
SET AUTOTRACE traceonly 
set linesize 1000
select /*+index(EMPLOYEES,idx_emp_id)*/ * from EMPLOYEES  where employee_id>0;
/
set autotrace off 

----- 發現存在行遷移的方法
--首先建chaind_rows相關表,這是必需的步驟

drop table chained_rows purge;
@?/rdbms/admin/utlchain.sql
----如下命令針對EMPLOYEES表和EMPLOYEES_BK作分析,將產生行遷移的記錄插入到chained_rows表中
analyze table EMPLOYEES list chained rows into chained_rows;
select count(*)  from chained_rows where table_name='EMPLOYEES';

---如下方法能夠去除行遷移

drop table EMPLOYEES_TMP;
create table EMPLOYEES_TMP as select * from EMPLOYEES where rowid in (select head_rowid from chained_rows);
Delete from EMPLOYEES where rowid in (select head_rowid from chained_rows);
Insert into EMPLOYEES select * from EMPLOYEES_TMP;
delete from chained_rows ;
commit;
analyze table EMPLOYEES list chained rows into chained_rows;
select count(*)  from chained_rows where table_name='EMPLOYEES';
--這時的取值必定爲0。

  

 行鏈接:orm

產生緣由:當一行數據大於一個數據塊,ORACLE會同時分配兩個數據塊,並在第一個塊上登記第二個塊的地址,從而造成行連接。blog

預防方法:針對表空間擴大數據塊大小。索引

檢查行遷移的語句:

analyze table 表名 validate structure cascade into chained_rows;

  測試語句:

--- PCTFREE試驗準備之建表
DROP TABLE EMPLOYEES PURGE;
CREATE TABLE EMPLOYEES AS SELECT * FROM HR.EMPLOYEES ;
desc EMPLOYEES;
create index idx_emp_id on employees(employee_id);


--- PCTFREE試驗準備之擴大字段
alter table EMPLOYEES modify FIRST_NAME VARCHAR2(2000);
alter table EMPLOYEES modify LAST_NAME  VARCHAR2(2000);
alter table EMPLOYEES modify EMAIL VARCHAR2(2000);
alter table EMPLOYEES modify PHONE_NUMBER  VARCHAR2(2000);

--- PCTFREE試驗準備之更新表
UPDATE EMPLOYEES
  SET FIRST_NAME = LPAD('1', 2000, '*'), LAST_NAME = LPAD('1', 2000, '*'), EMAIL = LPAD('1', 2000, '*'),
  PHONE_NUMBER = LPAD('1', 2000, '*');
COMMIT;


---行連接移優化前,先看看該語句邏輯讀狀況
SET AUTOTRACE traceonly 
set linesize 1000
select /*+index(EMPLOYEES,idx_emp_id)*/ * from EMPLOYEES  where employee_id>0
/
set autotrace off 

----- 發現存在行連接的方法
--首先建chaind_rows相關表,這是必需的步驟
drop table chained_rows purge;
----如下命令針對EMPLOYEES表和EMPLOYEES_BK作分析,將產生行遷移的記錄插入到chained_rows表中
analyze table EMPLOYEES list chained rows into chained_rows;
select count(*)  from chained_rows where table_name='EMPLOYEES';

---用消除行遷移的方法根本沒法消除行連接!!!

drop table EMPLOYEES_TMP;
create table EMPLOYEES_TMP as select * from EMPLOYEES where rowid in (select head_rowid from chained_rows);
Delete from EMPLOYEES where rowid in (select head_rowid from chained_rows);
Insert into EMPLOYEES select * from EMPLOYEES_TMP;
delete from chained_rows ;
commit;
--發現用消除行遷移的方法根本沒法消除行連接!
analyze table EMPLOYEES list chained rows into chained_rows;
select count(*)  from chained_rows where table_name='EMPLOYEES';

SET AUTOTRACE traceonly 
set linesize 1000
select /*+index(EMPLOYEES,idx_emp_id)*/ * from EMPLOYEES  where employee_id>0


---啓動大小爲32K的塊新建表空間(WINDOWS下只能使用2K,4K,8K和16K)
--行連接只有經過加大BLOCK塊的方式才能夠避免,以下:
create tablespace TBS_LJB_16k 
blocksize 16K
datafile  'D:\ORACLE\ORADATA\TEST11G\TBS_LJB_32K_01.DBF' size 100M  
autoextend on  
extent management local 
segment space management auto;

DROP TABLE EMPLOYEES_BK PURGE;
CREATE TABLE EMPLOYEES_BK TABLESPACE TBS_LJB_16K AS SELECT * FROM EMPLOYEES;
delete from chained_rows ;
commit;
analyze table EMPLOYEES_BK list chained rows into chained_rows;
select count(*)  from chained_rows where table_name='EMPLOYEES_BK';
SQL> --發現用消除行遷移的方法根本沒法消除行連接!
SQL> analyze table EMPLOYEES list chained rows into chained_rows;
表已分析。
SQL> select count(*)  from chained_rows where table_name='EMPLOYEES';
  COUNT(*)
----------
       107
       
SQL> --行連接只有經過加大BLOCK塊的方式才能夠避免,以下:       
SQL> DROP TABLE EMPLOYEES_BK PURGE;
表已刪除。
SQL> CREATE TABLE EMPLOYEES_BK TABLESPACE TBS_LJB_16K AS SELECT * FROM EMPLOYEES;
表已建立。
SQL> delete from chained_rows ;
已刪除107行。
SQL> commit;
提交完成。
SQL> analyze table EMPLOYEES_BK list chained rows into chained_rows;
表已分析。
SQL> select count(*)  from chained_rows where table_name='EMPLOYEES_BK';
  COUNT(*)      

 

 Oralce中segment:

 

 

 

 

---構造t表
drop table t purge;
create table t tablespace tbs_ljb as select * from dba_objects where rownum=1 ;
col segment_name format a15
col segment_type format a10
col tablespace_name format a20
col blocks  format 9999
col extents format 9999

---查詢數據字典獲取segment相關信息
---建一個T表就產生了表段,T段(SEGMENT),請觀察區(EXTENT)及BLOCKS的個數。以下:
select segment_name, 
       segment_type,
       tablespace_name,
       blocks,extents,
       bytes/1024/1024 
from user_segments  where segment_name = 'T';
select count(*) from   user_extents  WHERE segment_name='T';

---建一個索引IDX_OBJ_ID就產生了索引段,IDX_OBJ_ID段(SEGMENT),和表的狀況相似,以下:
create index idx_obj_id on t(object_id);

select segment_name, 
       segment_type,
       tablespace_name,
       blocks,
       extents,
       bytes/1024/1024 
from user_segments  
where segment_name = 'IDX_OBJ_ID';
select count(*) from  user_extents  WHERE segment_name='IDX_OBJ_ID';

---插入數據後繼續觀察
insert into t select * from dba_objects ;
commit;

---隨着T表數據不斷增長,區(EXTENT)也不斷增多。以下:
select segment_name, 
       segment_type,
       tablespace_name,
       blocks,
       extents,bytes/1024/1024 
from user_segments  
where segment_name = 'T';
select count(*) from   user_extents  WHERE segment_name='T';

---隨着IDX_OBJ_ID不斷增大,區(EXTENT)也不斷增多。以下:
select segment_name, 
       segment_type,
       tablespace_name,
       blocks,
       extents,
       bytes/1024/1024 
from user_segments  
where segment_name = 'IDX_OBJ_ID';
select count(*) from   user_extents  WHERE segment_name='IDX_OBJ_ID';

----------------------------------------------------------------------------------------------------------------------------------------------
SQL> ---建一個T表就產生了表段,T段(SEGMENT),觀察區(EXTENT)及BLOCKS的個數。以下:
SQL> select segment_name,
  2         segment_type,
  3         tablespace_name,
  4         blocks,extents,
  5         bytes/1024/1024
  6  from user_segments  where segment_name = 'T';
SEGMENT_NAME    SEGMENT_TY TABLESPACE_NAME      BLOCKS EXTENTS BYTES/1024/1024
--------------- ---------- -------------------- ------ ------- ---------------
T               TABLE      TBS_LJB                   8       1           .0625

SQL>---建一個索引IDX_OBJ_ID就產生了索引段,和表狀況相似,以下:
SQL> create index idx_obj_id on t(object_id);
索引已建立。
SQL> select segment_name,
  2         segment_type,
  3         tablespace_name,
  4         blocks,
  5         extents,
  6         bytes/1024/1024
  7  from user_segments
  8  where segment_name = 'IDX_OBJ_ID';
SEGMENT_NAME    SEGMENT_TY TABLESPACE_NAME      BLOCKS EXTENTS BYTES/1024/1024
--------------- ---------- -------------------- ------ ------- ---------------
IDX_OBJ_ID      INDEX      TBS_LJB                   8       1           .0625


SQL> ---插入數據後繼續觀察
SQL> insert into t select * from dba_objects ;
已建立72882行。
SQL> commit;
提交完成。
SQL> ---隨着T表數據不斷增長,區(EXTENT)及BLOCKS的個數也不斷增多。以下:
SQL> select segment_name,
  2         segment_type,
  3         tablespace_name,
  4         blocks,
  5         extents,bytes/1024/1024
  6  from user_segments
  7  where segment_name = 'T';
SEGMENT_NAME    SEGMENT_TY TABLESPACE_NAME      BLOCKS EXTENTS BYTES/1024/1024
--------------- ---------- -------------------- ------ ------- ---------------
T               TABLE      TBS_LJB                1152      24               9

SQL> ---隨着IDX_OBJ_ID不斷增大,區(EXTENT)及BLOCKS的個數也不斷增多。以下:
SQL> select segment_name,
  2         segment_type,
  3         tablespace_name,
  4         blocks,
  5         extents,
  6         bytes/1024/1024
  7  from user_segments
  8  where segment_name = 'IDX_OBJ_ID';
SEGMENT_NAME    SEGMENT_TY TABLESPACE_NAME      BLOCKS EXTENTS BYTES/1024/1024
--------------- ---------- -------------------- ------ ------- ---------------
IDX_OBJ_ID      INDEX      TBS_LJB                 384      18               3

  

--- 查看Oracle 數據、臨時、回滾、系統表空間狀況

--查看錶空間的整體狀況

 SELECT A.TABLESPACE_NAME "表空間名",
       A.TOTAL_SPACE "總空間(G)",
       NVL(B.FREE_SPACE, 0) "剩餘空間(G)",
       A.TOTAL_SPACE - NVL(B.FREE_SPACE, 0) "使用空間(G)",
       CASE WHEN A.TOTAL_SPACE=0 THEN 0 ELSE trunc(NVL(B.FREE_SPACE, 0) / A.TOTAL_SPACE * 100, 2) END "剩餘百分比%" --避免分母爲0
  FROM (SELECT TABLESPACE_NAME, trunc(SUM(BYTES) / 1024 / 1024/1024 ,2) TOTAL_SPACE
          FROM DBA_DATA_FILES
         GROUP BY TABLESPACE_NAME) A,
       (SELECT TABLESPACE_NAME, trunc(SUM(BYTES / 1024 / 1024/1024  ),2) FREE_SPACE
          FROM DBA_FREE_SPACE
         GROUP BY TABLESPACE_NAME) B
 WHERE A.TABLESPACE_NAME = B.TABLESPACE_NAME(+)
 ORDER BY 5;
 
---建立用戶表空間
create tablespace TBS_LJB
datafile  'D:\ORACLE\ORADATA\TEST11G\TBS_LJB01.DBF'  size 100M
extent management local
segment space management auto;


col file_name format a50
col BYTES format 999999999999 
set linesize 366
SELECT file_name, tablespace_name, autoextensible,bytes
        FROM DBA_DATA_FILES
       WHERE TABLESPACE_NAME = 'TBS_LJB'
       order by substr(file_name, -12);
       
---臨時表空間(語法有些特別,有TEMPORARY及TEMPFILE的關鍵字)

CREATE TEMPORARY TABLESPACE  temp_ljb TEMPFILE 'D:\ORACLE\ORADATA\TEST11G\TEMP_LJB.DBF' SIZE 100M;
SELECT FILE_NAME,BYTES,AUTOEXTENSIBLE FROM DBA_TEMP_FILES where tablespace_name='TEMP_LJB';

---回滾段表空間(語法有些特別,有UNDO的關鍵字)
create undo tablespace undotbs2 datafile 'D:\ORACLE\ORADATA\TEST11G\UNDOTBS02.DBF' size 100M;
SELECT file_name, tablespace_name, autoextensible,bytes/1024/1024 
     FROM DBA_DATA_FILES
     WHERE TABLESPACE_NAME = 'UNDOTBS2'
       order by substr(file_name, -12); 

---系統表空間(Oracle 10g的系統表空間還增長了SYSAUX做爲輔助系統表空間使用)
SELECT file_name, tablespace_name,autoextensible,bytes/1024/1024
   FROM DBA_DATA_FILES
  WHERE TABLESPACE_NAME LIKE 'SYS%'
  order by substr(file_name, -12);

---系統表空間和用戶表空間都屬於永久保留內容的表空間
select tablespace_name,contents                                
  from dba_tablespaces                                         
 where tablespace_name in('TBS_LJB', 'TEMP_LJB', 'UNDOTBS2', 'SYSTEM', 'SYSAUX');
 
-------------------------------------------------------------------------------------------------------------------------------------------
SQL>  SELECT A.TABLESPACE_NAME "表空間名",
  2         A.TOTAL_SPACE "總空間(G)",
  3         NVL(B.FREE_SPACE, 0) "剩餘空間(G)",
  4         A.TOTAL_SPACE - NVL(B.FREE_SPACE, 0) "使用空間(G)",
  5         CASE WHEN A.TOTAL_SPACE=0 THEN 0 ELSE trunc(NVL(B.FREE_SPACE, 0) / A.TOTAL_SPACE * 100, 2) END "剩餘百分比%"
  6    FROM (SELECT TABLESPACE_NAME, trunc(SUM(BYTES) / 1024 / 1024/1024 ,2) TOTAL_SPACE
  7            FROM DBA_DATA_FILES
  8           GROUP BY TABLESPACE_NAME) A,
  9         (SELECT TABLESPACE_NAME, trunc(SUM(BYTES / 1024 / 1024/1024  ),2) FREE_SPACE
 10            FROM DBA_FREE_SPACE
 11           GROUP BY TABLESPACE_NAME) B
 12   WHERE A.TABLESPACE_NAME = B.TABLESPACE_NAME(+)
 13   ORDER BY 5;

表空間名                        總空間(G) 剩餘空間(G) 使用空間(G) 剩餘百分比%
------------------------------ ---------- ----------- ----------- -----------
SYSTEM                                .73           0         .73           0
SYSAUX                                .71         .04         .67        5.63
UNDOTBS1                             4.99         .56        4.43       11.22
USERS                                 .36         .23         .13       63.88
TBS_LJB                                13       12.67         .33       97.46
TBS_LJB_2K                            .09         .09           0         100
TBS_LJB_16K                           .09         .09           0         100


SQL>---建立用戶表空間
SQL>create tablespace TBS_LJB datafile  'D:\ORACLE\ORADATA\TEST11G\TBS_LJB01.DBF'  size 1G extent management local;  
表空間已建立。
SQL> SELECT file_name, tablespace_name, autoextensible,bytes  FROM DBA_DATA_FILES WHERE TABLESPACE_NAME = 'TBS_LJB';
FILE_NAME                                         TABLESPACE_NAME                AUT         BYTES
-------------------------------------------------- ------------------------------ --- -------------
D:\ORACLE\ORADATA\TEST11G\TBS_LJB01.DBF            TBS_LJB                        NO     1073741824

SQL> ---臨時表空間(語法有些特別,有TEMPORARY及TEMPFILE的關鍵字)
SQL> CREATE TEMPORARY TABLESPACE  temp_ljb TEMPFILE 'D:\ORACLE\ORADATA\TEST11G\TEMP_LJB.DBF' SIZE 100M;
表空間已建立。
SQL> SELECT FILE_NAME,tablespace_name,AUTOEXTENSIBLE,BYTES FROM DBA_TEMP_FILES where tablespace_name='TEMP_LJB';
FILE_NAME                                   TABLESPACE_NAME        AUT    BYTES
-------------------------------------------------- -------------------------------
D:\ORACLE\ORADATA\TEST11G\TEMP_LJB.DBF        TEMP_LJB              NO   104857600

SQL> ---建立回滾表空間
SQL> create undo tablespace undotbs2 datafile 'D:\ORACLE\ORADATA\TEST11G\UNDOTBS02.DBF' size 100M;
表空間已建立。
SQL> SELECT file_name, tablespace_name, autoextensible,bytes FROM DBA_DATA_FILES WHERE TABLESPACE_NAME = 'UNDOTBS2';
FILE_NAME                                          TABLESPACE_NAME           AUT       BYTES     
-------------------------------------------------- -----------------------------------------
D:\ORACLE\ORADATA\TEST11G\UNDOTBS02.DBF            UNDOTBS2                  NO    104857600

  

 

 

--構造表
drop table t purge;
create table t as select * from dba_objects;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
commit;

exec dbms_stats.gather_table_stats(ownname => 'LJB',tabname => 'T',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE) ;  

select num_rows,blocks from user_tab_statistics where table_name='T';

 NUM_ROWS     BLOCKS
--------- ----------
  2320250      33583


set autotrace off
delete from t where rownum<=2300000;
commit;
exec dbms_stats.gather_table_stats(ownname => 'LJB',tabname => 'T',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE) ;  

select num_rows,blocks from user_tab_statistics where table_name='T';

  NUM_ROWS     BLOCKS
---------- ----------
     32480      33583

  

 

 

--構造表
drop table t purge;
create table t as select * from dba_objects;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
commit;

--測試表的大小及語句的效率
select bytes/1024/1024 from user_segments where segment_name='T';
set autotrace on statistics
select count(*) from t;
select count(*) from t;

set autotrace off
delete from t where rownum<=2000000;
commit;
select bytes/1024/1024 from user_segments where segment_name='T';
set autotrace on statistics
select count(*) from t;
select count(*) from t;


--用move重組數據後,高水平位釋放(注意move操做會致使索引失效)
alter table t move;
select bytes/1024/1024 from user_segments where segment_name='T';
set autotrace on statistics
select count(*) from t;
select count(*) from t;



---延伸擴展,如何定位出存在高水平位的表
exec dbms_stats.gather_table_stats(ownname => 'LJB',tabname => 'T',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE) ;  
select * from user_tab
---------------------------------------------------------------------------------------------------------------------
SQL> --測試表的大小及語句的效率
SQL> select bytes/1024/1024 from user_segments where segment_name='T';
BYTES/1024/1024
---------------
     264
SQL> select count(*) from t;
  COUNT(*)
----------
   2332096
統計信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      33350  consistent gets
          0  physical reads



---刪除大量數據,再作試驗以下,發現SEGMENT未見減小,依然是:
SQL> delete from t where rownum<=2000000;
已刪除2000000行。
SQL> commit;
提交完成。
SQL> select bytes/1024/1024 from user_segments where segment_name='T';
BYTES/1024/1024
---------------
            264
SQL> select count(*) from t;
  COUNT(*)
----------
    332096
統計信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      33350  consistent gets
          0  physical reads

            
           
SQL> --用move重組數據後,高水平位釋放(注意move操做會致使索引失效)
SQL> alter table t move;
表已更改。
SQL> select bytes/1024/1024 from user_segments where segment_name='T';
BYTES/1024/1024
---------------
    38 
SQL> select count(*) from t;
  COUNT(*)
----------
    332096
統計信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       4742  consistent gets
          0  physical reads
相關文章
相關標籤/搜索