martes, 19 de mayo de 2009

Como mover datos de la partición default a una nueva?

Cuando tenemos una tabla particionada, en este caso por un rango de fechas y nos damos cuenta que los registros se están creando en la partición default, la solución es crear una nueva particion con el rango adecuado. Para este caso se puede usar la opción SPLIT la cual crea una nueva partición, en mi caso es la partición DCT_DOC2009.

ALTER TABLE dct_documentos
SPLIT PARTITION DCT_DOCMAX at (TO_DATE(' 2010-01-02 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
INTO (PARTITION DCT_DOC2009,
PARTITION DCT_DOCMAX)
UPDATE GLOBAL INDEXES;

Podemos usar la opción UPDATE GLOBAL INDEXES la cual recrea los indices globales de la tabla en cuestión o podriamos eliminar la opción de la claúsula y en un segundo paso recrear los indices con estado UNSUABLE.

Para hacer el reubuild de los indices usamos la siguiente query:

SELECT 'alter index 'owner'.'index_name ' REBUILD;'
FROM dba_indexes
WHERE status='UNUSABLE' ;

Para ver como quedaron las particiones se puede usar el siguiente query:

SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'DCT_DOCUMENTOS'
ORDER BY partition_name;

Para confirmar que ahora la partición tiene datos usamos:

SELECT * FROM dct_documentos PARTITION(DCT_DOC2009);

Par confirmar que la particion default en mi caso se llama DCT_DOCMAX, esta query debería no retornar filas.

SELECT * FROM dct_documentos PARTITION(DCT_DOCMAX);

Como calcular el tamaño realmente usado de un segmento

Con este script se puede calcular el tamaño realmente usado por un segmento.


PROMPT
PROMPT SEGMENT SPACE ALLOCATION
PROMPT

ACCEPT own PROMPT "Segment owner: "
ACCEPT obj PROMPT "Segment name: "
ACCEPT typ PROMPT "Segment type ((T)able, (I)ndex or (C)luster) or (L)ob: "

SET HEADING OFF
SELECT ' ' FROM sys.dual;

SET SERVEROUTPUT ON

DECLARE
op1 NUMBER;
op2 NUMBER;
op3 NUMBER;
op4 NUMBER;
op5 NUMBER;
op6 NUMBER;
op7 NUMBER;
obj_type VARCHAR2(10);
file_name VARCHAR2(255);
ts_name VARCHAR2(30);

BEGIN

IF UPPER('&typ') = 'T' THEN
obj_type := 'TABLE';
ELSIF UPPER('&typ') = 'I' THEN
obj_type := 'INDEX';
ELSIF UPPER('&typ') = 'C' THEN
obj_type := 'CLUSTER';
ELSIF UPPER('&typ') = 'L' THEN
obj_type := 'LOB';

ELSE
dbms_output.put_line('Invalid object type');
RETURN;
END IF;

dbms_space.unused_space(
UPPER('&own'),
UPPER('&obj'),
obj_type,
op1, op2, op3, op4, op5, op6, op7
);

dbms_output.put_line('Total Blocks = ' || op1);
dbms_output.put_line('Total Bytes = ' || op2);
dbms_output.put_line('Unused Blocks = ' || op3);
dbms_output.put_line('Unused Bytes = ' || op4);

SELECT file_name, tablespace_name
INTO file_name, ts_name
FROM dba_data_files
WHERE file_id = op5;

dbms_output.put_line('Segment Tablespace = ' || ts_name);
dbms_output.put_line('Last Used Extent File ID = ' || op5);
dbms_output.put_line('Last Used Extent File = ' || file_name);
dbms_output.put_line('Last Used Extent Block ID = ' || op6);
dbms_output.put_line('Last Used Block = ' || op7);

END;
/

UNDEFINE obj
UNDEFINE own
UNDEFINE typ

lunes, 18 de mayo de 2009

Como reducir el tamaño del tablespace UNDO

El procedimiento que muestro a continuación, muestra los pasos a seguir cuando tenemos que recrear el tablespace UNDO en un ambiente RAC.

Estamos trabajando con un cluster de dos nodos que contienen dos Instancias de bases de datos.

Los tablespaces de UNDO de una de las instancias han crecido más de la cuenta y nos han llenado el disco.

NOTA:Una base de datos solo puede tener un tablespace de UNDO activo en un momento determinado.

En un entorno RAC, tenemos un tablespace de UNDO para cada instancia.

Un tablespace no se puede reducir de tamaño. Lo que voy a hacer es eliminarlo y volver a crearlo con un tamaño adecuado, añadiendo además un límite a su crecimiento. El problema que tengo ahora es que estoy tratando con un tablespace de UNDO, el cual tiene un tratamiento especial.

No se puede poner offline y menos aún borrarlo mientras tenga segmentos activos. De modo que, lo primero es crear un nuevo tablespace de undo, las instrucciones que indico a continuación las ejecuto como usuario SYS:


Paso 1.

CREATE UNDO TABLESPACE UNDO_TEMPORAL DATAFILE SIZE 100M;

Ahora en la primera instancia del cluster (el orden da igual) se indica el parámetro que indica el tablespace de undo por defecto.

En este caso, se trata de un parámetro dinámico, y por lo tanto no hace falta parar la instancia para poder modificarlo:

ALTER SYSTEM SET UNDO_TABLESPACE='UNDO_TEMPORAL' SID='DB1';

Si ahora reviso los parámetros que hacen referencia al UNDO en esta instancia, me encuentro con lo siguiente:

Paso2.

sqlplus> show parameter undo

NAME TYPE VALUE
—————— ———– ——————–
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDO_TEMPORAL

En teoría, ya puedo borrar el tablespace de undo que me estaba dando el problema.

Si se diera la condición de que todos los segmentos estuvieran en estado offline, no haría ni siquiera falta poner offline el tablespace; le podría hacer un drop sin más complicaciones. El problema viene cuando hay algún segmento que no está offline; teniendo claro que no quiero hacer rollback, ni recuperar ningún tipo de transacción, y que puedo prescindir totalmente de estos datos, lo que voy a hacer es forzar el estado offline de los segmentos que no lo estén para poder borrar el tablespace de forma limpia. ¿Cómo compruebo esto? Con la siguiente select:


Paso3.

SELECT SEGMENT_NAME,TABLESPACE_NAME,STATUS
FROM DBA_ROLLBACK_SEGS
WHERE TABLESPACE_NAME='UNDOTBS1';

Que me dará un resultado como el que sigue:
SEGMENT_NAME TABLESPACE_NAME STATUS
——————– ————————- —————-
_SYSSMU13$ UNDOTBS1 NEEDS RECOVERY
_SYSSMU14$ UNDOTBS1 OFFLINE
_SYSSMU15$ UNDOTBS1 OFFLINE
_SYSSMU16$ UNDOTBS1 OFFLINE
_SYSSMU17$ UNDOTBS1 NEEDS RECOVERY
_SYSSMU18$ UNDOTBS1 OFFLINE
_SYSSMU19$ UNDOTBS1 OFFLINE
_SYSSMU20$ UNDOTBS1 OFFLINE

Cualquier status diferente de OFFLINE necesitará que le apliquemos el procedimiento que a continuación describo.

Paso4.

Lo siguiente es bajar la instancia; por suerte, estoy en un entorno RAC, por lo tanto no va a haber pérdida de servicio.

Como tengo que arrancar con un PFILE modificado a partir del SPFILE (con el que estoy trabajando), voy a crear un PFILE antes de bajar la instancia:


CREATE PFILE='/tmp/pfile.ora' from SPFILE;

Ahora bajo la instancia y edito el PFILE. Tengo que modificar el parámetro UNDO_MANAGEMENT al valor 'MANUAL' y añadir un parámetro oculto de ORACLE

para poder forzar el OFFLINE de los segmentos UNDO que no lo estén. Para el caso que describo, y según la anterior SELECT, yo insertaría esta línea

en mi PFILE:

*._offline_rollback_segments="_SYSSMU13$,_SYSSMU17quot;

Lo siguiente es levanter la instancia con el PFILE modificado:

STARTUP PFILE='/tmp/pfile.ora'

Si se ejecuta la anterior select, el resultado va a ser el mismo. Pero ahora ya se puede actuar sobre esos segmentos. En primer lugar:

DROP ROLLBACK SEGMENT '_SYSSMU13;

Que dará un resultado como este:

ERROR en línea 1:
ORA-02175: nombre de segmento de rollback no válido

Obviamos el error y repetimos el DROP sobre el otro segmento. Después de eso, ya se puede borrar el segmento de undo:


DROP TABLESPACE UNDOTBS1 INCLUDING CONTENTS AND DATAFILES;

Una vez borrado este tablespace, vuelvo a parar la instancia y arranco con el SPFILE (hago un startup sin ningún parámetro). En este momento,

ya puedo crear el nuevo tablespace de undo con los parámetros deseados:

CREATE UNDO TABLESPACE UNDOTBS1 DATAFILE SIZE 500M AUTOEXTEND ON NEXT 128M MAXSIZE 1G;

Igual que he hecho antes, cambio el tablespace de UNDO por defecto, substituyendo UNDO_TEMPORAL por UNDOTBS1.

ALTER SYSTEM SET UNDO_TABLESPACE='UNDOTBS1' SID=’DB1’;

Finalmente, solo queda repetir el proceso con la otra instancia. Cuando se concluya, se puede borrar el tablespace de UNDO que se ha creado para solucionar el problema.