Oracle no está utilizando un índice único para una clave larga


16

Tengo una tabla con 250K filas en mi base de datos de prueba. (Hay unos pocos cientos de millones en producción, podemos observar el mismo problema allí). La tabla tiene un identificador de cadena nvarchar2 (50), no nulo, con un índice único (no es el PK).

Los identificadores están formados por una primera parte que tiene 8 valores diferentes en mi base de datos de prueba (y alrededor de mil en producción), luego un signo @, y finalmente un número, de 1 a 6 dígitos de largo. Por ejemplo, podría haber 50 mil filas que comienzan con 'ABCD_BGX1741F_2006_13_20110808.xml @', y le siguen 50 mil números diferentes.

Cuando busco una sola fila en función de su identificador, la cardinalidad se estima en 1, el costo es muy bajo y funciona bien. Cuando busco más de una fila con varios identificadores en una expresión IN o una expresión OR, las estimaciones para el índice son completamente incorrectas, por lo que se utiliza una exploración de tabla completa. Si fuerzo el índice con una pista, es muy rápido, el escaneo completo de la tabla en realidad se ejecuta un orden de magnitud más lento (y mucho más lento en la producción). Por lo tanto, es un problema de optimizador.

Como prueba, dupliqué la tabla (en el mismo esquema + espacio de tabla) con exactamente el mismo DDL y exactamente el mismo contenido. Recreé el índice único en la primera tabla para una buena medida, y creé exactamente el mismo índice en la tabla de clonación. Hice un DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);. Incluso puede ver que los nombres de índice son consecutivos. Entonces, la única diferencia entre las dos tablas es que la primera se cargó en orden aleatorio durante un largo período de tiempo, con bloques dispersos en el disco (en un espacio de tabla junto con varias otras tablas grandes), la segunda se cargó como un lote INSERTAR-SELECCIONAR. Aparte de eso, no puedo imaginar ninguna diferencia. (La tabla original se ha reducido desde la última gran eliminación, y no ha habido una sola eliminación después de eso).

Aquí hay planes de consulta para los enfermos y la tabla de clonación (las cadenas debajo del pincel negro son las mismas en toda la imagen, y también debajo del pincel gris):

planes de consulta

(En este ejemplo, hay 1867 filas que comienzan con el identificador que está cepillado en negro. Una consulta de 2 filas produce una cardinalidad de 1867 * 2, una consulta de 3 filas produce una cardinalidad de 1867 * 3, etc. No se puede Por casualidad, a Oracle parece no importarle el final de los identificadores).

¿Qué podría causar este comportamiento? Obviamente, sería bastante costoso recrear la mesa en producción.

USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Solo cambié el esquema y el nombre del espacio de tabla. Puede ver que los nombres de tabla e índice son los mismos que en la captura de pantalla del plan de consulta.

Respuestas:


7

(Esto responde a la otra pregunta sobre por qué los histogramas son diferentes).

Los histogramas se crean de manera predeterminada en función del sesgo de la columna y de si la columna se usó en un predicado relevante. Copiar el DDL y los datos no es suficiente, la información de la carga de trabajo también es importante.

De acuerdo con la Guía de ajuste de rendimiento :

Cuando suelta una tabla, se pierde la información de carga de trabajo utilizada por la función de recopilación de histograma automático y el historial de estadísticas guardadas utilizado por los procedimientos RESTORE _ * _ STATS. Sin estos datos, estas características no funcionan correctamente.

Por ejemplo, aquí hay una tabla con datos asimétricos pero sin histograma:

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
NONE

Ejecutar lo mismo, pero con una consulta antes de que se recopilen las estadísticas, generará un histograma.

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
select count(*) from test1 where a = sysdate; --Only new line
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
FREQUENCY

2
Brillantemente simple ejemplo. ¿Tiene alguna idea de por qué la CBO estaba usando histogramas para estimar la cardinalidad en un escaneo único en lugar de solo asumir 1?
Jack Douglas el

¡Gracias! Hice una reproducción completa con mi tipo de datos y consultas en mi blog: joco.name/2014/01/05/…
fejesjoco

@ Jack Creo que es pereza. Los ingenieros de Oracle deben haber pensado que las estadísticas de un índice único tendrán el mismo número de valores distintos que las filas, por lo que el supuesto de cardinalidad 1 no está cableado, sino que simplemente se utiliza a partir de las estadísticas, como en cualquier otro caso. Además, como un caso general, los histogramas triunfan sobre las estadísticas simples. Mi caso parece ser muy especial solo por las teclas largas, pero creo que de lo contrario funciona bastante bien.
fejesjoco

@fejesjoco Creo que la explicación de JL es más probable, ya que los histogramas también habrían superado las estadísticas generales en el caso de una sola búsqueda (sin la in), ¿no? Creo que la CBO hace la suposición de cardinalidad 1, pero solo en el caso más simple. Supongo que podría UNION ALLsolucionar todo el problema utilizando un gran, pero puede haber otras razones para no hacerlo y JL menciona otras posibles soluciones en la publicación del blog vinculado.
Jack Douglas

1
Otro misterio menor a considerar: ¿cómo se creó este histograma en primer lugar? Oracle solo parece considerar que una columna está sesgada si tiene duplicados, que obviamente su columna única no puede tener. ¿Alguien creó intencionalmente este histograma (poco probable) o alguien recopiló estadísticas con los no recomendados method_opt=>'for all indexed columns'?
Jon Heller

8

¡Encontré la solución! Es tan hermoso y realmente aprendí MUCHO sobre Oracle.

En una palabra: histogramas.

Comencé a leer mucho sobre cómo funciona la CBO de Oracle y me topé con histogramas. No lo entendí completamente, así que eché un vistazo a la tabla USER_HISTOGRAMS y listo. Había varias filas para la mesa enferma, y ​​prácticamente nada para la mesa clonada. Para la tabla enferma, había una fila para cada una de las 8 partes de inicio de identificador diferentes. Y esta es la clave: se cortaron con 32 caracteres, antes del signo @. Como dije, la primera parte de las teclas es muy repetitiva, se vuelven diferentes después del signo @.

Parece que los histogramas pueden ser más poderosos que el simple hecho de que un índice único siempre tiene una cardinalidad de 0 o 1 para un valor dado. Cuando estaba buscando más de 2 filas, Oracle miró el histograma, pensó que podría haber decenas de miles de valores para esa parte de inicio del identificador, y desvió la CBO del rumbo.

¡Eliminé los histogramas para esa columna en la tabla anterior y el problema desapareció!

Más información: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating


2
Mencioné eso en nuestra sala de chat :) chat.stackexchange.com/transcript/message/12987649#12987649
Philᵀᴹ

No vi eso :). Entonces, lo único extraño es por qué había histogramas en la primera tabla y no en el clon, pensé que collect_schema_stats actualizaba todo, aparentemente no.
fejesjoco

6

Le envié un correo electrónico a Jonathan Lewis sobre esto y obtuve una respuesta muy útil:

La rareza en el cálculo es una consecuencia de los límites en los histogramas basados ​​en caracteres, ver particularmente:

http://jonathanlewis.wordpress.com/2010/10/13/frequency-histogram-5/ http://jonathanlewis.wordpress.com/2010/10/19/frequency-histograms-6/

Mirando el ejemplo, la consulta es para una lista IN, no para una sola fila, por lo que mi conjetura inicial sería que el optimizador ha utilizado una estrategia genérica para calcular la selectividad de varias filas en lugar de tener un código de caso especial para un Lista IN en una clave primaria. Supongo que no sería demasiado difícil para ellos reconocer este caso, pero los desarrolladores probablemente no han considerado que valga la pena el esfuerzo.

Le recomiendo leer las publicaciones de blog que vincula, que describen en detalle la limitación de los histogramas en los que se está ejecutando, por ejemplo:

Conclusión : si tiene cadenas bastante largas y similares en una columna que es un buen candidato para un histograma de frecuencia (por ejemplo, una columna de estado muy descriptiva), entonces tiene un problema si un valor que es muy raro parece idéntico a un muy popular valor hasta los primeros 32 caracteres. Puede encontrar que la única solución es cambiar la lista de valores legales (aunque varias estrategias que involucran columnas virtuales o índices basados ​​en funciones pueden evitar el problema).


Lamentablemente, los histogramas parecen ser una característica poco conocida, supongo que es porque es demasiado profunda para un desarrollador de SQL y la mayoría de las veces simplemente funcionan, pero es bueno saber que hay muchos recursos al respecto, simplemente no estaba buscando en el lugares correctos :). Es bastante malo que Oracle corte a 32 bytes y tome decisiones desastrosas basadas en eso. Afortunadamente, no necesito ningún ajuste, dejar caer los histogramas es una solución perfecta. Los valores clave son únicos, siempre busco 20 valores a la vez, funciona bien solo con un índice y es determinista. Pero no usaré teclas largas la próxima vez, eso es seguro.
fejesjoco

Los histogramas son bastante conocidos entre los DBA;) Me encanta el hecho de que pareces ansioso por aprender cosas más profundas y realmente crees que deberías leer el libro de JL, es muy, muy bueno. La CBO generalmente hace un gran trabajo: siempre habrá casos extremos que deben investigarse, pero vale la pena tener en cuenta que incluso sin el corte, las estimaciones siempre son solo estimaciones.
Jack Douglas

1
Si ejecuta un trabajo de estadísticas regular (como el que ejecuta Oracle de forma predeterminada en una instalación limpia), es posible que reaparezcan los histogramas, es posible que deba buscar una forma de evitarlo (como LOCK_TABLE_STATS tal vez)
Jack Douglas

Mencioné una publicación de blog en mi respuesta, hay instrucciones sobre cómo prevenir histogramas para una columna.
fejesjoco

1
@Jack Douglas, ¡gracias por involucrar a J. Lewis e informarnos!
Dimitre Radoulov
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.