关键步骤:
检查数据库中存在的LOB数据
将LOB字段设置为空的LOB值,从而实现删除LOB数据的效果(使用empty_blob()或者empty_clob()函数)。在具体操作时,可以选择性地使用WHERE子句来删除特定条件下的LOB数据。
实验背景
同事在巡检RAC业务库时发现共享存储剩余空间仅剩余200G左右了,问是否可以查出来LOB字段的表并统计大小,删除开发指定的LOB字段数据以释放空间。
数据类型说明
varchar2类型
在Oracle数据库存储的字符数据一般是用VARCHAR2。VARCHAR2既分PL/SQL Data Types中的变量类型,也分Oracle Database中的字段类型,不同场景的最大长度不同。
在Oracle Database中,VARCHAR2 字段类型,最大值为4000;PL/SQL中 VARCHAR2 变量类型,最大字节长度为32767。
当 VARCHAR2 容纳不下需要存储的信息时,就需要用Oracle的大数据类型LOB( Large Object,大型对象)。
LOB类型
在Oracle中,LOB是一种用于存储大型二进制或字符数据的数据类型。LOB(Large Object,大型对象)类型的字段现在用得越来越多了。因为这种类型的字段,容量大(最多能容纳4GB的数据),且一个表中可以有多个这种类型的字段,很灵活,适用于数据量非常大的业务领域(如图象、档案等)。
LOB类型分为BLOB和CLOB两种:BLOB即二进制大型对象(Binary Large Object),适用于存贮非文本的字节流数据(如程序、图象、影音等)。通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。
CLOB,即字符型大型对象(Character Large Object),则与字符集相关,适于存贮文本型的数据(如历史档案、大部头著作等),不支持宽度不等的字符集。虽然CLOB能解决VARCHAR2字符大小的限制,但是我们的DBA们都不建议用这些来处理,可能效率问题吧,大的文件应该放在服务器上,然后Database中存响应地址即可。可存储的最大大小为4G字节。
特别说明:
对表上的lob字段是单独存储的,所以使用segment_name = 表名 的时候,只能统计非 lob 字段的数据大小,无法统计上lob字段的。正确的计算方式应该是:dba_segments中表占用的空间+LOB segments占的空间+ LOB 的索引。
LOB数据常用操作
创建LOB业务表
创建含BLOB业务表
create table scott.blob_temp
(
name VARCHAR2(200),
age NUMBER,
temp_clob BLOB
);创建含CLOB业务表
创建表(使用sql或者直接在PL/SQL客户端创建),字段类型CLOB
create table scott.temp
(
name VARCHAR2(200),
age NUMBER,
temp_clob CLOB
);增删改查
含BLOB业务表插入数据
示例1:将内容转换成二进制进行插入
--示例1
INSERT INTO scott.blob_temp VALUES ('1',19,rawtohex('内容'));
commit;
insert into scott.blob_temp select * from scott.blob_temp;含CLOB业务表插入数据
普通方式增删改查
普通插入操作会因为Oracle的隐式转换,默认把字符串转换成varchar2类型,一旦字符串内容超过varchar2的最大限度就会报会报ora-01704(字符串太长)错误。
--插入数据
insert into scott.temp values('1',12,'这是一相故顶真参压顶地');
commit;
--普通查询
col name for a30
SELECT t.name, t.temp_clob FROM scott.temp t;
NAME TEMP_CLOB
------------------------------ --------------------------------------------------------------------------------
1 这是一相故顶真参压顶地
1 这是一相故顶真参压顶地dbms_lob方式增删改查
--使用PL/SQL语法,采取绑定变量的方式解决,而不是直接拼接SQL
DECLARE
V_LANG CLOB := '待插入的海量字符串';
V_UPDATE CLOB := '更新的海量字符串';
BEGIN
insert into scott.temp values('1',3,'新疆自治区的第一旅行'); --增加
UPDATE scott.temp t SET t.temp_clob = 'V_UPDATE' WHERE rownum = 1; --修改
SELECT t.NAME, dbms_lob.substr(t.temp_clob) FROM scott.temp t; --查询 将CLOB转成字符类型
DELETE scott.temp t WHERE rownum = 1; --按列删除
COMMIT;
END;
/dbms_lob 方法总结:
dbms_lob.createtemporary(V_SQL,true); --创建一个临时clob,用来存储拼接的sql
dbms_lob.write(v_SQL,'写入信息'); --写入操作
dbms_lob.append(v_SQL,','); --拼接clob
dbms_lob.substr(v_SQL); --截取clob,不传参数就是全部读取
dbms_lob.freetemporary(v_SQL); --释放clob检查LOB数据
删除LOB数据之前,首先需要检查数据库中存在的LOB数据。
--获取LOB字段的名称、拥有者、大小
col segment_name for a50
set linesize 999 pagesize 999
SELECT owner,SEGMENT_NAME, ROUND(SUM(BYTES / 1024 / 1024 / 1024), 2) G
FROM DBA_SEGMENTS
WHERE SEGMENT_NAME IN
(SELECT TABLE_NAME FROM DBA_TAB_COLUMNS WHERE DATA_TYPE = 'BLOB')
and owner='SCOTT'
GROUP BY owner,SEGMENT_NAME
ORDER BY 2 DESC;
或
col tablespace_name for a40
col column_name for a40
SELECT B.TABLE_NAME,
B.COLUMN_NAME,
A.TABLESPACE_NAME,
A.SEGMENT_NAME,
a.SEGMENT_TYPE,
ROUND(SUM(A.BYTES / 1024 / 1024 / 1024), 2) G
FROM DBA_SEGMENTS A
LEFT JOIN DBA_LOBS B
ON A.OWNER = B.OWNER
AND A.SEGMENT_NAME = B.SEGMENT_NAME
where A.OWNER ='SCOTT'
--WHERE B.SEGMENT_NAME = 'SYS_LOB0000026212C00002$$'
HAVING ROUND(SUM(A.BYTES / 1024 / 1024 / 1024), 2) >1
GROUP BY B.TABLE_NAME, B.COLUMN_NAME, A.SEGMENT_NAME,a.SEGMENT_TYPE,A.TABLESPACE_NAME
order by ROUND(SUM(A.BYTES / 1024 / 1024 / 1024), 2) ;上述查询语句将返回包含LOB字段的表名和对应的LOB字段名。通过这个查询结果,我们可以确认数据库中存在哪些LOB数据。
删除LOB数据
empty_blob()或empty_clob()函数方式
当LOB数据不再需要时,可能需要将其从数据库中删除以释放存储空间。
在删除LOB数据之前,需要明确删除的是哪个表中的LOB数据以及对应的LOB字段名称。假设要删除的表名为my_table,LOB字段名称为my_lob,可以使用如下的SQL语句来删除LOB数据:
UPDATE my_table
SET my_lob = empty_blob() -- 对于BLOB字段使用empty_blob(),对于CLOB字段使用empty_clob()
WHERE <some condition>; -- 可选,选择性地删除特定的LOB数据在上述的SQL语句中,将要删除的LOB字段设置为空的LOB值,从而实现删除LOB数据的效果。需要注意的是,可以选择性地使用WHERE子句来删除特定条件下的LOB数据,如果不指定条件,则会删除表中所有的LOB数据。
另外,如果要删除的是CLOB字段而不是BLOB字段,那么应该使用empty_clob()函数来将CLOB字段设置为空的CLOB值。
函数方式并不会释放空间,查询依然是原来占用空间大小。
tuncate方式
直接删除
示例
--确认表中存在的LOB字段
set linesize 999
SELECT table_name, column_name ,data_type
FROM user_tab_cols
WHERE table_name = 'TEMP';
TABLE_NAME COLUMN_NAME DATA_TYPE
------------------------------------------------------------ ------------------------------------------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TEMP NAME VARCHAR2
TEMP AGE NUMBER
TEMP TEMP_CLOB CLOB
--查询数据
SQL> col name for a30
SQL> select * from temp;
NAME AGE TEMP_CLOB
------------------------------ ---------- --------------------------------------------------------------------------------
1 12 这是一相故顶真参压顶地
--删除temp表中的LOB数据
UPDATE temp
SET TEMP_CLOB = empty_clob()
WHERE name='1';如果是删除blob内容,将empty_clob()更改为empty_blob()
案例1
最近,某个数据接口项目上ORACLE数据库的使用空间越来越大,查看一下数据库中哪个数据表的数据量的大小,以便进行有针对性的处理.
查看Oracle中表空间及表数据大小
--查看数据库中有哪些用户
set lin1000 pagesize 999
select username,default_tablespace,temporary_tablespace from dba_users where username not in ('SYS','SYSTEM','HR','OUTLN','MGMT_VIEW','FLOWS_FILES','MDSYS','ORDSYS','EXFSYS','DBSNMP','WMSYS','APPQOSSYS','APEX_030200','OWBSYS_AUDIT','ORDDATA','CTXSYS','ANONYMOUS','SYSMAN','XDB','ORDPLUGINS','OWBSYS','SI_INFORMTN_SCHEMA','OLAPSYS','SCOTT','ORACLE_OCM','XS$NULL','BI','PM','MDDATA','IX','SH','DIP','OE','APEX_PUBLIC_USER','SPATIAL_CSW_ADMIN_USR','SPATIAL_WFS_ADMIN_USR');
--查看Oracle中表空间及表数据大小
SELECT OWNER,SEGMENT_NAME,
(SUM(BYTES) / 1024 / 1024 / 1024) AS TABLE_SIZE
FROM DBA_EXTENTS where owner in ('TOPICIS','TOPICPSP')
GROUP BY OWNER,SEGMENT_NAME
ORDER BY owner,TABLE_SIZE DESC;分析数据最大的对象
从返回的结果看,有一个segment名为"SYS_LOB0000090035C00005$$"的对象占用了大量的空间,这种带有SYS_LOB***即LOB(BLOB和CLOB)对象占用数据库的空间名称。

定位数据最大的对象对应的表
根据segment_name,就可以从 dba_lobs 表里查到是哪个表,哪个字段,具体的SQL如下:
SELECT * FROM DBA_LOBS WHERE SEGMENT_NAME LIKE 'SYS_LOB0000164649C00003$$';
可以发现是LOG_MQLISTENERINFO这个表里存在的Bolb类型LOGINFO存有大数据,占用了20.03GB的空间。
查看表结构

清理数据
--删除temp表中的LOB数据 UPDATE LOG_MQLISTENERINFO SET LOGINFO = empty_clob(); commit; 使用empty_clob()后空间并不会缩小,查询后大小还是原来大小 --启用行迁移 alter table REG_BUSMAIINF_XMLDATA enable row movement; --释放空间方法 方法1:收缩表 ALTER TABLE REG_BUSMAIINF_XMLDATA SHRINK SPACE CASCADE; 方法2:truncate表 truncate table TOPICIS. REG_BUSMAIINF_XMLDATA; --查询对象大小,已变小 SELECT OWNER,SEGMENT_NAME, (SUM(BYTES) / 1024 / 1024 / 1024) AS TABLE_SIZE FROM DBA_EXTENTS where owner in ('TOPICIS','TOPICPSP') and segment_name='SYS_LOB0000095888C00003$$' GROUP BY OWNER,SEGMENT_NAME ORDER BY owner,TABLE_SIZE DESC;--关闭行迁移 alter table REG_BUSMAIINF_XMLDATA disable row movement;
参考链接:https://blog.csdn.net/qq_20197983/article/details/81487411




