You are hereOracle / UPDATE detalles internos...y sus efectos secundarios

UPDATE detalles internos...y sus efectos secundarios


By IgnacioRuiz - Posted on 23 May 2010

A primera vista, puede parecer que hablar de UPDATE sea trivial, sin embargo en algunos casos puede ser la causa de severos problemas de performance, tareas de mantenimiento innecesarias y/o desperdicio de storage.

Tal vez esten pensando: "un UPDATE es muy basico, ¿porque deberia preocuparme?", bueno hay casos en los que al usar UPDATE sin cuidado, las cosas se pueden poner feas. Lo podemos observar con mi viejo compañero ALTER SYSTEM DUMP BLOCK y unos inserts y updates, sobre una tabla con 3 columnas, dos de ellas tipo VARCHAR2.

Esta demostración la hago usando una base de datos Oracle 10.2.0.4, veamos el desarrollo:

create table table5 (col1 number, col2 varchar(10), col3 varchar(46));

insert into table5 (col1,col2,col3) values (1,'row one','This is the first row');

commit;

insert into table5 (col1,col2,col3) values (2,'row two','This is the 2nd row,updates will be Committed!');

commit;
alter session set tracefile_identifier='table5_04_b103';
alter system dump datafile 6 block 103;

Les recomiendo que usen el parametro TRACEFILE_IDENTIFIER, ya que ayuda a identificar con rapidez el archivo trace generado.

Abajo el estado inicial de los bloques donde se insertaron los registros, segun el vaciado (dump) de los mismos:

CF8F390 C1023320 01FFFF02 C102FF80 0203022C [ 3..........,...]
CF8F3A0 720703C1 7420776F 542D6F77 20736968 [...row two-This ]
CF8F3B0 74207369 32206568 7220646E 752C776F [is the 2nd row,u]
CF8F3C0 74616470 77207365 206C6C69 43206562 [pdates will be C]
CF8F3D0 696D6D6F 21646574 0203012C 720702C1 [ommited!,......r]
CF8F3E0 6F20776F 5415656E 20736968 74207369 [ow one.This is t]
CF8F3F0 66206568 74737269 776F7220 14A30601 [he first row....]
Block header dump: 0x01800067
...
0xe:pti[0] nrow=2 offs=0
0x12:pri[0] offs=0x1f74
0x14:pri[1] offs=0x1f38
block_row_dump:
tab 0, row 0, @0x1f74
tl: 36 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 7] 72 6f 77 20 6f 6e 65
col 2: [21]
54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 72 6f 77
tab 0, row 1, @0x1f38
tl: 60 fb: --H-FL-- lb: 0x2 cc: 3
col 0: [ 2] c1 03
col 1: [ 7] 72 6f 77 20 74 77 6f
col 2: [45]
54 68 69 73 20 69 73 20 74 68 65 20 32 6e 64 20 72 6f 77 2c 75 70 64 61 74
65 73 20 77 69 6c 6c 20 62 65 20 43 6f 6d 6d 69 74 65 64 21
end_of_block_dump
End dump data blocks tsn: 7 file#: 6 minblk 103 maxblk 103

Muestra los dos registros: iniciando de abajo hacia arriba con el primer registro; a continuación, "sobre" él, el segundo registro/tupla (es por este crecimiento de abajo hacia arriba
que las tablas Oracle se llaman mas apropiadamente HEAP ORGANIZED TABLE).

Ahora sometamos al registro #2 a algunas actualizaciones en la columna 2 (col2), para la cual cambiaremos su valor de 'row two' a 'row two-02', hasta llegar a 'row two-25'

update table5b set col2 = 'row two-02' where col1 = 2;
...
update table5b set col2 = 'row two-25' where col1 = 2;


CF8F350 C3041100 FF373005 1702C102 03012C2F [.....07...../,..]
CF8F360 0A03C102 20776F72 2D6F7774 542D3532 [....row two-25-T]
CF8F370 20736968 74207369 32206568 7220646E [his is the 2nd r]
CF8F380 752C776F 74616470 77207365 206C6C69 [ow,updates will ]
CF8F390 43206562 696D6D6F 21646574 0203002C [be Commited!,...]
CF8F3A0 720703C1 7420776F 542D6F77 20736968 [...row two-This ]
CF8F3B0 74207369 32206568 7220646E 752C776F [is the 2nd row,u]
CF8F3C0 74616470 77207365 206C6C69 43206562 [pdates will be C]
CF8F3D0 696D6D6F 21646574 0203002C 720702C1 [ommited!,......r]
CF8F3E0 6F20776F 5415656E 20736968 74207369 [ow one.This is t]
CF8F3F0 66206568 74737269 776F7220 14F10602 [he first row....]
Block header dump: 0x01800067
...
0xe:pti[0] nrow=2 offs=0
0x12:pri[0] offs=0x1f74
0x14:pri[1] offs=0x1ef9
block_row_dump:
tab 0, row 0, @0x1f74
tl: 36 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 02
col 1: [ 7] 72 6f 77 20 6f 6e 65
col 2: [21]
54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 72 6f 77
tab 0, row 1, @0x1ef9
tl: 63 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 03
col 1: [10] 72 6f 77 20 74 77 6f 2d 32 35
col 2: [45]
54 68 69 73 20 69 73 20 74 68 65 20 32 6e 64 20 72 6f 77 2c 75 70 64 61 74
65 73 20 77 69 6c 6c 20 62 65 20 43 6f 6d 6d 69 74 65 64 21
end_of_block_dump

Actualizar col2 de 'row two' a 'row two-02', requirió duplicar el registro completo... por supuesto, el registro anterior con col2='row two' fue borrado logicamente, permaneciendo en el registro (Texto en gris, en el vaciado de bloque arriba). ¿Es este un registro muerto? Si, lo es...

Despues de 24 UPDATEs consecutivos, no se observamos nuevos duplicados... probablemente supusieron eso, pero ¿que pasó entonces? Parece que
el algoritmo de UPDATE de Oracle tiene la inteligencia suficiente para discernir que el valor anterior y el nuevo poseen la misma longitud, y actualiza
col2 en el mismo punto. El UPDATE en columnas CHAR (tipos de datos de longitud fija) se comportan de esta manera invariablemente.

Bien, tratemos ahora de modificar la longitud de los valores a actualizar entre transacciones. Primero alternemos tambien actualizaciones en col2 y col3.


update table5 set col2 = 'row two-38' where col1 = 2;
commit;

update table5 set col3 = 'Now update the second row, shorter text!' where col1 = 2;
commit;

update table5 set col2 = 'row 2-39' where col1 = 2;
commit;

update table5 set col3 = 'Now re-update the second row, another text!' where col1 = 2;
commit;

update table5 set col2 = 'row two-40' where col1 = 2;
commit;

update table5 set col3 = 'Now re-re-update the second row, another text!' where col1 = 2;
commit;

update table5 set col2 = 'row 2-41' where col1 = 2;
commit;

update table5 set col3 = 'Now re-re-re-update the second row, will fit?' where col1 = 2;
commit;

update table5 set col2 = 'row two-42' where col1 = 2;
commit;

update table5 set col3 = 'Now re-re-re-re-update the 2nd row, changed?' where col1 = 2;
commit;

update table5 set col2 = 'row two-43' where col1 = 2;

y posteriormente, actualizaciones solo en col2

update table5 set col2 = 'row two-44' where col1 = 2;
commit;

update table5 set col2 = 'row 2-45' where col1 = 2;
commit;

update table5 set col2 = 'row two-46' where col1 = 2;
commit;

update table5 set col2 = 'row 2-47' where col1 = 2;
commit;

update table5 set col2 = 'row two-48' where col1 = 2;
commit;

update table5 set col2 = 'row 2-49' where col1 = 2;

Veamos como luce el vaciado del bloque; repentinamente el bloque aparece lleno, como si hubieramos instruido inserciones en vez de actualizaciones, muy extraño por decir lo menos

D84D000 61623763 4E5F3163 2C6F6765 C1020302 [c7bac1_Nego,....]
D84D010 6F720803 2D322077 4E2C3934 7220776F [..row 2-49,Now r]
D84D020 65722D65 2D65722D 752D6572 74616470 [e-re-re-re-updat]
D84D030 68742065 6E322065 6F722064 63202C77 [e the 2nd row, c]
D84D040 676E6168 2C3F6465 C1020300 6F720A03 [hanged?,......ro]
D84D050 77742077 38342D6F 776F4E2C 2D657220 [w two-48,Now re-]
D84D060 722D6572 65722D65 6470752D 20657461 [re-re-re-update ]
D84D070 20656874 20646E32 2C776F72 61686320 [the 2nd row, cha]
...... 46 rows not shown .....
D84D360 0A03C102 20776F72 2D6F7774 542D3833 [....row two-25-T]
D84D370 20736968 74207369 32206568 7220646E [his is the 2nd r]
D84D380 752C776F 74616470 77207365 206C6C69 [ow,updates will ]
D84D390 43206562 696D6D6F 21646574 0203002C [be Commited!,...]
D84D3A0 720703C1 7420776F 542D6F77 20736968 [...row two-This ]
D84D3B0 74207369 32206568 7220646E 752C776F [is the 2nd row,u]
D84D3C0 74616470 77207365 206C6C69 43206562 [pdates will be C]
D84D3D0 696D6D6F 21646574 0203002C 720702C1 [ommited!,......r]
D84D3E0 6F20776F 5415656E 20736968 74207369 [ow one.This is t]
D84D3F0 66206568 74737269 776F7220 61470602 [he first row..Ga]
Block header dump: 0x01800067

Parece que el bloque contiene muchos registros, pero una mirada al encabezado del bloque nos indica la situación real en nrow: el bloque contiene 2 registros/tuplas vivas, y todo lo demás esta borrado "logicamente", muerto... espacio desperdiciado (en color gris, vaciado de bloque arriba).

ntab=1
nrow=2
frre=-1
fsbo=0x16
fseo=0x1ba7
avsp=0x1f20
tosp=0x1f22
0xe:pti[0] nrow=2 offs=0
0x12:pri[0] offs=0x1f74
0x14:pri[1] offs=0x1ba7

Asi es, esto es algo de lo cual se debe tener cuidado... conclusion: las operaciones UPDATE en campos con tipos de longitud variable no deben tomarse a la ligera.

Recuerden que siempre hay un balance, en este caso el algortimo para la actualizacion de columnas con tipo de longitud variable (como VARCHAR2) esta optimizado para el performance, que es una de las características sobresalientes de Oracle.

Es deseable que este comportamiento se prevenga en etapas de diseño o programación, evitando UPDATEs repetitivos sobre el mismo registro o privilegiando el uso de CHAR o tipos de longitud constante, con un "desperdicio" fijo y constante de espacio, versus VARCHAR2/tipos de longitud variable y casos extremos como el presentado.

Si la solución preventiva no es factible, entonces Oracle Database provee las instrucciones ALTER TABLE para lidiar con esto y dar mantenimiento a la tabla, reorganizar los datos y deshacernos de los "registros muertos".

Quiz: ¿Nuestras sentencias salvadoras son ALTER TABLE {tabla} SHRINK SPACE o ALTER TABLE {tabla} SHRINK SPACE COMPACT?
¿Que prerequisitos se deben cumplir para hacer uso efectivo de cualquier de estas sentencias?
¿Son intercambiebles o cual es su diferencia funcional?

Muchas gracias por su atención hoy, espero el contenido sea de su utilidad. La próxima vez abordaré el tema de updates con tablas que poseen INDICES...

Si el articulo no satisfizo tus dudas, visita nuestros foros


Visita mi pagina en Oracle Community
Visita mi blog en Blogger

Suscripcion a Contenido Sindicado(RSS)

Suscribir a Databases Hispamerica por Email

Follow IgnacioRuizMX on Twitter



Syndicate

Syndicate content

Follow DatabasesLA on Twitter

Who's online

There are currently 0 users and 6 guests online.

Estadisticas

Locations of visitors to this page

hidden hit counter