[Oracle] Optimizer RBO y CBO

1. ¿Qué es un optimizador?

Optimizer (Optimizer) es un subsistema central integrado en la base de datos Oracle, que puede entenderse como un módulo central o un componente funcional central en la base de datos Oracle. El objetivo del optimizador es obtener el plan de ejecución del SQL de destino.

Hay dos tipos de optimizadores en Oracle:

  • RBO: abreviatura de Optimizador basado en reglas, traducido literalmente como: optimizador basado en reglas
  • CBO: abreviatura de Optimizador basado en costos, traducido literalmente como: optimizador basado en costos

El principio de juicio utilizado por RBO es un conjunto de reglas integradas, que están codificadas en el código de la base de datos Oracle. RBO seleccionará una de las muchas rutas de ejecución posibles del SQL objetivo como su plan de ejecución de acuerdo con estas reglas; sin embargo, CBO utiliza El principio de juicio es el costo. CBO elegirá el más pequeño de las muchas rutas de ejecución posibles del SQL objetivo como su plan de ejecución. El valor del costo de cada ruta de ejecución se basa en los objetos relacionados, tales como tablas, índices y columnas involucradas en la declaración SQL objetivo. La información estadística se calcula.

El proceso de ejecución de sentencias SQL en la base de datos Oracle se puede representar en la Figura 1-1

2. Optimizador basado en reglas RBO

Oracle no admite RBO después de 10 g, pero el código RBO no se ha eliminado de la base de datos de Oracle, lo que significa que podemos seguir usando RBO modificando el bloque optimizador

Oracle asignará una calificación a varios tipos de rutas de ejecución en el código de antemano. Hay un total de 15 calificaciones, de grado 1 a grado 15, y Oracle pensará que cuanto menor sea la calificación, mayor será la eficiencia de ejecución. Al decidir la ruta de ejecución del SQL de destino, si hay más de una ruta de ejecución posible, el RBO seleccionará una ruta de ejecución con el valor de rango más bajo de las muchas rutas de ejecución posibles del SQL como su plan de ejecución.

Para las sentencias SQL OLTP, el acceso a través de ROWID es la forma más eficiente, mientras que el acceso a través de una exploración de tabla completa es la forma menos eficiente.En consecuencia, la ruta de ejecución correspondiente al nivel 1 integrado de RBO es "fila única por ID de fila" (acceso a una fila única de datos a través de Rowid), y la ruta de ejecución correspondiente al nivel 15 es "exploración de tabla completa (exploración de tabla completa)"

2.1 Las columnas en la Tabla 2 también tienen índices ¿Cómo elegir RBO?

(1) Crear una tabla y establecer un índice

-- 建表
create table emp_tmp as select * from emp;
-- 创建索引
create index idx_mgr_tmp on emp_tmp(mgr);
create index 了IDX_DEPTNO_TMP on emp_tmp(deptno);

(2) Cambie al modo RBO y verifique qué índice selecciona RBO

-- 切换为RBO模式
SQL> alter session set optimizer_mode=rule;

会话已更改。

-- 产生执行计划
/**
知识补充:
1.set autotrace off 不生成autotrace报告,这是默认模式;
2.set autotrace on explain 只显示优化器执行路径报告
3.set autotrace on statistics 只显示执行统计信息
4.set autotrace on 显示执行计划和统计信息
5.set autotrace traceonly 同4,但是不显示查询输出
6.set autotrace traceonly explain 同explain plan,但是不执行语句,只产生计划
7.set autotrace traceonly statistics 同5,只显示执行路径
*/ 
SQL> set autotrace traceonly explain

-- 执行SQL语句
select * from emp_tmp where mgr > 100 and deptno > 100

El plan de ejecución de salida es el siguiente: de

la figura anterior, podemos ver que RBO ha indexado IDX_DEPTNO_TMP, pero no el índice IDX_MGR_TMP.

2.2 Ajustar plan RBO

Para el caso en 2.1, uniéndose a nosotros descubrió que el índice IDX_MGR_TMP es más eficiente que el índice IDX_DEPTNO_TMP, entonces, ¿cómo puedo dejar que RBO escuche mi opinión?

2.2.1 Escribir SQL equivalente

Podemos tomar algunas transformaciones en la columna deptno, por ejemplo: deptno + 0, luego no se tomará el índice IDX_DEPTNO_TMP

-- 等价SQL
select * from emp_tmp where mgr > 100 and deptno + 0> 100;

El plan de ejecución de salida es el siguiente:

A través de la imagen de arriba, encontramos que el índice es realmente obediente, dejando IDX_MGR_TMP, pero hay otro método, como sigue 2.2.2

2.2.2 Modificar el orden de caché de los objetos en el diccionario de datos

Dado que acabamos de crear el índice IDX_MGR_TMP primero, y luego el índice IDX_DEPTNO_TMP, el IDX_MGR_TMP se almacena primero en la caché del diccionario de datos, y luego el IDX_DEPTNO_TMP se almacena en la memoria caché. Debido a que "los ladrillos del muro, este último primero", RBO eligió El índice IDX_DEPTNO_TMP, aquí, podemos hacer esto, eliminamos el índice IDX_MGR_TMP y reconstruimos, así que dejemos que el índice IDX_MGR_TMP se llame tarde.

-- 删除索引
drop index IDX_MGR_TMP;

-- 重建索引
create index idx_mgr_tmp on emp_tmp(mgr);

-- 查询
select * from emp_tmp where mgr > 100 and deptno > 100;

El plan de ejecución de salida es el siguiente:

De lo contrario, es realmente posible ajustar la selección de índice del RBO modificando el orden de caché del diccionario de datos.

2.2.3 Orden de la tabla de modificación de la conexión de varias mesas

Si el SQL de destino tiene dos o más rutas de ejecución con el mismo valor de nivel, puede ajustar el plan de ejecución del SQL de destino cambiando el orden en que los objetos involucrados en el SQL de destino aparecen en el texto de SQL.Esto generalmente se aplica a la situación en la que se producen múltiples uniones de tablas en el SQL de destino. Cuando hay dos o más valores de nivel de ruta de ejecución en el SQL de destino, el RBO se determinará en orden de derecha a izquierda Quién es la tabla de conducción y quién es la tabla de conducción, y luego elegirá el plan de ejecución de acuerdo con esto, si cambiamos el orden de los diversos objetos involucrados en el SQL de destino en el texto de SQL, también cambiará la conducción de la conexión de la tabla Tabla y tabla controlada, y luego ajustó el plan de ejecución de SQL.

-- 建表
create table emp_tmp1 as select * from emp;

-- 执行SQL
select t1.mgr,t2.deptno from emp_tmp t1,emp_tmp1 t2 where t1.empno=t2.empno

El plan de ejecución de salida es el siguiente:

Como se puede ver en la figura anterior, la tabla emp_tmp1 está a la derecha en el texto SQL, por lo que la tabla es la tabla conductora y emp_tmp es la tabla conducida. El plan de ejecución anterior es ordenar y fusionar conexiones.
Estrictamente hablando, la conexión de combinación de clasificación no tiene el concepto de tabla de unidades y tabla de unidades, aquí es solo por conveniencia de explicación y se agregó artificialmente el concepto anterior para clasificar la conexión de combinación

Pero la conclusión anterior tiene una premisa: el SQL de destino debe tener dos o más rutas de ejecución con el mismo valor de nivel. Es difícil para RBO elegir un plan de ejecución basado únicamente en el valor del nivel

¿Vamos a usar la tabla emp para asociarnos con la tabla emp_tmp para ver qué está pasando? La tabla emp tiene un índice de clave principal en emnpno

select t1.mgr,t2.deptno from emp t1,emp_tmp t2 where t1.empno=t2.empno;

En este momento, el plan de ejecución es una conexión de bucle anidado, y la tabla de conducción es emp_tmp

Veamos el plan de ejecución después de intercambiar la posición de la tabla emp y emp_tmp.

Se puede ver a partir de lo anterior que no hay cambios, entonces se verifica la conclusión: si el RBO puede seleccionar el plan de ejecución en función del tamaño de cada valor de nivel de ruta de ejecución del SQL objetivo, sin importar cómo ajustar la posición del objeto en el texto SQL, para Ninguno de los planes de ejecución de SQL tendrá un impacto.

3. Optimizador basado en costos CBO

RBO tiene defectos obvios, como muchas buenas características en Oracle, que ya no son compatibles con RBO, el plan generado por RBO es difícil de ajustar, etc. Estos aún son menores, la mayor crítica es que RBO está codificado en la base de datos , No considera el número real de objetos involucrados en el SQL de destino, la distribución de datos real, etc., de modo que una vez que las reglas no se aplican a los objetos reales involucrados en el SQL, el plan de ejecución generado según el RBO no es el plan de ejecución óptimo .
Da un ejemplo:

select * from emp where deptno=20

Supongamos que hay un índice de árbol B de valor de clave única llamado IDX_DEPTNO_TMP en el departamento de la tabla emp. Si aplicamos RBO, no importa qué tan grandes sean los datos EMP e independientemente de la distribución de la columna DEPTNO, Oracle lo ejecutará Primero vaya al índice y luego busque los registros en el EMP en la tabla posterior. Oracle no escaneará la tabla EMP en este momento. Para RBO,El valor de nivel de la exploración de tabla completa es más alto que el valor de nivel de la exploración de rango de índice.

Este tipo de rendimiento de RBO no es un problema cuando la cantidad de datos no es grande, o la cantidad de datos es grande, pero hay pocos registros que cumplan con las condiciones. Pero si el número es grande y las tres cuartas partes de los registros de la columna DEPTNO son deptno = 20, RBO primero escanea el índice y luego devuelve la tabla para obtener los datos, lo que obviamente no es tan rápido como un escaneo completo de la tabla.

Basado en las deficiencias de RBO, el índice introdujo CBO después de Oracle 7, cuando CBO elige la ruta de ejecución del SQL objetivo, todos los principios de juicio son costos, y CBO elegirá una ejecución con el valor de costo más pequeño de las muchas rutas de ejecución de la declaración SQL Ruta como su plan de ejecución. El valor de costo de cada ruta de ejecución se calcula en función de la información estadística de los objetos relacionados, como tablas, índices y columnas involucradas en la declaración SQL de destino.

El costo en la base de datos Oracle es en realidad una estimación de los recursos de E / S, CPU y red necesarios para ejecutar el SQL objetivo.

Nota especial:
Al calcular el costo de una ruta de ejecución, Oracle no necesariamente completa el cálculo de principio a fin, siempre que Oracle descubra que la parte calculada del valor del costo ha sido mayor que el valor de costo más pequeño guardado hasta el presente durante el proceso de cálculo. , Terminará inmediatamente el cálculo del valor de costo de la ruta de ejecución actual y, en su lugar, ejecutará el costo de la próxima ruta de ejecución nueva. Este proceso continuará hasta que se hayan calculado todas las rutas de ejecución posibles del SQL de destino o un umbral predefinido del número de rutas de ejecución que se calcularán.

3.1 Establecer potencial

La cardinalidad es una propiedad única de CBO, traducida como: establecer potencial. Se refiere al número de registros contenidos en la colección. Para decirlo claramente es especificar el número de filas en el conjunto de resultados.

La cardinalidad en realidad representa una estimación del número de registros contenidos en el resultado de ejecución de un paso de ejecución específico del SQL de destino. Por supuesto, si se dirige a todo el SQL de destino, Cardinality en este momento representa una estimación del número de registros incluidos en el resultado final de la ejecución de SQL.

La cardinalidad está estrechamente relacionada con la estimación del valor del costo, porque los recursos de E / S consumidos por el conjunto de resultados obtenido por Oracle pueden verse como crecientes con el número de registros.Por lo tanto, cuanto mayor sea el valor de Cardinalidad correspondiente a un paso de ejecución, mayor será el valor de costo correspondiente y mayor será el valor de costo total de la ruta de ejecución donde se encuentra el paso de ejecución.

3.2 Selectividad

La selectividad es también un concepto único de CBO. Se refiere a devolver un conjunto de resultados después de un número especificado de registros de condición predicado se aplica al número de registros en el conjunto de resultados original no se aplica a cualquiera de la condición predicado ratio.

La tasa seleccionable varía de 0 a 1. Cuanto menor sea el valor, mejor será la tasa seleccionable.

Los valores de selectividad y costo también están estrechamente relacionados, porqueCuanto mayor sea la capacidad de selección, mayor será el valor de Cardinalidad en el conjunto de resultados devuelto, por lo tanto, mayor será el costo estimado.

De hecho, CBO usa la selectividad para estimar la cardinalidad. La cardinalidad original se usa aquí para indicar el número de registros en el conjunto de resultados original sin condiciones de predicado, y la cardinalidad computada se usa para indicar los registros del conjunto de resultados devueltos después de aplicar las condiciones de predicado especificadas. Número, la fórmula es la siguiente:

Computed Cardinality = Original Cardinality *  selectivity

Aunque parece que la fórmula de cálculo para la tasa de selección es muy simple, de hecho, el proceso de cálculo específico es muy complicado. Cada caso específico tendrá una fórmula de cálculo diferente. En particular, no hay histograma en la columna de destino y ningún valor NULO. En este caso, la selectividad de la columna de destino para una consulta equivalente se calcula utilizando la siguiente fórmula:

selectivity = 1/NUM_DISTINCT 
--这里的NUM_DISTINCT 表示目标列的distinct值的数量

Caso:

--------建立测试表
create table person(id int,name varchar2(40),sal number,addr varchar2(100));
insert into person values(1,'Jack',10000,'China');
insert into person values(2,'Tom',20000,'China');
insert into person values(3,'Alice',30000,'China');
insert into person values(4,'Json',50000,'China');


SQL> alter table person modify (sal not null);

Table altered.

SQL> create index idx_person_sal on person(sal);

Index created.
SQL> select count(1) from person;

  COUNT(1)
----------
        4


SQL> select count(distinct sal) from person;

COUNT(DISTINCTSAL)
------------------
                 4
                 
SQL> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'PERSON',estimate_percent=>100,cascade=>true,method_opt=>'for all columns size 1',no_invalidate => false);

PL/SQL procedure successfully completed.   

SQL> set linesize 800
SQL> set pagesize 900

SQL> set autotrace traceonly explain
SQL> select * from person where sal=10000;

El plan de ejecución de salida es el siguiente:

La columna Costo (% CPU) ya existe en el plan de ejecución anterior, lo que significa que CBO se usa durante el análisis de SQL, y aquí Filas es el valor de Cardinalidad correspondiente a cada paso de ejecución en el plan de ejecución anterior, columna Costo (% CPU) registra el valor de costo correspondiente a cada paso en el plan de ejecución anterior.

Como se puede ver en la pantalla anterior, el plan de ejecución ahora toma la exploración de rango de índice de IDX_PERSON_SAL, el valor de las filas de fila correspondientes al paso de ejecución de Id = 2 es 1, lo que significa que la cardinalidad correspondiente a este paso evaluado por CBO 1. De manera similar, la cardinalidad correspondiente a Id = 0 también es 1.

Entonces, ¿cómo se calculan estos dos valores CBO?

Como se mencionó anteriormente: cuando no hay histograma en la columna de destino y no hay un valor NULL, la selectividad de la columna de destino para una consulta equivalente es: 1/4, y luego de acuerdo con la fórmula

Computed Cardinality = Original Cardinality *  selectivity

Se puede obtener fácilmente que el número de registros devueltos después de consultar a través de la condición where es: 4 * (1/4) = 1, por lo que la Cardinalidad correspondiente al resultado del paso Id = 2 también es 1, y debido a que la consulta completa solo tiene una condición where, el resultado de ejecución final La cardinalidad correspondiente también es 1

Ahora modificamos todos los valores de la columna SAL a 10000 y probamos nuevamente

SQL> update person set sal=10000;

4 rows updated.
SQL> commit;

Commit complete.

-- 重新收集一下统计信息
SQL> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'PERSON',estimate_percent=>100,cascade=>true,method_opt=>'for all columns size 1',no_invalidate => false);

PL/SQL procedure successfully completed.

SQL> select * from person where sal=10000;

El plan de ejecución de salida es el siguiente:

Como puede ver en la figura anterior, el valor de Cardinality ha cambiado de 1 a 4, lo cual es fácil de entender. Ahora el valor después de ditcinct en la columna SAL ha cambiado a 1, por lo que la selectividad de la consulta para el valor equivalente de la columna SAL es de 1/4 Se convierte en 1, por lo que el valor de Cardinalidad correspondiente al paso de ejecución y el valor de Cardinalidad correspondiente al resultado final será 4 * 1/1 = 4

Si el volumen de datos actual de la tabla de personas es 10 millones y todos los valores de sal son 10000, ¿cómo elegirá la CBO en este momento?

Aquí no es necesario insertar realmente 10 millones de filas de registros en la tabla. Es importante hacer que el volumen de datos de la tabla sea de 10 millones (porque la CBO calcula el costo basándose completamente en la información estadística de los objetos relevantes del SQL objetivo, por lo que solo necesita cambiar Las estadísticas de la persona de la tabla y el índice IDX_PERSON_SAL pueden ser)

-- 调整表的统计信息
SQL> exec dbms_stats.set_table_stats(ownname=>'SCOTT',tabname=>'PERSON',numrows=>10000000,no_invalidate => false);

PL/SQL procedure successfully completed.
-- 将索引IDX_PERSON_SAL对应其索引叶子块的数量的统计信息修改为10万
SQL> exec dbms_stats.set_index_stats(ownname=>'SCOTT',indname=>'IDX_PERSON_SAL',numlblks=>100000,no_invalidate => false);

PL/SQL procedure successfully completed.
SQL> select * from person where sal=10000;

El plan de ejecución de salida es el siguiente:

Como se puede ver en la figura, el valor de Cardinalidad actual se ha convertido en 10M (10M significa 10 millones), es decir, cuando ocurre esta situación extrema, la CBO no irá al índice, y se realiza un escaneo completo de la tabla .

En este momento modificamos el optimizador a RBO y vemos los resultados.

alter session set optimizer_mode=rule;
SQL> select * from person where sal=10000;

Como puede ver en la imagen de arriba, el modo RBO aún elige tomar el índice, no es tan inteligente como CBO.

3.3 Transmisibilidad

La transitividad es un concepto único de CBO. Lo primero que hace CBO en la conversión de consultas es que CBO simplemente puede reescribir el SQL de destino original.Una reescritura equivalente simple de SQL de destino usando transferibilidad solo es aplicable a CBO, RBO no hará tal cosa.

3.3.1 Entrega de predicado simple

t1.c1 = t2.c1 and t1.c1=10 等价于t1.c1 = t2.c1 and t1.c1=10 and t2.c1=10 

3.3.2 Entrega de predicado de conexión

t1.c1=t2.c1 and t2.c1=t3.c1 等价于 t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=t3.c1 

3.3.3 Entrega de predicado de conexión externa

t1.c1=t2.c1(+) and t1.c1=10 等价于 t1.c1=t2.c1(+) and t1.c1=10 and t2.c1(+)=10

Prueba de caso:

-- 建立两个表
create table t1(c1 number,c2 varchar2(10));
create table t2(c1 number,c2 varchar2(10));

-- 在表t2的c1列上建立索引
create index idx_t2 on t2(c1);

-- 插入测试数据
insert into t1 values(10,'aaa');
insert into t1 values(11,'bbb');
insert into t1 values(12,'ccc');
insert into t1 values(13,'ddd');

insert into t2 values(10,'aaa');
insert into t2 values(11,'bbb');
insert into t2 values(12,'ccc');
insert into t2 values(13,'ddd');

--- 修改为CBO优化器
alter session set optimizer_mode=all_rows;

select t1.c1,t2.c1 from t1,t2 where t1.c1=t2.c1 and t1.c1=10

El plan de ejecución de salida es:

Como se puede ver en la figura anterior, aunque no tenemos una condición de predicado simple para la columna c1 de la tabla t2, Oracle todavía toma el índice de la tabla t2. La condición de consulta de conducción para id = 4 a continuación es 4 - access("T2"."C1"=10)que esto no está en nuestro SQL La condición de predicado indica que la condición de predicado se reescribe de manera equivalente. SQL fue reescrito como:

select t1.c1,t2.c1 from t1,t2 where t1.c1=t2.c1 and t1.c1=10 and t2.c1=10

3.4 Limitaciones

El nacimiento de CBO es para resolver los defectos congénitos de RBO, pero a medida que la versión de Oracle continúa cambiando, CBO se vuelve más y más inteligente y más poderoso, pero esto no significa que CBO sea perfecto. Los siguientes son los defectos de CBO:

(1) La declaración SQL objetivo predeterminada de CBO donde las condiciones aparecen en cada columna es independiente, no hay relación

(2) CBO asumirá que todos los SQL de destino se ejecutan por separado y no interfieren entre sí.

Los bloques de hojas de índice y los bloques de datos a los que necesitamos acceder al ejecutar el SQL de destino pueden haberse almacenado en caché en el Buffer Cache debido al SQL ejecutado previamente, por lo que esta vez no es necesario gastar E / S físicas para leer en el disco relevante Para obtener datos, solo necesita leer en el caché. Por lo tanto, la CBO se ejecuta por separado: si el valor del costo se calcula sin considerar el método de caché, el costo del índice relevante puede sobreestimarse, lo que puede conducir a un plan de ejecución incorrecto.

(3) CBO tiene muchas restricciones en las estadísticas de histograma

(4) CBO puede perder el plan de ejecución correcto al analizar el SQL de destino de la asociación de varias tablas.

Supongo que te gusta

Origin www.cnblogs.com/OliverQin/p/12723891.html
Recomendado
Clasificación