domingo, 12 de diciembre de 2010

Oracle OpenWorld Latin America 2010

Es difíci retomar el blog después de tantas cosas importantes que pasaron este año y no tuve tiempo de comentar.
Como el primer evento del grupo de usuarios Oracle del Uruguay (UYOUG) en junio, o el  UYOUG OTN Day en octubre.
O la cantidad de borradores de post técnicos esperando ser pulidos para ver la luz.

Pero después de haber participado del Oracle Open World Latin América en San Pablo, que terminó el pasado jueves, comentarlo no puede hacerse esperar.

Fue mi primer participación en un Open World. Comentaron quienes estuvieron en otras ediciones que este fue bueno, pero quizás no el mejor.

En mi experiencia, fue excelente.
A pesar de no poder haber ido a 32 de las 40 charlas diarias, por haber 8 sesiones en paralelo, sin contar las sesiones de Develop y JavaOne.
A pesar de perderme muchos de los hands on labs, por saber tan poco de tantos temas.
A pesar de haber gastado tanto en tan pocos días, porque el cambio no nos favorece.

¿Por qué fue tan bueno entonces ?.

Porque rindieron las presentaciones a las que fuí: además de ser muy buenas, pude charlar con presentadores como Simon Law, Principal Product Manager de Oracle TimesTen In-Memory Database. Tomas Ulin, VicePresident de MySQL development. Andre Luiz de Almeida, Director Técnico del departamento de Salud del estado de Sao Paulo. Y Rodrigo Queiroga, del departamento de Salud del estado de Minas Gerais.

Porque pude conocer personalmente a mucha gente que ya conocía de forma virtual: reconocidos miembros de la comunidad Oracle de Brasil (Rodrigo Almeida, Marcos Vinicius, Ricardo Portilho, Eduardo Hahn), Perú (Miguel Palacios), y gente de Oracle que apoya la actividad de los grupos de usuarios (Marina Neumann), entre otros.

Porque además se ven de cerca los planes en todos los frentes: hardware, aplicativos, tecnología, integración de productos.

Y se puede ver todo esto acá nomás, en San Pablo, sin necesidad de viajar 20 horas y gastar cinco veces más yendo a San Francisco.
Claro, van a decir que esto no es lo mismo.
Y comparto.
Acá hay sesiones en portugués y español, además de inglés.
Acá hablamos en los demos con instructores y otros asistentes interesados, en los corredores, en los stands, de realidades similares a las nuestras.

Todavía no encontré el material de las presentaciones publicado en el sitio oficial del evento. Ojalá lo hagan en algún momento.

Si hay fotos, gracias a Edel y Daniel.

Si llegaron hasta acá, espero que les haya dado ganas de haber estado, y que no quieran perderse el próximo.
Vale la inversión.

Un saludo.

sábado, 13 de marzo de 2010

Usando funciones en índices para columnas con valores dispersos

Hace unos días tuve que mejorar una consulta como esta que demoraba varios minutos y generó una tranca en el acceso a disco, con el evento 'db file scattered read':

select *
from SMALL
where DESTINO2 = 2 and solicitud2 not in
(select solicitud from TINY
where DESTINO=2 and solicitud not in
(select solicitud2 from BIGTABLE where estado='F'));

El plan de ejecución es:

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2833 | 376K| 220K (2)| 00:44:08 |
|* 1 | FILTER | | | | | |
|* 2 | TABLE ACCESS FULL | SMALL | 2833 | 376K| 173 (1)| 00:00:03 |
|* 3 | FILTER | | | | | |
|* 4 | TABLE ACCESS FULL| TINY | 1 | 8 | 122 (2)| 00:00:02 |
|* 5 | TABLE ACCESS FULL| BIGTABLE | 1 | 5 | 34 (3)| 00:00:01 |
--------------------------------------------------------------------------------------------

El primer paso obvio es crear índices, ya que estas tablas solo tienen PK y no son estas columnas. Revisando el tamaño de las tablas, hay una muy grande:

TABLE_NAME NUM_ROWS
------------------------------ ----------
SMALL 51602
BIGTABLE 10762233
TINY 26882

Sobre las dos tablas chicas se pueden crear sin problemas los indices faltantes, pero sobre la grande conviene analizar un poco más:

create index i_TINY on TINY (DESTINO, solicitud);
create index i_SMALL on SMALL (DESTINO2, solicitud2);

En BIGTABLE revisamos la distribución de datos en la columna usada para filtrar, para ver que tan selectivo es el filtro usado por la consulta:

select estado, count(1)
from BIGTABLE
group by estado;

E COUNT(1)
- ----------
H 13154
P 10321688
D 1740
A 288
E 13349
L 2659
S 1829
G 4831
F 2552

En este caso, para el valor F hay muy pocos registros. Habría que revisar el código de la aplicación para ver si este filtro es estático.

En caso que pueda usarse cualquier valor para filtrar, hay que evaluar la frecuencia de sentencias DML (insert/update/delete) sobre la tabla, ya que un nuevo índice puede hacer que estas operaciones sean más lentas, y además se debe evaluar el espacio que ocupa el mismo.

En caso que siempre se filtre por F es donde se pone interesante, ya que podemos indexar usando una función de forma que el índice incluya sólo los registros que nos interesa. Esto hace que el índice sea mucho más chico, por lo tanto ocupa muy poco espacio, involucra menos operaciones de IO, y no enlentece tanto las operaciones DML.
Solo debe hacerse un pequeño cambio en la forma de escribir la consulta para que sea usado por Oracle.

El índice quedaría:

create index i_BIGTABLE_est on BIGTABLE(decode(estado,'F','F',null),solicitud2);

Y la nueva consulta :

select *
from SMALL
where DESTINO2 = 2 and solicitud2 not in
(select solicitud from TINY
where DESTINO=2 and solicitud not in
(select solicitud2 from BIGTABLE where decode(estado,'F','F',null)='F'));

El plan ahora es mucho más lindo, y lo que es mejor, involucra mucho menos operaciones de IO, al utilizar índices que son mucho más chicos que la tabla original:

-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2833 | 376K| 35522 (4)| 00:07:07 |
|* 1 | FILTER | | | | | |
|* 2 | TABLE ACCESS FULL | SMALL | 2833 | 376K| 173 (1)| 00:00:03 |
|* 3 | FILTER | | | | | |
|* 4 | INDEX FAST FULL SCAN| I_TINY | 1 | 8 | 22 (5)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | I_BIGTABLE_EST | 1 | 5 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------