Database: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 – Production Version 19.3.0.0.0
I write this post in order to follow step by step how a bigfile tablespace is created and managed in 19c. The main purpose is to find a way to estimate the space benefits of defragmenting the tablespace. I will use two methods.
Method 1. By calculating the theoretical size of the tables. It is outlined below in steps 19-20. The limitation of this calculation is that right now it works for tables only
Method 2. By using the Segment Advisor. Outlined below in steps 21-22
1. Check the datafile does not exist at OS level:
[oracle@localhost ~]$ ls -lh /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
ls: cannot access /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf: No such file or directory
2. Create the tablespace as user SYSTEM:
CREATE bigfile tablespace "TEST_TBS" datafile
'/u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf' size 10M
autoextend ON next 10M maxsize 5G
logging online permanent blocksize 8192 extent management local
autoallocate default nocompress segment space management auto
;
3. Check DB file getting created:
[oracle@localhost ~]$ ls -lh /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
-rw-r-----. 1 oracle oinstall 11M Nov 13 08:37 /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
4. Login as ‘radu’ user. Create test tables in the test_tbs tablespace:
create table qz_results (
player varchar2(10)
, round integer
, score number
)
tablespace TEST_TBS
;
--
create table qz_results2 (
player varchar2(10)
, round integer
, score number
)
tablespace TEST_TBS
;
5. Check size of the DB file. Should not be changed: 11M
[oracle@localhost ~]$ ls -lh /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
-rw-r-----. 1 oracle oinstall 11M Nov 13 08:42 /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
6. Run test SQL:
SELECT
fs.tablespace_name "Tablespace",
( df.totalspace - fs.freespace ) "Used MB",
fs.freespace "Free MB",
df.totalspace "Total MB",
round(100 *(fs.freespace / df.totalspace)) "Pct. Free"
FROM
(
SELECT
tablespace_name,
round(SUM(bytes) / 1048576) totalspace
FROM
dba_data_files
GROUP BY
tablespace_name
) df,
(
SELECT
tablespace_name,
round(SUM(bytes) / 1048576) freespace
FROM
dba_free_space
GROUP BY
tablespace_name
) fs
WHERE
df.tablespace_name = fs.tablespace_name
and df.tablespace_name = 'TEST_TBS';
Output:
Tablespace Used MB Free MB Total MB Pct. Free
TEST_TBS 6 4 10 40
7. Populate first test table with 9 million rows. Example of block used to populate the table:
BEGIN
FOR i IN 1..1000000 LOOP
INSERT INTO qz_results VALUES (
'Johansson',
1,
110
);
INSERT INTO qz_results VALUES (
'Johansson',
2,
125
);
INSERT INTO qz_results VALUES (
'Johansson',
3,
105
);
INSERT INTO qz_results VALUES (
'Radetskiy',
1,
108
);
INSERT INTO qz_results VALUES (
'Radetskiy',
2,
115
);
INSERT INTO qz_results VALUES (
'Radetskiy',
3,
122
);
INSERT INTO qz_results VALUES (
'Williams',
1,
120
);
INSERT INTO qz_results VALUES (
'Williams',
2,
112
);
INSERT INTO qz_results VALUES (
'Williams',
3,
112
);
END LOOP;
COMMIT;
END;
8. Check size of the DB file –> 291M
[oracle@localhost ~]$ ls -lh /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
-rw-r-----. 1 oracle oinstall 291M Nov 13 09:29 /u01/app/oracle/oradata/ORCLCDB/orcl/test_tbs.dbf
9. Run the control SQL from step 6. Used –> 284M
Tablespace Used MB Free MB Total MB Pct. Free
TEST_TBS 284 14 298 5
10. Populate second test table with 9 million rows.
11. Check size of the DB file –> 526M
12. Run the control SQL from step 6. Used –> 500M
Tablespace Used MB Free MB Total MB Pct. Free
TEST_TBS 500 25 525 5
13. Delete all the rows from the first test table and commit.
delete from qz_results;
commit;
14. Check size of the DB file –> 526M. Unchanged
15. Run the control SQL from step 6. Used –> 500M Unchanged
Tablespace Used MB Free MB Total MB Pct. Free
TEST_TBS 500 25 525 5
16. Delete two thirds (6 million) rows form the second table and commit.
delete from qz_results2 where player in ('Radetskiy', 'Williams');
commit;
17. Check size of the DB file –> 526M. Unchanged
18. Run the control SQL from step 6. Used –> 500M Unchanged
Tablespace Used MB Free MB Total MB Pct. Free
TEST_TBS 500 25 525 5
19. Calculate the theoretical size of the tables by using the DBA_TABLES (this can only succeed if the statistics were collected for the impacted objects) –> approx. 50 MB
select owner, table_name,
num_rows * avg_row_len / 1024 / 1024 "Theoretical Size (MB)" from dba_tables
where owner = 'RADU'
and tablespace_name = 'TEST_TBS'
;
OWNER TABLE_NAME Theoretical Size (MB)
RADU QZ_RESULTS 0
RADU QZ_RESULTS2 48,63739013671875
select sum( num_rows * avg_row_len / 1024 / 1024 ) "Theoretical Size (MB)" from dba_tables
where owner = 'RADU'
and tablespace_name = 'TEST_TBS'
;
Theoretical Size (MB)
48,63739013671875
Note: using dba_segments will not generate a correct theoretical size!
select segment_name, bytes/1024/1024
from dba_segments
where owner = 'RADU'
and tablespace_name = 'TEST_TBS'
and segment_type='TABLE'
;
SEGMENT_NAME MB
QZ_RESULTS 216
QZ_RESULTS2 216
20. Calculate estimate space savings by adding a 10% tolerance to the total theoretical size computed on step 20 –> 445 MB
select 500 - (0 + 50) * 110 / 100 "MB Saved" from dual;
MB Saved
445
Verification:
The tables in the previous steps were created so they generate a predictable space usage. As 18 million rows needed 500 MB and we deleted 16 million rows, the approx. potential space saving will be –> approx. 445 MB
select (16 * 500) / 18 "MB Deleted" from dual;
MB Deleted
444,444444444444444444444444444444444444
21. Running the Segment Advisor Manually with PL/SQL (ref.: Oracle Documentation)
variable id number;
begin
declare
name varchar2(100);
descr varchar2(500);
obj_id number;
begin
name:='radu_task';
descr:='Tablespace Segment Advisor Example';
dbms_advisor.create_task (
advisor_name => 'Segment Advisor',
task_id => :id,
task_name => name,
task_desc => descr);
dbms_advisor.create_object (
task_name => name,
object_type => 'TABLESPACE',
attr1 => 'TEST_TBS',
attr2 => NULL,
attr3 => NULL,
attr4 => NULL,
attr5 => NULL,
object_id => obj_id);
dbms_advisor.set_task_parameter(
task_name => name,
parameter => 'recommend_all',
value => 'TRUE');
dbms_advisor.execute_task(name);
end;
end;
/
22. Query the DBA_ADVISOR_* tables and estimate savings –> 330 MB
select message, more_info from DBA_ADVISOR_FINDINGS
where task_name = 'radu_task';
MESSAGE MORE_INFO
Enable row movement of the table RADU.QZ_RESULTS and perform shrink, estimated savings is 226484224 bytes. Allocated Space:226492416: Used Space:8192: Reclaimable Space :226484224:
Enable row movement of the table RADU.QZ_RESULTS2 and perform shrink, estimated savings is 123592167 bytes. Allocated Space:226492416: Used Space:102900249: Reclaimable Space :123592167:
select benefit_type from DBA_ADVISOR_RECOMMENDATIONS
where task_name = 'radu_task'
;
BENEFIT_TYPE
Enable row movement of the table RADU.QZ_RESULTS and perform shrink, estimated savings is 226484224 bytes.
Enable row movement of the table RADU.QZ_RESULTS2 and perform shrink, estimated savings is 123592167 bytes.
select (123592167 + 226484224) / 1024 / 1024 "MB Advisor Savings" from dual;
MB Advisor Savings
333,85886287689208984375
Conclusions: two methods were outlined. Method 1 has limitations and provides a bigger potential benefit while method 2 uses the segment advisor at tablespace level and provides a more conservative estimation that might fir much more test cases beside the special case outlined in this article.