邏輯體系結構與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