关于ORA-1652的一点简单总结

关于ORA-1652错误,可谓是太常见不过了,但是我想未必每个人都能知道其根本原因和其处理方式,
今天在群里有人问到这个问题,故就有了这篇文章。
该错误的格式如下(摘取该网友的错误信息):

ORA-1652: unable to extend temp segment by 128 in tablespace  OCSTS

我们先来看看MOS文档对此错误的描述(其实通过oerr 1652也能得到):

Error:  ORA-1652
Text:   unable to extend temp segment by %s in tablespace %s
------- -----------------------------------------------------------------------
Cause:  Failed to allocate an extent for temp segment in tablespace.
Action: Use ALTER TABLESPACE ADD DATAFILE statement to add one or more
        files to the tablespace indicated or create the object in another
        tablespace.

简单的描述该错误实质就是oracle在该tablespace上无法分配一段连续的extent了进而抛出错误。
在该种情况下,我们首先需要去查询表空间使用率,如下:
SQL> select *
  2    from (Select a.tablespace_name,
  3                 to_char(a.bytes / 1024 / 1024, '99,999.999') total_bytes,
  4                 to_char(b.bytes / 1024 / 1024, '99,999.999') free_bytes,
  5                 to_char(a.bytes / 1024 / 1024 - b.bytes / 1024 / 1024,
  6                         '99,999.999') use_bytes,
  7                 to_char((1 - b.bytes / a.bytes) * 100, '99.99') || '%' use
  8            from (select tablespace_name, sum(bytes) bytes
  9                    from dba_data_files
 10                   group by tablespace_name) a,
 11                 (select tablespace_name, sum(bytes) bytes
 12                    from dba_free_space
 13                   group by tablespace_name) b
 14           where a.tablespace_name = b.tablespace_name
 15          union all
 16          select c.tablespace_name,
 17                 to_char(c.bytes / 1024 / 1024, '99,999.999') total_bytes,
 18                 to_char((c.bytes - d.bytes_used) / 1024 / 1024, '99,999.999') free_bytes,
 19                 to_char(d.bytes_used / 1024 / 1024, '99,999.999') use_bytes,
 20                 to_char(d.bytes_used * 100 / c.bytes, '99.99') || '%' use
 21            from (select tablespace_name, sum(bytes) bytes
 22                    from dba_temp_files
 23                   group by tablespace_name) c,
 24                 (select tablespace_name, sum(bytes_cached) bytes_used
 25                    from v$temp_extent_pool
 26                   group by tablespace_name) d
 27           where c.tablespace_name = d.tablespace_name)
 28   order by tablespace_name
 29  /

TABLESPACE TOTAL_BYTES FREE_BYTES  USE_BYTES   USE
---------- ----------- ----------- ----------- -------
ROGER           50.000      49.938       0.063   0.13%
SYSAUX         250.000       6.875     243.125  97.25%
SYSTEM         480.000       5.188     474.813  98.92%
TEMP            20.000       2.000      18.000  90.00%
UNDOTBS1        25.000       5.250      19.750  79.00%
USERS            5.000       4.563       0.438   8.75%

6 rows selected.
这时通常的情况下是关闭了datafile的自动扩容的,当然为了解决这个问题,你可以开启自动扩容;
不过, 通常我们并不推荐这么做,而是通过如下方式进行处理:
SQL> col file_name for a60
SQL> select file_id,file_name,bytes/1024/1024
  2  from dba_temp_files
  3  where tablespace_name='TEMP';

FILE_ID    FILE_NAME                                                    BYTES/1024/1024
---------- ------------------------------------------------------------ ---------------
 1         F:\ORACLE_TEST\PRODUCT\10.2.0\ORADATA\ROGER\TEMP01.DBF                    20

SQL> select username,default_tablespace,TEMPORARY_TABLESPACE
 2   from dba_users
 3   where username='ROGER';

USERNAME                       DEFAULT_TABLESPACE             TEMPORARY_TABLESPACE
------------------------------ ------------------------------ -----------------------
ROGER                          ROGER                          TEMP

SQL> alter database datafile 5 resize 1024m;

Database altered.

SQL> select file_id,bytes/1024/1024,autoextensible
  2  from dba_data_files
  3  where tablespace_name='ROGER';

FILE_ID BYTES/1024/1024 AUTOEXTENSIBLE
------- --------------- --------------
5       1024            NO

#### 或者我们想办法去优化我们的sql语句,尽量降低排序操作。####

SQL> conn roger/roger
Connected.

SQL> create table ht1 as select * from sys.dba_objects where 1=2;

Table created.

SQL> begin
  2    for i in 1..100 loop
  3      insert /*+ append */into ht1 select * from sys.dba_objects;
  4      commit;
  5    end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

SQL> select count(*) from ht1;

COUNT(*)
----------
 5003900

SQL> begin
 2    for i in 1 .. 100 loop
 3      insert /*+ append */
 4      into ht1
 5        select * from ht1;
 6      commit;
 7    end loop;
 8  end;
 9  /
begin
*
ERROR at line 1:
ORA-01653: unable to extend table ROGER.HT1 by 1024 in tablespace ROGER
ORA-06512: at line 3

#### 下面为该表创建index试试 ####

SQL> create index idx_ht1_id on ht1(owner,object_id) tablespace roger;
create index idx_ht1_id on ht1(owner,object_id) tablespace roger
 *
ERROR at line 1:
ORA-01652: unable to extend temp segment by 128 in tablespace TEMP

++++++ 在另外的窗口中查询,当前是谁在使用temp。++++++

SQL> set lines 140
SQL> col username for a10
SQL> col osuser for a15
SQL> col sql_text for a70
SQL> col tablespace_name for a10
SQL> SELECT a.username,
  2          a.sid,
  3          a.serial#,
  4          a.osuser,
  5          b.tablespace,
  6          b.blocks,
  7          c.sql_text
  8    FROM v$session a, v$tempseg_usage b, v$sqlarea c
  9   WHERE a.saddr = b.session_addr
  10     AND c.address = a.sql_address
  11     AND c.hash_value = a.sql_hash_value
  12   ORDER BY b.tablespace, b.blocks
  13  /

no rows selected

SQL> /

USERNAME  SID SERIAL# OSUSER          TABLESPACE  BLOCKS  SQL_TEXT
-------- ---- ------- --------------- ----------- ------  ----------------------------------------------------------------
ROGER     147       3 KILLDB\think    TEMP          2432  create index idx_ht1_id on ht1(owner,object_id) tablespace roger


SQL> select TABLESPACE_NAME,TOTAL_BLOCKS,USED_BLOCKS,FREE_BLOCKS from v$sort_segment;

TABLESPACE TOTAL_BLOCKS USED_BLOCKS FREE_BLOCKS
---------- ------------ ----------- -----------
TEMP               2432           0        2432

SQL> /

TABLESPACE TOTAL_BLOCKS USED_BLOCKS FREE_BLOCKS
---------- ------------ ----------- -----------
TEMP               2432        1920         512
从上面的测试我们可以看到,create index是需要进行排序操作的,如果segment很大,
那么很可能需要非常大的temp segment。

到这里我们有必要简单的总结下,哪些情况下需要使用temp segment呢?
总的来说有如下几种情况需要temp段:

-- 排序操作 比如select或dml(ddl)语句中包含order by之类;
-- create index
-- create pk constraint (其实这个跟create index类似,因为创建主键约束时默认会同时创建index)
-- enable constraint操作
-- create table语句

既然我们知道了哪些情况下的操作是需要temp segment的,那么这时我就萌生出一个想法了:
能否估算某个操作大概需要多少temp segment呢?那样的话就可以预先进行操作,防止该错误发生了。
SQL> analyze table ht1 compute statistics;

Table analyzed.

SQL> select count(*) from ht1;

COUNT(*)
----------
 5003900

SQL> select table_name,column_name,avg_col_len
  2  from user_tab_columns
  3  where table_name='HT1';

TABLE_NAME                     COLUMN_NAME                    AVG_COL_LEN
------------------------------ ------------------------------ -----------
HT1                            OWNER                                    5
HT1                            OBJECT_NAME                             24
HT1                            SUBOBJECT_NAME                           2
HT1                            OBJECT_ID                                4
HT1                            DATA_OBJECT_ID                           2
HT1                            OBJECT_TYPE                              8
HT1                            CREATED                                  7
HT1                            LAST_DDL_TIME                            7
HT1                            TIMESTAMP                               19
HT1                            STATUS                                   5
HT1                            TEMPORARY                                1
HT1                            GENERATED                                1
HT1                            SECONDARY                                1

13 rows selected.

###### 通过explain plan方式来看排序需要多少temp ######

SQL> explain plan for
 2     create index idx_ht1_id on ht1(owner,object_id) tablespace roger;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
Plan hash value: 4061622788

-------------------------------------------------------------------------------------
| Id  | Operation              | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | CREATE INDEX STATEMENT |            |  5003K|    42M| 18132   (1)| 00:03:38 |
|   1 |  INDEX BUILD NON UNIQUE| IDX_HT1_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |            |  5003K|    42M|            |          |
|   3 |    TABLE ACCESS FULL   | HT1        |  5003K|    42M| 15239   (1)| 00:03:03 |
-------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
Note
-----
 - estimated index size: 117M bytes

14 rows selected.
从上面可以看出该操作进行排序操作需要42m空间,具体这是如何计算出来的,不得而知,不过我可以进行
猜测,oracle必定也是根据平均列长度等进行计算出来的,有兴趣的朋友可以研究研究。

参与排序的数据源包括所有要索引的列和rowid,列的长度,还有index entry header。

http://dbaoracle.itpub.net/post/901/29560 提到了,但是也不确认具体每部分到底是多少byte。
由于现在使用的win7,不方便进行有些实验,关于该部分回头我会再写一篇,进行描述rowid,列长度
以及index entry header分别占据多少个byte。

mos文档 ORA-01652: Estimate Space Needed to CREATE INDEX [ID 100492.1] 中也提到了如何进行估算。
但是很可惜,该文档描述太过含糊了,比如说如果我有2个或2个以上的列需要进行索引,那么计算的时候根据
那个列为准呢?难道是最大的列平均长度?
如果我这里使用较大的列来计算,那么就是owner列,如下:
SQL> select 5003900*5/1024/1024 from dual;

5003900*5/1024/1024
-------------------
 23.8604546
该结果进行四舍五入即为24M,再乘以3,那么即为72M,远大于执行计划中的42M。
当然为什么说这里是乘3而不是2或4呢?我猜测oracle这里也是在往大的方向进行估算,毕竟大点比小好。

最后,至于说到底如何计算才是准为准确的?我想这个问题不必那么较真了,毕竟无多大意义,只要能
让我们的操作顺利进行就行了。

如果大家有兴趣,可以去看看如下几个文档:

ORA-1652 Error Troubleshooting [ID 793380.1]
How Do You Find Who And What SQL Is Using Temp Segments [ID 317441.1]
OERR: ORA 1652 unable to extend temp segment by %s in tablespace %s [ID 19047.1]
ORA-01652: Estimate Space Needed to CREATE INDEX [ID 100492.1]
Extent Sizes for Sort, Direct Load and Parallel Operations (PCTAS & PDML) [ID 50592.1]
How Can Temporary Segment Usage Be Monitored Over Time? [ID 364417.1]
Temporary Segments Are Not Being De-Allocated After a Sort [ID 1039341.6]


评论

  1. 大家可以参考下tom精辟回答 关于ora-1652 http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:36859040165792

  2. 你这个查询表空间利用率的脚本存在BUG,比如某个表空间完全耗尽时,dba_free_space里是没有这个表空间的数据的,这样关联出来就会丢失这个表空间的信息。应该使用外关联。

  3. to 熊哥:这个到没注意过 只是知道有几个关于dba_free_space的bug。 10204+ 还有这个问题? 你指的bug有具体的bug号没 ?

  4. Freespace=0 in the Repository Admin Utility but tablespace does have freespace [ID 133758.1]

    ——————————————————————————–

    修改时间 16-FEB-2011 类型 PROBLEM 状态 PUBLISHED

    *** Checked for relevance on 16-Feb-2011 ***

    Problem Description
    ——————-
    In the RAU one or more Tablespaces show 0 for freespace. When you go into
    SQLPLUS with the select statement:

    select tablespace_name, sum(bytes)/1024/1024 from dba_free_space
    group by tablespace_name;

    Shows lots of space for that tablespace

    Solution Description
    ——————–
    Regrant all of the permissions in the install manual for the repository owner.

    GRANT EXECUTE ON DBMS_LOCK TO ;
    GRANT EXECUTE ON DBMS_PIPE TO ;
    GRANT CREATE TABLE TO ;
    GRANT CREATE VIEW TO ;
    GRANT CREATE PROCEDURE TO ;
    GRANT CREATE SYNONYM TO ;
    GRANT CREATE SEQUENCE TO ;
    GRANT SELECT ON sys.v_$nls_parameters TO WITH GRANT OPTION;
    GRANT SELECT on sys.V_$PARAMETER TO ;
    GRANT SELECT ON dba_rollback_segs TO ;
    GRANT SELECT ON dba_segments TO ;
    GRANT CREATE ANY SYNONYM TO ;
    GRANT DROP ANY SYNONYM TO ;
    GRANT CREATE PUBLIC SYNONYM TO ;
    GRANT DROP PUBLIC SYNONYM TO ;
    GRANT ck_oracle_repos_owner to ;
    GRANT CONNECT, RESOURCE TO ;

    Explanation
    ———–
    Designer requires all permissions that are given in the install manual to
    operate properly.

  5. 不是指Oracle的BUG,我的意思是你的脚本有缺陷。比如一个表空间的所有空间都已经用完的时候,dba_free_space里面就不会有这个表空间的记录了。

  6. to 熊哥: 哈哈 了解了。。。。

  7. I have been browsing on-line more than 3 hours these days, yet I never discovered any attention-grabbing article like yours. It’s lovely value sufficient for me. In my view, if all web owners and bloggers made just right content material as you probably did, the web will likely be much more helpful than ever before.

回复 roger 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注