二话不说,先看实验
首先创建一个目录,上传一张图片。用于测试保存到数据库中
[oracle@test11 pic]$ pwd
/home/oracle/pic
[oracle@test11 pic]$ ls -l
total 172
-rw-r--r-- 1 oracle oinstall 170556 Dec 23 2017 space.jpg
创建测试表
CREATE TABLE TEST_LOB (ID NUMBER, FILE_NAME VARCHAR2(45), IMAGE BLOB, TIMESTAMP DATE);
CREATE SEQUENCE TEST_LOB_SEQ;
CREATE OR REPLACE DIRECTORY IMAGES_DIR AS '/home/oracle/pic';
CREATE OR REPLACE PROCEDURE LOAD_BLOB_FROM_FILE_IMAGE
AS
DEST_LOC BLOB;
FILE_NAME TEST_LOB.FILE_NAME%TYPE := 'space.jpg';
SRC_LOC BFILE := BFILENAME('IMAGES_DIR', FILE_NAME);
BEGIN
INSERT INTO TEST_LOB (ID, FILE_NAME, IMAGE, TIMESTAMP)
VALUES (TEST_LOB_SEQ.NEXTVAL, FILE_NAME, EMPTY_BLOB(), SYSDATE)
RETURNING IMAGE INTO DEST_LOC;
DBMS_LOB.OPEN(SRC_LOC, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(DEST_LOC, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(
DEST_LOB => DEST_LOC
, SRC_LOB => SRC_LOC
, AMOUNT => DBMS_LOB.GETLENGTH(SRC_LOC));
DBMS_LOB.CLOSE(DEST_LOC);
DBMS_LOB.CLOSE(SRC_LOC);
COMMIT;
END;
插入1000行记录
BEGIN
FOR I IN 1 .. 1000
LOOP
LOAD_BLOB_FROM_FILE_IMAGE();
END LOOP;
END;
SQL> SELECT count(*) FROM TEST_LOB;
COUNT(*)
----------
1000
SQL>
IMAGE为BLOB 字段,保存了一张图片:
LOB字段大小为 174063616
删除一半的记录
LOB字段大小空间并没有变化。还是174063616
再次插入1000行
LOB 字段大小增大到 348127232
执行收缩表空间
ALTER TABLE TEST_LOB MODIFY LOB (IMAGE) (SHRINK SPACE);
居然大小没有发生改变:还是348127232
说明alter shrink 的方法对LOB 字段没有作用
创建另外一个测试表,上面的测试步骤重新执行一次
CREATE TABLE TEST_LOB_SECURE (ID NUMBER, FILE_NAME VARCHAR2(45), IMAGE BLOB, TIMESTAMP DATE) LOB(IMAGE) STORE AS SECUREFILE
TABLESPACE TBS_TS2;
同样插入1000行记录,LOB字段大小为 201523200
删除500行,然后插入1000行
空间缩小了70M
总结:通过上面的实验可以看到。DELETE表中的数据后,并不会释放LOB空间。重新插入记录也不会重用之前的空间。然后LOB字段的undo 管理又是依靠LOB本身空间去维护,不是使用数据库的空间。所以在生成实际使用过程中 ,LOB 字段所占空间非常容易越来越大。
一下三种方法可以回收LOB类型所占空间:
* Export drop the table import
* CREATE TABLE ... AS SELECT
* ALTER TABLE ... MOVE
move 操作需要注意执行完后,收集统计信息,重建索引,并且执行过程中会产生大量归档,物理IO,并且最好在维护时间操作等等
最核心的建议是:LOB字段在OTLP系统里面能不用就不要使用,通过其他办法去存储大对象。
不用,哪里还有考虑空间问题呢。