sql >> Base de Datos >  >> RDS >> Oracle

Índice de tiempo constante para la columna de cadena en la base de datos de Oracle

Los clústeres hash pueden proporcionar tiempo de acceso O(1), pero no tiempo de aplicación de restricciones O(1). Sin embargo, en la práctica, el tiempo de acceso constante de un clúster hash es peor que el tiempo de acceso O (log N) de un índice de árbol b regular. Además, los clústeres son más difíciles de configurar y no escalan bien para algunas operaciones.

Crear clúster hash

drop table orders_cluster;
drop cluster cluster1;

create cluster cluster1
(
    MerchantID number,
    TransactionID varchar2(20)
)
single table hashkeys 10000; --This number is important, choose wisely!

create table orders_cluster
(
    id number,
    MerchantID number,
    TransactionID varchar2(20)
) cluster cluster1(merchantid, transactionid);

--Add 1 million rows.  20 seconds.
begin
    for i in 1 .. 10 loop
        insert into orders_cluster
        select rownum + i * 100000, mod(level, 100)+ i * 100000, level
        from dual connect by level <= 100000;
        commit;
    end loop;
end;
/

create unique index orders_cluster_idx on orders_cluster(merchantid, transactionid);

begin
    dbms_stats.gather_table_stats(user, 'ORDERS_CLUSTER');
end;
/

Crear tabla regular (para comparación)

drop table orders_table;

create table orders_table
(
    id number,
    MerchantID number,
    TransactionID varchar2(20)
) nologging;

--Add 1 million rows.  2 seconds.
begin
    for i in 1 .. 10 loop
        insert into orders_table
        select rownum + i * 100000, mod(level, 100)+ i * 100000, level
        from dual connect by level <= 100000;
        commit;
    end loop;
end;
/

create unique index orders_table_idx on orders_table(merchantid, transactionid);

begin
    dbms_stats.gather_table_stats(user, 'ORDERS_TABLE');
end;
/

Ejemplo de seguimiento

SQL*Plus Autotrace es una forma rápida de encontrar el plan de explicación y realizar un seguimiento de la actividad de E/S por declaración. El número de solicitudes de E/S se etiqueta como "obtenciones consistentes" y es una forma decente de medir la cantidad de trabajo realizado. Este código demuestra cómo se generaron los números para otras secciones. Las consultas a menudo necesitan ejecutarse más de una vez para calentar las cosas.

SQL> set autotrace on;
SQL> select * from orders_cluster where merchantid = 100001 and transactionid = '2';

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 621801084

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |    16 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS HASH| ORDERS_CLUSTER |     1 |    16 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

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

   1 - access("MERCHANTID"=100001 AND "TRANSACTIONID"='2')


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         31  consistent gets
          0  physical reads
          0  redo size
        485  bytes sent via SQL*Net to client
        540  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>

Encuentre claves hash óptimas, compensaciones

Para un rendimiento de lectura óptimo, todas las colisiones hash deben caber en un bloque (toda la E/S de Oracle se realiza por bloque, generalmente 8K). Conseguir el almacenamiento ideal es complicado y requiere conocer el algoritmo hash, el tamaño del almacenamiento (que no es el mismo que el tamaño del bloque) y la cantidad de claves hash (los cubos). Oracle tiene un algoritmo y un tamaño predeterminados, por lo que es posible centrarse en un solo atributo, la cantidad de claves hash.

Más claves hash conducen a menos colisiones. Esto es bueno para el rendimiento de TABLE ACCESS HASH ya que solo hay un bloque para leer. A continuación se muestra la cantidad de resultados consistentes para diferentes tamaños de hashkey. A modo de comparación, también se incluye un índice de acceso. Con suficientes hashkeys, el número de bloques se reduce al número óptimo, 1.

Method          Consistent Gets (for transactionid = 1, 20, 300, 4000, and 50000)
Index           4,  3,  3,  3,  3
Hashkeys 100    1, 31, 31, 31, 31
Hashkeys 1000   1,  3,  4,  4,  4
Hashkeys 10000  1,  1,  1,  1,  1

Más claves hash también conducen a más cubos, más espacio desperdiciado y una operación TABLE ACCESS FULL más lenta.

Table type      Space in MB
HeapTable       24MB
Hashkeys 100    26MB
hashkeys 1000   30MB
hashkeys 10000  81MB

Para reproducir mis resultados, use una consulta de muestra como select * from orders_cluster where merchantid = 100001 and transactionid = '1'; y cambie el último valor a 1, 20, 300, 4000 y 50000.

Comparación de rendimiento

Los resultados consistentes son predecibles y fáciles de medir, pero al final del día solo importa la hora del reloj de pared. Sorprendentemente, el acceso al índice con 4 veces más resultados consistentes sigue siendo más rápido que el escenario de clúster hash óptimo.

--3.5 seconds for b-tree access.
declare
    v_count number;
begin
    for i in 1 .. 100000 loop
        select count(*)
        into v_count
        from orders_table
        where merchantid = 100000 and transactionid = '1';
    end loop;
end;
/

--3.8 seconds for hash cluster access.
declare
    v_count number;
begin
    for i in 1 .. 100000 loop
        select count(*)
        into v_count
        from orders_cluster
        where merchantid = 100000 and transactionid = '1';
    end loop;
end;
/

También probé la prueba con predicados variables pero los resultados fueron similares.

¿Es escalable?

No, los clústeres hash no escalan. A pesar de la complejidad de tiempo O(1) de TABLE ACCESS HASH y la complejidad de tiempo O(log n) de INDEX UNIQUE SCAN, los clústeres hash nunca parecen superar los índices de árbol b.

Probé el código de muestra anterior con 10 millones de filas. El clúster de hash tardó mucho en cargarse y aun así tuvo un rendimiento inferior al índice de rendimiento de SELECT. Traté de escalarlo hasta 100 millones de filas, pero la inserción iba a tardar 11 días.

La buena noticia es que b*trees escala bien. Agregar 100 millones de filas al ejemplo anterior solo requiere 3 niveles en el índice. Revisé todos los DBA_INDEXES para un entorno de base de datos grande (cientos de bases de datos y un petabyte de datos), el peor índice tenía solo 7 niveles. Y ese fue un índice patológico en VARCHAR2(4000) columnas En la mayoría de los casos, sus índices de árbol b se mantendrán superficiales independientemente del tamaño de la tabla.

En este caso, O(log n) vence a O(1).

Pero ¿POR QUÉ?

El rendimiento deficiente del clúster hash es quizás una víctima del intento de Oracle de simplificar las cosas y ocultar el tipo de detalles necesarios para que un clúster hash funcione bien. Los clústeres son difíciles de configurar y usar correctamente y, de todos modos, rara vez proporcionarían un beneficio significativo. Oracle no ha puesto mucho esfuerzo en ellos en las últimas décadas.

Los comentaristas tienen razón en que lo mejor es un índice b-tree simple. Pero no es obvio por qué eso debería ser cierto y es bueno pensar en los algoritmos utilizados en la base de datos.