viernes, 3 de julio de 2009

Nuevos Features en 11g para hacernos mas fácil el mantenimiento y upgrade de aplicaciones


En esta nota les voy a mostrar rapidamente 5 nuevos features de 11g. La mayoria de estos features esta relacionado con mejoras sobre actividades comunes en el mantenimiento y actualización de aplicaciones.


1. Permitir obtener valores de secuencias desde expresiones PL/SQL (Sequences from pl/sql expression)


Cualquier programador PL/SQL se habrá preguntado alguna vez porque hay que realizar un select seq. from dual para obtener el próximo valor o el valor corriente de una secuencia y no hacer una simple llamada desde una linea PL/SQL. Ahora en 11g se puede, y ya no es necesario invocar el motor sql para que nos devuelva el valor sino que se puede invocar con una simple expresión.

Primero veamos que pasa con una versión inferior a 11g

rop@DESA10G> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Solaris: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

rop@DESA10G> create sequence seq;

Secuencia creada.

rop@DESA10G> declare
2 l_nval int;
3 begin
4 l_nval := seq.nextval;
5 end;
6 /
l_nval := seq.nextval;
*
ERROR en línea 4:
ORA-06550: línea 4, columna 27:
PLS-00357: La referencia a la tabla, vista o secuencia 'SEQ.NEXTVAL' no está permitida en este
contexto
ORA-06550: línea 4, columna 13:
PL/SQL: Statement ignored

El mensaje de error es claro y se ve que no es posible obtener el valor desde pl/sql.

Ahora, veamos que pasa en 11g:

rop@DESA11G> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
PL/SQL Release 11.1.0.7.0 - Production
CORE 11.1.0.7.0 Production
TNS for Solaris: Version 11.1.0.7.0 - Production
NLSRTL Version 11.1.0.7.0 - Production

rop@DESA11G> create sequence seq;

Secuencia creada.

rop@DESA11G> declare
2 l_nval int;
3 begin
4 l_nval := seq.nextval;
5 end;
6 /
1

Procedimiento PL/SQL terminado correctamente.

rop@DESA11G> /
2

Procedimiento PL/SQL terminado correctamente.

rop@DESA11G>

Ahora no falla y se obtiene el valor requerido de una forma mas simple.


2 - DDL con opcion de wait (DDL with wait)

Cuando se necesita cambiar la definición de una tabla (sentencia DDL) no puede existir transacciones activas (que no hayan hecho aun commit o rollback) porque sino la DDL fallará.
Eso suena logico ya que no puedo estar cambiando la definición si todavia quedan transacciones
pendientes. Antes de 11g el dba tenia que probar cada cierto tiempo y lograr que en el instante de cambio no hubiese ninguna transaccion activa. Un practica común es armar un bloque pl/sql que vaya probando cada n segundos hasta que se puede realizar con exito el cambio.
Primero vamos a ver que pasa en 10g, voy a usar dos sesiones, desde la primera sesión realizo una transaccion y no la confirmo hasta pasados 30".

En 10g:

Sesion 1:

rop@DESA10G> create table t (x varchar2(1));

Tabla creada.

rop@DESA10G> begin
2 insert into t values ('1');
3 dbms_lock.sleep(30);
4 commit;
5 end;
6 /

Procedimiento PL/SQL terminado correctamente.

rop@DESA10G>

En la segunda sesion intento modificar la definición de la tabla agregandole modificando el tipo de datos.

Sesion 2:

rop@DESA10G> alter table t modify x varchar2(2);
alter table t modify x varchar2(2)
*
ERROR en línea 1:
ORA-00054: recurso ocupado y obtenido con NOWAIT especificado

No me dejó. Espero 30" y vuelvo a correr la ddl:

rop@DESA10G> /

Tabla modificada.

rop@DESA10G>

Recien una vez que se confirmó la transaccion pude modificar la tabla.
Ahora le voy a mostrar como hacerlo en 11g aprovechando el nuevo feature:

En 11g:

Sesion 1:

rop@DESA11G> create table t (x varchar2(1));

Tabla creada.

rop@DESA11G> begin
2 insert into t values ('1');
3 dbms_lock.sleep(30);
4 commit;
5 end;
6 /

Procedimiento PL/SQL terminado correctamente.

rop@DESA11G>

La sesion 1 es exactamente igual que la anterior. En la sesion 2 voy a definir el parametro ddl_lock_timeout a 60" y voy a correr al ddl:

Sesion 2:

rop@DESA11G> alter session set ddl_lock_timeout=60;

Sesión modificada.

rop@DESA11G> alter table t modify x varchar2(2);

Tabla modificada.

rop@DESA11G>

Como se vio, no tuve que intentar nuevamente, es el mismo Oracle el que se encarga de verificar cuando se puede alterar la tabla y lo hace automaticamente sin necesidad de intervención del dba.


3. Indices Invisibles (Invisible Indexes)

Otro feature interesante introducido en 11g es la posibilidad de crear indices invisibles. Muchos se preguntaran para que puede servir eso. Bueno, en principio, se podria crear un indice invisible sobre una tabla productiva y analizar a nivel sesion como cambia el plan y si se aprovecharia es nuevo indice para mejorar los accesos. Es importante aclarar que si bien el indice es invisible se mantiene actualizado con cada dml sobre la tabla donde esta definido. El indice es solo invisible para el optimizador, es decir no es tomado en cuenta en el parsing de las sentencias.

Vamos a ver su uso con un ejemplo. Como siempre voy a crear una tabla de 1M de filas con valores ficticios

rop@DESA11G> create table t (x int,y char(50));

Tabla creada.

rop@DESA11G> insert into t
2 select rownum,
3 dbms_random.string('a',30)
4 from dual
5 connect by rownum <= 1000000; 1000000 filas creadas. rop@DESA11G> commit;

Confirmación terminada.

rop@DESA11G> explain plan for select max(x) from t where y = 'A';

Explicado.

rop@DESA11G> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 1842905362

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2505 (2)| 00:00:31 |
| 1 | SORT AGGREGATE | | 1 | 65 | | |
|* 2 | TABLE ACCESS FULL| T | 100 | 6500 | 2505 (2)| 00:00:31 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("Y"='A')

Note
-----
- dynamic sampling used for this statement

18 filas seleccionadas.

rop@DESA11G> create index t_y_idx on t(y) invisible;

Índice creado.

rop@DESA11G> explain plan for select max(x) from t where y = 'A';

Explicado.

rop@DESA11G> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 1842905362

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2505 (2)| 00:00:31 |
| 1 | SORT AGGREGATE | | 1 | 65 | | |
|* 2 | TABLE ACCESS FULL| T | 100 | 6500 | 2505 (2)| 00:00:31 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("Y"='A')

Note
-----
- dynamic sampling used for this statement

18 filas seleccionadas.

El indice es invisible y como se observó mas arriba no fue tenido en cuenta por el optimizador. Ahora hagamoslo visible:


rop@DESA11G> alter index t_y_idx visible;

Índice modificado.

rop@DESA11G> explain plan for select max(x) from t where y = 'A';

Explicado.

rop@DESA11G> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 767882303

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 65 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 65 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T_Y_IDX | 1 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - access("Y"='A')

Note
-----
- dynamic sampling used for this statement

19 filas seleccionadas.

rop@DESA11G> select index_name,visibility from user_indexes where table_name = 'T';

INDEX_NAME VISIBILIT
------------------------------ ---------
T_Y_IDX VISIBLE

Cuando lo hicimos visible el indice fue usado para armar el plan.


rop@DESA11G> alter index t_y_idx invisible;

Índice modificado.

rop@DESA11G> select index_name,visibility from user_indexes where table_name = 'T';

INDEX_NAME VISIBILIT
------------------------------ ---------
T_Y_IDX INVISIBLE

rop@DESA11G> explain plan for select max(x) from t where y = 'A';

Explicado.

rop@DESA11G> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 1842905362

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2505 (2)| 00:00:31 |
| 1 | SORT AGGREGATE | | 1 | 65 | | |
|* 2 | TABLE ACCESS FULL| T | 100 | 6500 | 2505 (2)| 00:00:31 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("Y"='A')

Note
-----
- dynamic sampling used for this statement

18 filas seleccionadas.


Veamos como trabajar con su visibilidad a nivel sesion:

rop@DESA11G> sho parameter optimizer_use_invisible_indexes

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_use_invisible_indexes boolean FALSE
rop@DESA11G> alter session set optimizer_use_invisible_indexes=true;

Sesión modificada.

rop@DESA11G> explain plan for select max(x) from t where y = 'A';

Explicado.

rop@DESA11G> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 767882303

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 65 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 65 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T_Y_IDX | 1 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - access("Y"='A')

Note
-----
- dynamic sampling used for this statement

19 filas seleccionadas.

rop@DESA11G>


Siempre que se piensa en agregar un indice hay que analizar el trade-off que existe entre el beneficio de un acceso por indice contra la degradación en las dml's sobre la tabla, ya que ahora habrá un nuevo indice que mantener en cada update/delete/insert/merge. Se puede crear invisible, de forma tal de no repercutir en los planes y ver como es afectada la operatoria dml sobre la tabla y luego ver si conviene ponerlo visible o directamente borrarlo.


4. Tablas de solo lectura (Read Only Tables)

No hay mucho para contarles sobre este nuevo feature, solo para considerar que ahora en 11g
no solo se puede poner la base de datos o algun tablespace en read only sino que podemos ir
mas fino en la granularidad y alterar una tabla para que solo se pueda consultar. Ahi va un ejemplo bien sencillo:


rop@DESA11G> alter table t read only;

Tabla modificada.

rop@DESA11G> insert into t values (1,'a');
insert into t values (1,'a')
*
ERROR en línea 1:
ORA-12081: no se permite la operación de actualización en la tabla "ROP"."T"


rop@DESA11G> alter table t read write;

Tabla modificada.

rop@DESA11G> insert into t values (1,'a');

1 fila creada.

rop@DESA11G> commit;

Confirmación terminada.

rop@DESA11G>


5 - Dependencia entre objetos mas fina (Fine Grained Dependencies)

Quien ha trabajado algunos años con Oracle y haya tenido que realizar modificaciones "en caliente" de algún objeto en producción, seguramente se ha encontrado con invalidaciones de objetos relacionados directa o indirectamente con el objeto modificado. Esto puede tener implicaciones terribles en producción y literalmente "freezar" la actividad sobre las base. En lo posible es recomendable realizar cambios en periodos de minima actividad o idealmente en ventanas de mantenimiento, si las hubiera, para asi evitar riesgos.

Veamos una invalidación en 10g que no es lógica, ya que vamos a ver como una vista definida sobre un select de dos campos de una tabla se invalida al agregar un tercer campo en la tabla que no tiene nada que ver con la vista.

En 10g:

rop@DESA10G> create view t_v as select x,y from t;

Vista creada.

Transcurrido: 00:00:00.17
rop@DESA10G> select status from user_objects where object_name = 'T_V';

STATUS
-------
VALID

Transcurrido: 00:00:00.10
rop@DESA10G> alter table t add z date;

Tabla modificada.

Transcurrido: 00:00:00.60
rop@DESA10G> select status from user_objects where object_name = 'T_V';

STATUS
-------
INVALID

Transcurrido: 00:00:00.10
rop@DESA10G> select count(1) from t_v;

COUNT(1)
----------
1000000

Transcurrido: 00:00:00.89
rop@DESA10G> select status from user_objects where object_name = 'T_V';

STATUS
-------
VALID

Transcurrido: 00:00:00.09
rop@DESA10G>

La vista se invalidó y recien cuando se invocó por primera vez se compiló ok. Imaginemos que de esta vista pueden depender objetos de código como stored procedures, paquetes, funciones, etc y que se descompiladaran en cascada.

En 11g la dependencia es mas fina y evita invalidaciones innecesarias:

En 11g:

rop@DESA11G> create view t_v as select x,y from t;

Vista creada.

Transcurrido: 00:00:00.18
rop@DESA11G> select status from user_objects where object_name = 'T_V';

STATUS
-------
VALID

Transcurrido: 00:00:00.15
rop@DESA11G> alter table t add z date;

Tabla modificada.

Transcurrido: 00:00:00.09
rop@DESA11G> select status from user_objects where object_name = 'T_V';

STATUS
-------
VALID

Transcurrido: 00:00:00.07
rop@DESA11G>

No hay comentarios:

Publicar un comentario