Oracle Ask Tom Partitioning Learning Series: Tutorial de particionamiento para desarrolladores

Particionamiento de Oracle: una introducción paso a paso para desarrolladores es uno de los cursos para desarrolladores de bases de datos de Oracle .

inserte la descripción de la imagen aquí

Desarrollo con particionamiento de Oracle/Desarrollo con particionamiento de Oracle

La partición en la base de datos refleja la misma forma en que manejamos tareas grandes en el mundo real. Cuando una tarea se vuelve demasiado grande para abordarla de una sola vez, ya sea podar un árbol, hacer un viaje largo o lavar los platos, podemos dividir el dividir tareas en partes más pequeñas para hacerlas más manejables. ¡Puede ser tan simple como tratar de reubicar el catálogo de libros técnicos de Oracle! La partición
en una base de datos refleja la forma en que manejamos tareas grandes en el mundo real. Cuando una tarea se vuelve demasiado grande para abordarla a la vez, ya sea podar árboles, un viaje largo o lavar los platos, podemos dividir las tareas en partes más pequeñas para que sean más manejables. ¡Puede ser tan simple como tratar de reubicar el catálogo de libros técnicos de Oracle!

video

En este video, el autor Connor McDonald puede dividir el libro por editorial, por año, por versión de la base de datos Oracle...

La partición es el mismo pensamiento aplicado a los datos almacenados en la base de datos. A medida que aumenta la demanda de almacenar más y más datos en la base de datos, el rendimiento de las operaciones en esas tablas grandes puede verse afectado. Y al aplicar el principio de Pareto al almacenamiento de datos, por lo general, solo se trabaja activamente en un subconjunto del conjunto de datos completo para satisfacer las necesidades diarias de los usuarios de nuestro negocio. Al usar la opción Partición en la base de datos de Oracle, los datos se pueden segmentar en fragmentos más pequeños y manejables para facilitar las tareas de mantenimiento para los administradores de la base de datos, pero también dar lugar a un mejor rendimiento de la aplicación a través de una ejecución más eficiente de las consultas emitidas por los desarrolladores de aplicaciones.
La partición es la misma idea aplicada a los datos almacenados en una base de datos. A medida que aumenta la necesidad de almacenar más y más datos en la base de datos, el rendimiento de las operaciones en estas tablas grandes puede verse afectado. Al aplicar los principios de Pareto al almacenamiento de datos, generalmente solo se procesa activamente un subconjunto del conjunto de datos completo para satisfacer las necesidades diarias de nuestros usuarios comerciales. Con la opción de partición en la base de datos Oracle, los datos se pueden dividir en fragmentos más pequeños y manejables, lo que facilita las tareas de mantenimiento para los administradores de la base de datos y también brinda un mejor rendimiento al ejecutar las consultas emitidas por los desarrolladores de aplicaciones de manera más eficiente.

Principio de Pareto

El principio de Pareto establece que para muchos resultados, aproximadamente el 80 % de las consecuencias provienen del 20 % de las causas (los “pocos vitales”).[1] Otros nombres para este principio son la regla 80/20, la ley de los pocos vitales o el principio de escasez de factores.[2][3]

Opciones de partición/opciones de partición

Hay diferentes tipos de opciones de particionamiento disponibles para satisfacer los requisitos comerciales específicos. Puede haber un requisito para dividir los datos de VENTAS en cada año calendario. O puede haber información recopilada sobre DEPORTES populares que se separarán debido a la cantidad mínima de deportes cruzados. serie alguna vez se ejecutará. O una tabla de LLAMADAS de teléfonos celulares puede ser tan grande que debe distribuirse uniformemente en segmentos más pequeños para mantenerlos en un tamaño manejable. Todas estas opciones son posibles con la opción de particionamiento de Oracle. Hay diferentes Los tipos
de opciones de particionamiento están disponibles para satisfacer las necesidades comerciales específicas. Puede ser necesario desglosar los datos de VENTAS en cada año calendario. O podría recopilar información sobre deportes populares como NBA, NFL, MLB, NHL, es decir, baloncesto, fútbol, ​​béisbol y hockey, que estarían separados ya que las consultas entre deportes son menos comunes. Alternativamente, la tabla de llamadas de teléfonos celulares puede ser demasiado grande y debe distribuirse uniformemente en segmentos más pequeños para mantenerlos en un tamaño manejable. Todas estas opciones están disponibles a través de la opción Partición de Oracle.

También existen otras estrategias de partición para requisitos más esotéricos
, incluidas estrategias de partición entre tablas vinculadas por integridad referencial y formas multidimensionales de partición (particiones de particiones).Estrategias de

Primeros pasos con el particionamiento

Obtener un entorno/Obtener entorno

Simplemente vaya a un servicio gratuito de Oracle llamado livesql.oracle.com para comenzar. Este servicio le permite ejecutar SQL y crear objetos de base de datos sin necesidad de ningún otro software que no sea su navegador. También hay cientos de scripts de muestra y tutoriales sobre una amplia variedad de temas para ayudarlo a aprender sobre Oracle Database.

Oracle Partitioning también está disponible como una función totalmente compatible con todos los Oracle Database Cloud Service y Oracle Database Enterprise Edition en las instalaciones. Aquí hay una introducción rápida sobre LiveSQL del vicepresidente de Oracle, Mike Hichwa:

video

Características de Oracle LiveSQL:

  • Gratis: LiveSQL es completamente gratuito para cualquier uso. Registrarse es gratis, rápido y fácil.
  • Secuencias de comandos: el SQL que escribe en LiveSQL se puede etiquetar con metadatos, guardar, compartir con otros o con toda la comunidad de Oracle.
  • Tutoriales: LiveSQL incluye cientos de tutoriales escritos por expertos internos y externos de Oracle Corporation para ayudarlo a ser productivo rápidamente.
  • Últimas versiones: LiveSQL se ejecuta en las últimas versiones de Oracle Database, por lo que puede probar nuevas funciones de forma segura antes de actualizar sus propios sistemas.

El siguiente comando obtiene la versión actual de la base de datos:

select * from v$version;

inserte la descripción de la imagen aquí

Un primer vistazo a la sintaxis de particionamiento/Un estudio preliminar sobre la sintaxis de particionamiento

Quizás el ejemplo más común de creación de particiones en las bases de datos de Oracle es dividir una gran cantidad de datos en particiones en función de un atributo de tiempo . Las tablas más grandes de sus aplicaciones suelen ser un registro temporal de actividades comerciales críticas. Pueden ser transacciones de venta para un negocio minorista, llamadas de teléfonos móviles para un negocio de telecomunicaciones o depósitos y retiros para una institución bancaria. En todos estos casos, hay un par de elementos comunes, a saber, cada transacción (fila) en la tabla tiene una marca de tiempo de cuándo ocurrió la transacción y, por lo general, el volumen de tales transacciones es alto, lo que hace que la tabla sea grande en tamaño . un corto período de tiempo . La creación de particiones es una opción natural para este tipo de tablas, porque las consultas a menudo solo quieren leer detenidamente (随便翻阅,浏览)subconjuntos de datos basados ​​en el tiempo, por ejemplo, transacciones para el mes actual o la semana actual. Además, dividir la tabla grande en piezas más pequeñas y de un tamaño más manejable es útil para los administradores desde la perspectiva del mantenimiento . El tiempo es una medida continua (analógica) y, por lo tanto, una tabla grande se segmentaría en rangos basados ​​en el tiempo, por lo que el término utilizado para esta operación es partición basada en rangos. Este video lo guía a través de la creación de una tabla particionada por rango simple.

video

punto clave:

  • Partición por rango: La palabra clave para definir que una tabla se particiona es PARTICIÓN POR, que sigue la definición de columna de tabla normal. La cláusula BY RANGE especifica el tipo de esquema de partición que utilizará la tabla.
  • No contiene límite superior: la partición de rango no proporciona límites inferior y superior, solo límites superiores. El límite inferior se define implícitamente como el límite superior de la partición anterior. Una partición puede contener valores hasta, pero sin incluir, el valor especificado en la cláusula VALUES LESS THAN. ( Es un poco complicado, de hecho, es mayor o igual que el límite inferior y más pequeño que el límite superior )
  • Al menos 1 partición: Después de la cláusula PARTITION BY, siempre debe haber al menos una partición definida. ( Esto es cierto para el particionamiento por rango, mientras que otros tipos de particionamiento no son necesariamente aplicables, como el particionamiento automático, pero también hay algunos casos )
  • USER_TAB_PARTITIONS: el diccionario de datos realiza un seguimiento de todas las particiones definidas para la tabla. USER_TAB_PARTITIONS muestra una fila para cada partición definida de cada tabla particionada en el esquema.

Beneficios de rendimiento/ventaja de rendimiento

Incluso con un simple ejemplo de particionamiento de rango, tenemos suficientes herramientas a nuestra disposición (由我们支配,任我们处置) para examinar los posibles beneficios de rendimiento al particionar una tabla. Cuando las consultas SQL consumen demasiada E/S , a menudo la única solución que se considera es crear índices en la tabla. La premisa (前提) de la indexación es simple: ubique los datos más rápidamente y evite escanear datos innecesariamente . Particionar una tabla tiene el mismo enfoque a través de un concepto conocido como "poda" o "eliminación". Si una consulta en una tabla particionada contiene predicados redactados apropiadamente que incluyen la(s) columna(s) de partición, el optimizador puede generar un plan de ejecución que omitirá esas particiones.que, por definición, no podría contener datos relevantes para la consulta. El siguiente video muestra una demostración de esto, incluida una comparación del costo de la poda de partición versus una estrategia de indexación convencional (la estrategia de índice se refiere al tipo de partición, como partición de rango , partición de lista, partición hash, etc.) .

video

punto clave:

  • Eliminación de particiones: si el optimizador puede eliminar las consideraciones de partición, el rendimiento de las consultas puede mejorar significativamente. En un video posterior, verá cómo interpretar el resultado del plan de ejecución del optimizador para determinar si se debe realizar la eliminación de particiones para una consulta SQL determinada.
  • Generar datos de prueba: puede usar la técnica DUAL en el video para generar datos de prueba arbitrarios para cualquier tabla, particionada o no. De acuerdo con el video, mantenga la cantidad de filas generadas por una sola consulta DUAL CONNECT BY dentro de decenas de miles y use uniones cartesianas si necesita escalar. Vea la publicación de blog de Tanel Poder sobre cómo estas consultas afectan la memoria PGA y por qué no debe ir a los extremos.
  • Reducción de índice: en algunos casos, la partición de una tabla permite fusionar o eliminar índices existentes, lo que puede reducir el tamaño general de la base de datos y mejorar el rendimiento de inserción, actualización y eliminación.
create table SALES
(
  tstamp    timestamp(6) not null,
  sales_id  number(10) not null,
  amount    number(12, 2) not null 
);

-- timestamp之后的6表示秒的小数点的位数,6是默认值。

insert into sales
select
    timestamp '2010-01-01 00:00:00' +
    numtodsinterval(rownum*5, 'SECOND'),
    rownum,
    dbms_random.value(1,20)
from
    (select 1 from dual connect by level <= 10000),
    (select 1 from dual connect by level <= 10000)
where rownum <= 6000000;

commit;

-- 6000000 * 5 可换算为347.22天,所以时间戳都在2010年内,最大时间戳在12月中旬

set autotrace on
select max(amount)
from sales
where tstamp >= timestamp '2010-06-01 00:00:00'
and tstamp <= timestamp '2010-08-01 00:00:00';

El resultado es el siguiente, tenga en cuenta que la constante obtenida en este momento es 19326:

MAX(AMOUNT)
-----------
         20


Execution Plan
----------------------------------------------------------
Plan hash value: 1047182207

----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |    26 |  5265   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE    |       |     1 |    26 |            |          |
|*  2 |   TABLE ACCESS FULL| SALES |  1062K|    26M|  5265   (1)| 00:00:01 |
----------------------------------------------------------------------------

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

   2 - filter("TSTAMP">=TIMESTAMP' 2010-06-01 00:00:00.000000000' AND
              "TSTAMP"<=TIMESTAMP' 2010-08-01 00:00:00.000000000')

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


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

Crear un índice:

create index sales_ix on sales(tstamp);

Consulta de nuevo, su plan de ejecución y estadísticas son los siguientes. Encontré que los índices no ayudan, todavía use el escaneo completo de la tabla:

MAX(AMOUNT)
-----------
         20


Execution Plan
----------------------------------------------------------
Plan hash value: 1047182207

----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |    26 |  5265   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE    |       |     1 |    26 |            |          |
|*  2 |   TABLE ACCESS FULL| SALES |  1062K|    26M|  5265   (1)| 00:00:01 |
----------------------------------------------------------------------------

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

   2 - filter("TSTAMP">=TIMESTAMP' 2010-06-01 00:00:00.000000000' AND
              "TSTAMP"<=TIMESTAMP' 2010-08-01 00:00:00.000000000')

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


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

Modificar a la tabla de particiones:

alter table sales
modify partition by range (tstamp)
(
    partition p00 values less than (timestamp '2010-01-01 00:00:00'),
    partition p01 values less than (timestamp '2010-02-01 00:00:00'),
    partition p02 values less than (timestamp '2010-03-01 00:00:00'),
    partition p03 values less than (timestamp '2010-04-01 00:00:00'),
    partition p04 values less than (timestamp '2010-05-01 00:00:00'),
    partition p05 values less than (timestamp '2010-06-01 00:00:00'),
    partition p06 values less than (timestamp '2010-07-01 00:00:00'),
    partition p07 values less than (timestamp '2010-08-01 00:00:00'),
    partition p08 values less than (timestamp '2010-09-01 00:00:00'),
    partition p09 values less than (timestamp '2010-10-01 00:00:00'),
    partition p10 values less than (timestamp '2010-11-01 00:00:00'),
    partition p11 values less than (timestamp '2010-12-01 00:00:00'),
    partition p12 values less than (timestamp '2011-01-01 00:00:00')
);

Vuelva a ejecutar la consulta y la eliminación de la partición surtirá efecto. consistente se reduce a 5075:

---------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |       |     1 |    15 |  1415   (1)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE           |       |     1 |    15 |            |          |       |       |
|   2 |   PARTITION RANGE ITERATOR|       |  1054K|    15M|  1415   (1)| 00:00:01 |     7 |     9 |
|*  3 |    TABLE ACCESS FULL      | SALES |  1054K|    15M|  1415   (1)| 00:00:01 |     7 |     9 |
---------------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"<=TIMESTAMP' 2010-08-01 00:00:00.000000000')


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

Finalmente, limpia la mesa:

drop table sales purge;

Si cree que la sintaxis de partición de rango anterior es complicada, también puede usar la partición de intervalo. La partición por intervalos es una extensión de la partición por rangos:

alter table sales
modify partition by range (tstamp) interval (numtoyminterval(1, 'MONTH'))
(partition p00 values less than (timestamp '2010-01-01 00:00:00'));

Ver información de la partición:

-- 对于间隔分区,PARTITION_COUNT总是1048575
col TABLE_NAME for a20
col name for a20
col column_name for a20
set lines 140
col PARTITION_NAME for a20
col HIGH_VALUE for a40
set pages 9999

select TABLE_NAME, PARTITIONING_TYPE, PARTITION_COUNT, STATUS from USER_PART_TABLES;

TABLE_NAME           PARTITION PARTITION_COUNT STATUS
-------------------- --------- --------------- --------
SALES                RANGE             1048575 VALID

exec dbms_stats.gather_table_stats(null, 'SALES');

-- 必须搜集统计信息,NUM_ROWS才会有显示
select PARTITION_NAME, HIGH_VALUE, NUM_ROWS from USER_TAB_PARTITIONS where TABLE_NAME='SALES';

PARTITION_NAME       HIGH_VALUE                                 NUM_ROWS
-------------------- ---------------------------------------- ----------
P00                  TIMESTAMP' 2010-01-01 00:00:00'                   0
SYS_P27320           TIMESTAMP' 2010-02-01 00:00:00'              535679
SYS_P27321           TIMESTAMP' 2010-03-01 00:00:00'              483840
SYS_P27322           TIMESTAMP' 2010-04-01 00:00:00'              535680
SYS_P27323           TIMESTAMP' 2010-05-01 00:00:00'              518400
SYS_P27324           TIMESTAMP' 2010-06-01 00:00:00'              535680
SYS_P27325           TIMESTAMP' 2010-07-01 00:00:00'              518400
SYS_P27326           TIMESTAMP' 2010-08-01 00:00:00'              535680
SYS_P27327           TIMESTAMP' 2010-09-01 00:00:00'              535680
SYS_P27328           TIMESTAMP' 2010-10-01 00:00:00'              518400
SYS_P27329           TIMESTAMP' 2010-11-01 00:00:00'              535680
SYS_P27330           TIMESTAMP' 2010-12-01 00:00:00'              518400
SYS_P27331           TIMESTAMP' 2011-01-01 00:00:00'              228481

13 rows selected.

select * from USER_PART_KEY_COLUMNS;

NAME                 OBJEC COLUMN_NAME          COLUMN_POSITION COLLATED_COLUMN_ID
-------------------- ----- -------------------- --------------- ------------------
SALES                TABLE TSTAMP                             1

Particionamiento de rango de varias columnas/particionamiento de rango de varias columnas

Se pueden especificar varias columnas como clave de partición, el orden de las columnas es importante.

video

create table SALES_DATA
(
    yyyy number(4) not null,
    mm number(2) not null,
    sales_id varchar2(10) not null,
    amount number(10, 2)
)
partition by range (yyyy, mm)
(
    partition p2010_q1 values less than (2010, 04),
    partition p2010_q2 values less than (2010, 07),
    partition p2010_q3 values less than (2010, 10),
    partition p2010_q4 values less than (2011, 01),
    partition p2011_q1 values less than (2011, 04),
    partition p2011_q2 values less than (2011, 07),
    partition p2011_q3 values less than (2011, 10),
    partition p2011_q4 values less than (2012, 01)
);

insert into sales_data values(2010, 03, 'Shoes', 27.10);
insert into sales_data values(2010, 02, 'Belt', 17.99);
insert into sales_data values(2010, 04, 'Hat', 42.40);
insert into sales_data values(2010, 09, 'Coffee', 3.50);
insert into sales_data values(2010, 10, 'Biscuits', 2.60);

exec dbms_stats.gather_table_stats('', 'SALES_DATA');

select partition_name, num_rows
from user_tab_partitions
where table_name = 'SALES_DATA';

La salida es:

PARTITION_NAME	NUM_ROWS
P2010_Q1	2
P2010_Q2	1
P2010_Q3	1
P2010_Q4	1
P2011_Q1	0
P2011_Q2	0
P2011_Q3	0
P2011_Q4	0

8 rows selected.

Veamos otro ejemplo:

create table mobile_phone
(
    start_day date not null,
    end_day date not null,
    account_id varchar2(10) not null,
    calls number(10)
)
partition by range(start_day, end_day)
(
    partition p01 values less than (date '2010-02-01', date '2010-03-01'), 
    partition p02 values less than (date '2010-03-01', date '2010-04-01'), 
    partition p03 values less than (date '2010-04-01', date '2010-05-01') 
);

insert into mobile_phone values('07-FEB-2010', '12-FEB-2010', 'Acct#1', 100);
insert into mobile_phone values('12-FEB-2010', '13-APR-2010', 'Acct#1', 175);
exec dbms_stats.gather_table_stats('', 'MOBILE_PHONE');

select partition_name, num_rows
from user_tab_partitions
where table_name = 'MOBILE_PHONE';

La salida es:

PARTITION_NAME	NUM_ROWS
P01	0
P02	2
P03	0

3 rows selected.

Todos los datos van a la segunda partición, que obviamente no es lo que queremos.

punto clave:

  • El desempate no es multidimensional
    La segunda y la tercera columna en la definición de partición se usan solo como valores de "desempate". Al insertar filas en una tabla particionada por rango de varias columnas, la primera columna de la clave de partición se usa para determinar la partición donde se almacena la fila. Si varias particiones tienen el mismo valor para la primera columna, se usa la segunda columna de partición (es decir, tiempo extra o desempate para determinar qué partición se almacena), y así sucesivamente . Las columnas múltiples en la clave de partición no son particiones de estructura 'matriz' o 'n-dimensional'.
  • Almacenamiento de fechas como números
    Uno de los ejemplos del video utiliza el tipo de datos NÚMERO para almacenar información . Como dice el video, esta es generalmente una mala idea de diseño . Consulte Fechas de almacenamiento de Richard Foote para obtener más ejemplos de por qué podría querer reconsiderar este enfoque en su base de datos.
  • Vistas de diccionario
    De la misma manera que USER_TABLES contiene varias columnas para reflejar las estadísticas actuales del optimizador, USER_TAB_PARTITIONS también contiene esta información a nivel de partición. Por lo tanto, puede usar NUM_ROWS, BLOCKS, etc. para obtener información sobre los volúmenes por partición, solo con la precisión de la hora/granularidad en la que se recopilan las estadísticas mediante DBMS_STATS .

La diferencia entre los dos ejemplos anteriores es que la clave de partición del primer ejemplo es el punto de tiempo, y el segundo ejemplo es el período de tiempo, y los períodos de tiempo se superponen, por lo que hay un problema.

Partición hash/partición hash

A veces, la partición no es una segmentación lógica de una tabla basada en sus atributos, como lo hace la partición por rango. Cuando cualquier tabla de la base de datos crece, se vuelve más difícil de administrar para los administradores porque el mantenimiento de dichas tablas a menudo resulta en un tiempo de inactividad más prolongado para las aplicaciones comerciales. La partición hash le permite dividir una tabla en partes del mismo tamaño según una función hash aplicada a una o más columnas de la tabla.

Crear una tabla con particiones hash es fácil, pero la cantidad de particiones hash especificadas es crítica.

video

El particionamiento hash es muy importante para los administradores de bases de datos y el particionamiento de índice hash es muy importante para los desarrolladores.

punto clave:

  • Potencia de 2
    Para mantener el tamaño de las particiones cercano, el número de particiones debe ser una potencia de 2.
  • El algoritmo hash ORA_HASH
    no está documentado, pero se ha observado que la función ORA_HASH devuelve resultados coherentes con la fragmentación de datos que se produce con la partición hash.
  • Dividir
    particiones se pueden dividir usando el comando ALTER TABLE SPLIT PARTITION, una tarea que generalmente realiza el administrador de la base de datos. Dividir una partición es una actividad que consume muchos recursos porque se pueden mover los datos de toda la partición.
create table T
(
    x number(10)
) partition by hash(x)
partitions 8;

select partition_name
from user_tab_partitions
where table_name = 'T';

La salida es:

PARTITION_NAME
SYS_P492050
SYS_P492051
SYS_P492052
SYS_P492053
SYS_P492054
SYS_P492055
SYS_P492056
SYS_P492057

8 rows selected.

Inserte 100,000 piezas de datos:

insert into T
select level from dual 
connect by level <= 100000;

Ver ID de fila:

select rowid from T where rownum = 1;

El resultado es el siguiente, rowid en realidad contiene el ID del objeto:

ROWID
AJUIk+ADDAAABSTAAA

Por lo tanto, el número real de filas en cada partición se puede obtener a través del siguiente SQL:

select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

El resultado es el siguiente, se puede ver que los datos están cerca de la distribución promedio:

PTN_OBJ	COUNT(*)
156272962	12342
156272963	12381
156272965	12382
156272961	12508
156272959	12575
156272960	12581
156272958	12603
156272964	12628

8 rows selected.

Se puede verificar mediante la función ORA_HASH, donde 7 significa de 0 a 8:

select ora_hash(x, 7), count(*)
from t
group by ora_hash(x, 7)
order by 2;

La salida es la siguiente:

ORA_HASH(X,7)	COUNT(*)
4	12342
5	12381
7	12382
3	12508
1	12575
2	12581
0	12603
6	12628

8 rows selected.

El número de particiones hash debe ser una potencia de 2, como 2, 4, 8, 16..., de lo contrario, la distribución puede ser desigual.

drop table t purge;

create table T
(
    x number(10)
) partition by hash(x)
partitions 5;

insert into T
select level from dual 
connect by level <= 100000;

select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

Se puede observar que la distribución no es uniforme:

   PTN_OBJ   COUNT(*)
---------- ----------
    107682      12342
    107678      12603
    107681      24890
    107679      24956
    107680      25209

5 rows selected.

¿Cómo cambiar el tamaño de 5 particiones a 8 particiones? Veamos primero el enfoque tradicional, la mayoría de las filas (casi el 88% en este caso) deben moverse:

select count(*) from T
where ora_hash(x, 4) != ora_hash(x, 7);

COUNT(*)
87564

select count(*) from T
where ora_hash(x, 4) != ora_hash(x, 5);

COUNT(*)
83224

Oracle adopta un algoritmo más inteligente, primero mire el cambio de 5 particiones a 6 particiones:

alter table T add partition;

select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

PTN_OBJ	COUNT(*)
156275768	12342
156276447	12381
156276446	12575
156275764	12603
156275767	24890
156275766	25209

6 rows selected.

Puede ver el efecto de agregar una partición hash, en comparación con dividir una partición hash. En este ejemplo, 24956 se divide en 12381 y 12575.

Repita el proceso anterior, puede ver que la distribución de datos tiende a ser equilibrada:

alter table T add partition;
select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

PTN_OBJ	COUNT(*)
156275768	12342
156276447	12381
156276446	12575
156276791	12581
156275764	12603
156276792	12628
156275767	24890

7 rows selected.

alter table T add partition;
select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

PTN_OBJ	COUNT(*)
156275768	12342
156276447	12381
156276843	12382
156276842	12508
156276446	12575
156276791	12581
156275764	12603
156276792	12628

8 rows selected.

Las particiones hash solo se pueden agregar una por una. El proceso de agregar particiones es equivalente a dividir, y también es el proceso de refrito de datos.

La reducción de las particiones hash se logra a través de la operación coalesce.

select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

   PTN_OBJ   COUNT(*)
---------- ----------
    107682      12342
    107684      12381
    107688      12382
    107687      12508
    107683      12575
    107685      12581
    107678      12603
    107686      12628

8 rows selected.

alter table T COALESCE PARTITION;

-- 数据库自动选择了107687和107687进行coalesce
select dbms_rowid.rowid_object(rowid) ptn_obj, count(*)
from T
group by dbms_rowid.rowid_object(rowid)
order by 2;

   PTN_OBJ   COUNT(*)
---------- ----------
    107682      12342
    107684      12381
    107683      12575
    107685      12581
    107678      12603
    107686      12628
    107689      24890

7 rows selected.

Listar partición/listar partición

La partición de rango, como sugiere el nombre, se trata de dividir datos que son de naturaleza analógica (模拟) , es decir, un rango continuo de valores (连续的值) . Esta es la razón por la que las fechas son un candidato natural para un esquema de partición basado en rangos.
Pero a veces, la columna en la que puede querer particionar contiene un conjunto de valores discretos (离散) , que es cuando la partición LIST es la mejor solución. La creación de una tabla particionada LIST requiere la nominación de valores discretos para cada partición, o confiar en la nueva cláusula AUTOMATIC en 12c Release 2.

video

El particionamiento de lista es adecuado para situaciones en las que hay pocos valores distintos, el particionamiento de rango es engorroso y el particionamiento hash está distribuido de manera desigual.

punto clave:

  • Uno o más valores
    Una sola partición puede contener uno o más valores discretos.
  • Las particiones "catch-all" predeterminadas
    se pueden definir utilizando la cláusula DEFAULT en la instrucción VALUES. Los valores nulos también entran en esta partición.

Ejemplo:

create table sports
(
    sport varchar2(3)
);

insert into sports values('NHL');
insert into sports values('MLB');
insert into sports values('NBA');
insert into sports values('NFL');

alter table sports modify partition by hash(sport) partitions 4;

select sport, ora_hash(sport, 3) hash from sports;

El resultado es el siguiente, se puede ver que la distribución no es uniforme:

SPORT	HASH
NHL		0
NFL		0
MLB		1
NBA		1

4 rows selected.

Cambie directamente a la lista de particiones:

alter table sports modify partition by list(sport)
(
    partition NHL values ('NHL'),
    partition MLB values ('MLB'),
    partition NBA values ('NBA'),
    partition NFL values ('NFL')
);

alter table sports add partition OTHERS values ('LAC', 'TEN');

insert into sports values('XYZ');

El error se informa como:

ORA-14400: inserted partition key does not map to any partition ORA-06512: at "SYS.DBMS_SQL", line 1721

Simplemente agregue una partición predeterminada:

alter table sports add partition ALL_OTHERS values (DEFAULT);

insert into sports values('XYZ');

Consultas que especifican particiones:

select * from sports partition(ALL_OTHERS);

Particiones de Particiones/Particiones de Particiones

Esa es una partición compuesta. Hasta dos niveles, las combinaciones permitidas son [Interval | Range | List | Hash]-[Range | List | Hash].

Incluso una vez dividida, una tabla puede seguir siendo extremadamente grande . O el esquema de partición puede dar como resultado particiones que no tienen el mismo tamaño en el disco. Por ejemplo, archivar datos antiguos puede significar que las particiones históricas son mucho más pequeñas que las particiones actuales (这里假设业务是不断增长的,因此今年的数据多于去年). Las particiones de tamaño equivalente pueden ser beneficiosas en particular cuando se trata de realizar operaciones en paralelo por partición .
Las particiones se pueden segmentar aún más en subparticiones. Cada partición puede tener su propio esquema de subparticionamiento o todas las particiones pueden compartir un esquema común.

punto clave:

  • Flexibilidad
    El esquema de partición secundaria puede ser diferente del esquema de partición principal.
  • Definición por partición
    Cada partición puede tener su propia definición de subpartición. Las particiones también son válidas sin subparticiones. Es decir, para una misma tabla, se permite que algunas particiones no tengan subparticiones y algunas particiones tengan subparticiones. O diferentes particiones pueden tener diferentes estrategias de subpartición
  • La columna SUBPARTITION_POSITION en el orden
    USER_TAB_SUBPARTITIONS hace referencia a la posición relativa de la subpartición dentro de la partición principal.
  • Plantillas
    Las plantillas de subparticiones facilitan la implementación de un esquema común para todas las particiones y reducen el tamaño de los scripts DDL.
  • La existencia de subparticiones lógicas/físicas
    determina si una partición es un segmento físico en el disco o simplemente una colección lógica de segmentos de subparticiones físicas. El primero es el caso cuando no hay una subpartición y el segundo es el caso cuando hay una subpartición, porque los datos se almacenan realmente en la subpartición. La partición mencionada aquí se refiere a que la partición principal es más precisa

Particiones de intervalo/partición de intervalo

Para los datos entrantes, ya debe existir una partición a la que se asignaría la clave de partición entrante. Si no existe una partición, las transacciones fallarán con ORA-14400. En versiones anteriores de Oracle Database, esto significaba que los administradores tenían que tener mucho cuidado para garantizar que se definieran las particiones para los datos futuros o correr el riesgo de interrupciones de la aplicación. La partición de intervalos (introducida en Oracle Database 11g) elimina este riesgo al crear automáticamente nuevas particiones en una tabla particionada según sea necesario cuando llegan nuevos datos .

Las tablas particionadas por intervalos se diferencian de la partición por rangos en que se permiten "lagunas" lógicas en la cobertura de la partición.

El particionamiento por intervalos es un tipo de particionamiento por rangos.

video

punto clave:

  • Asignación automática de nombres
    Debido a que las particiones se crean dinámicamente, se asigna un nombre generado por el sistema a la nueva partición. Se les puede cambiar el nombre para cumplir con los estándares comerciales existentes.
  • El rango de la clave de partición que permite espacios
    es fijo, es decir, el tamaño del intervalo. A diferencia de la partición por rangos, los límites superior e inferior están definidos por intervalos en lugar de particiones adyacentes. En otras palabras, las particiones de rango son continuas y finitas; las particiones de intervalo son infinitas, con agujeros permitidos en el medio
  • Sintaxis FOR
    Si no conoce el nombre de la partición de intervalo, puede usar la sintaxis FOR (clave-valor)

Para el particionamiento por rango, dado que las particiones establecidas en DDL son limitadas, las particiones deben preestablecerse antes de que lleguen nuevos datos. Por ejemplo, en la víspera de Año Nuevo, es necesario crear una partición para los datos de Año Nuevo; de lo contrario, se informará un error al insertar, lo que es una carga para el DBA. Por lo tanto, se necesita una forma automática, que es la partición por intervalos.

create table sales
(
    tstamp  date    not null,
    empno   number(10)  not null,
    ename   varchar(2)  not null,
    deptno  varchar(2)  not null
)
partition by range (tstamp)
interval (numtoyminterval(1, 'YEAR'))
(
    partition p00 values less than (DATE '2010-01-01')
);

select partition_name, high_value from user_tab_partitions
where table_name = 'SALES';

PARTITION_NAME	HIGH_VALUE
--------------  ----------
P00				TO_DATE(' 2010-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')

insert into sales values (to_date(''12-DEC-2011), 100, 'ME', 'EA');

select partition_name, high_value from user_tab_partitions
where table_name = 'SALES';

PARTITION_NAME	HIGH_VALUE
--------------  ----------
P00				TO_DATE(' 2010-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
SYS_P492130		TO_DATE(' 2012-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')

El sistema crea automáticamente una partición cuyo nombre comienza con SYS_P y se puede cambiar de nombre más adelante.

PARTICIÓN PARA sintaxis, referenciando particiones por datos:

alter table SALES move partition for (DATE '2011-02-01');

select * from SALES partition for (DATE '2011-02-01');

PARTITION FOR es una sintaxis genérica, no limitada a particiones de intervalo. También disponible para partición hash:

select count(*) from T partition for (100);

select * from T partition(p1, p2, ...);

Conversión a particiones de intervalo/conversión a particiones de intervalo

Las tablas particionadas por rango existentes se pueden convertir en tablas de partición por intervalos con un simple comando de conversión, sin necesidad de tiempo de inactividad ni migración de datos . Las particiones existentes en la tabla particionada por rango permanecieron definidas como "particiones de rango", mientras que las nuevas particiones creadas dinámicamente son "particiones de intervalo". Por lo tanto, una tabla que no se creó inicialmente como una tabla particionada por intervalos es un híbrido entre las dos y contiene ambos tipos de particiones. Debido a que los intervalos se definen como un desplazamiento de un límite de partición de rango fijo inicial, no puede descartar todas las particiones de rango en una tabla particionada por intervalo. Al menos uno siempre debe permanecer. En Oracle Database 12c versión 2, la base de datos convertirá automáticamente la partición de intervalo más antigua en una partición de rango si no se puede encontrar un límite de partición de rango inicial.

video

punto clave:

  • Conversión Una
    tabla particionada por rango se puede convertir en una tabla particionada por intervalo simplemente especificando el intervalo deseado con ALTER TABLE… SET INTERVAL.
  • Particiones mixtas En las tablas de traducción, la columna INTERVAL
    en USER_TAB_PARTITIONS indica si cada partición es una partición de rango o una partición de intervalo.
  • Límites mínimos de rango Al menos una partición de rango
    debe permanecer en la tabla . Antes de Oracle Database 12c versión 2, volver a ejecutar el comando SET INTERVAL marcaría una partición de intervalo existente como una partición de rango.

Ejemplo:

create table SALES
(
  tstamp    timestamp(6) not null,
  sales_id  number(10) not null,
  amount    number(12, 2) not null 
);

alter table sales
modify partition by range (tstamp)
(
    partition p00 values less than (timestamp '2010-01-01 00:00:00'),
    partition p01 values less than (timestamp '2010-02-01 00:00:00')
);

insert into sales values(timestamp '2009-12-02 00:00:00', 100, 100);
insert into sales values(timestamp '2010-01-02 00:00:00', 200, 200);

alter table sales set interval(numtoyminterval(1, 'MONTH'));

insert into sales values(timestamp '2010-02-02 00:00:00', 300, 300);

col pname for a20
col high_value for a40

select
    partition_name pname,
    partition_position pos, 
    high_value,
    interval
from user_tab_partitions
where table_name = 'SALES';

La salida es:

PNAME                       POS HIGH_VALUE                               INT
-------------------- ---------- ---------------------------------------- ---
P00                           1 TIMESTAMP' 2010-01-01 00:00:00'          NO
P01                           2 TIMESTAMP' 2010-02-01 00:00:00'          NO
SYS_P27344                    3 TIMESTAMP' 2010-03-01 00:00:00'          YES

3 rows selected.

Si el intervalo es NO, significa partición de rango y SÍ significa partición de intervalo.

El límite superior de la partición de rango sirve como marcador frente al cual las particiones de intervalo subsiguientes son relativas. Por lo tanto, no puede descartar todas las particiones de rango. Sin embargo, esta limitación se rompió y aparentemente se mejoró el software. Después de 12.2, si elimina todas las particiones de rango, las particiones de intervalo restantes se convierten automáticamente en particiones de rango:

alter table sales drop partition p00;
alter table sales drop partition p01;
col pname for a20
col high_value for a40

select
    partition_name pname,
    partition_position pos, 
    high_value,
    interval
from user_tab_partitions
where table_name = 'SALES';

PNAME                       POS HIGH_VALUE                               INT
-------------------- ---------- ---------------------------------------- ---
SYS_P27344                    1 TIMESTAMP' 2010-03-01 00:00:00'          NO

Particiones de intervalo para listas/partición de intervalo de la lista

El particionamiento de INTERVALO es excelente para el particionamiento de rango para evitar la necesidad de un mantenimiento regular por parte de un DBA para garantizar que todos los valores se puedan almacenar. La misma función está disponible para tablas particionadas LIST desde Oracle Database 12c versión 2 en adelante. De esta forma, puede asegurarse de que los valores de datos legales que no se han definido como claves de partición puedan crear automáticamente particiones sobre la marcha .

video

punto clave:

  • Una partición estática
    Todavía debe definir al menos una partición estática
  • Permitir valores nulos
    Todavía puede definir una partición para contener valores nulos si lo desea
  • Usada con prudencia,
    la partición automática funciona mejor con un conjunto limitado de valores distintos . No caiga en la trampa de millones (es decir, demasiados valores distintos) de particiones

La partición automática de listas es adecuada para valores distintos limitados, como provincias, municipios, estados. Pero no demasiado poco, como el género.

create table people(
    id int,
    name varchar2(100),
    gender varchar2(2)
);


alter table people
modify partition by list(gender) automatic
(
    partition male values ('M'),
    partition female values ('F')
);

insert into people values(100, 'VOID', 'U');

select partition_name from user_tab_partitions
where table_name = 'PEOPLE';

La salida es:

PARTITION_NAME
--------------
FEMALE
MALE
SYS_P492154

3 rows selected.

Particionamiento de referencia/partición de referencia

Las tablas en una base de datos relacional no funcionan de forma aislada y se vinculan a través de la integridad referencial declarativa. Por esta razón, es posible que una tabla grande no contenga la columna por la que deseamos dividirla. Por ejemplo, una tabla VENTAS puede dividirse por FECHA_VENTAS, pero una tabla secundaria (digamos) ARTÍCULOS_VENTAS solo puede tener un reverso externo a la tabla VENTAS principal y, por lo tanto, no tiene una columna FECHA_VENTAS en la que particionar. La partición de referencia se puede utilizar para manejar estos diseños más complejos.
SALES_ITEMS中的数据比SALES会更多,因为是多对一的关系

video

punto clave:

  • Las claves externas declarativas
    en tablas secundarias definen claves de partición
  • Truncamiento en cascada
    La opción CASCADE en el comando TRUNCATE se puede usar para omitir las comprobaciones habituales de clave externa si es necesario truncar el padre y sus hijos.
  • Enlace fuerte
    Debido a la naturaleza estrechamente acoplada de los datos, existen algunas restricciones cuando las particiones hacen referencia a las tablas.

Ejemplo:

create table PARENT
(
    dte date    not null,
    pk  number(10)  not null,
    pad char(10)
)
partition by range(dte)
(
    partition p1 values less than (to_date('01-JAN-2010')),
    partition p2 values less than (to_date('01-FEB-2010')),
    partition p3 values less than (to_date('01-MAR-2010')),
    partition p4 values less than (to_date('01-APR-2010')),
    partition p5 values less than (to_date('01-MAY-2010')),
    partition p6 values less than (to_date('01-JUN-2010')),
    partition p7 values less than (to_date('01-JUL-2010')),
    partition p8 values less than (to_date('01-AUG-2010')),
    partition p9 values less than (to_date('01-SEP-2010'))
);

alter table PARENT add primary key(pk);

create table CHILD
(
    p number(10) not null,
    c number(10) not null,
    constraint CHILD_FK foreign key (p) references PARENT(pk)
    on delete cascade
)
partition by reference (CHILD_FK);

insert into PARENT select to_date('01-JAN-2010')+rownum, rownum, rownum
from dual connect by level <= 100;

insert into CHILD select rownum, rownum
from dual connect by level <= 100;

exec dbms_stats.gather_table_stats(null, 'CHILD');
select partition_name, num_rows from user_tab_partitions
where table_name = 'CHILD';

El resultado es el siguiente, y puede ver que la subtabla también está dividida:

PARTITION_NAME	NUM_ROWS
P1	0
P2	30
P3	28
P4	31
P5	11
P6	0
P7	0
P8	0
P9	0
Download CSV
9 rows selected.

Índices en tablas particionadas/índice en tablas particionadas

Un índice normalmente apunta a una fila en un segmento físico (una tabla). Pero una tabla particionada consta de varios segmentos físicos, por lo que las estructuras de índice y los datos contenidos en el índice deben ser ligeramente diferentes para las tablas particionadas. Si el índice abarca todas las particiones, ¿qué sucede cuando realizamos el mantenimiento en una sola partición, como eliminarla? ¿Qué sucede con las entradas de índice que hacen referencia a la partición ahora eliminada?

video

punto clave:

  • Un índice global
    abarca todas las particiones, lo que significa que cada entrada de índice tiene un ROWID más grande para referirse a la partición (objeto de base de datos) y la posición de la fila dentro de esa partición
  • Mantenimiento
    Alterar las particiones de la tabla (truncar, dividir, fusionar, descartar, etc.) puede marcar los índices en un estado NO UTILIZABLE. De forma predeterminada, no se generan errores cuando esto sucede, su plan de consulta simplemente ya no usa el índice . La cláusula ACTUALIZAR ÍNDICES puede ser beneficiosa aquí
  • Asíncrono
    En 12c y posteriores, el mantenimiento de partición DROP y TRUNCATE tiene menos impacto en los índices globales porque la limpieza del índice se realiza en segundo plano . Esta función es útil para la eliminación masiva de datos

Si una tabla de particiones usa un índice tradicional, es decir, un índice global, cuando se elimina la partición, el estado del índice pasa a ser INUTILIZABLE.
Puede actualizar el índice mientras suelta la partición:

alter table <table_name> drop partition <partition_name> update index;

Omitir índices no disponibles se establece en VERDADERO de forma predeterminada:

SQL> show parameter skip

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
skip_unusable_indexes                boolean     TRUE

El índice global no solo necesita registrar el ID de fila, sino que también debe registrar la ID del segmento donde se encuentra la partición. Eso es rowid extendido.

ROWID es una columna virtual y ocupa 10 bytes:

SQL> select rowid, vsize(rowid), dump(rowid) from part where rownum <2;

ROWID              VSIZE(ROWID)
------------------ ------------
DUMP(ROWID)
--------------------------------------------------------------------------------
AAASNxAAMAAAAGDAAA           10
Typ=69 Len=10: 0,1,35,113,3,0,1,131,0,0

Índices particionados localmente/índice de partición local

Un índice se puede equiparticionar con su tabla subyacente. Dicho índice se conoce como índice local (可以与global) . Los índices locales pueden tener beneficios significativos cuando se trata de la gestión del ciclo de vida de la información (ILM). Debido a que las operaciones de particiones de tablas como TRUNCATE y DROP están aisladas en una sola partición de índice, el resto del índice permanece disponible y nunca necesita un mantenimiento innecesario . Los índices locales también se prestan para facilitar el intercambio de particiones , lo cual es una técnica útil para archivar datos antiguos o introducir datos nuevos en una tabla particionada sin tiempo de inactividad .

Un índice de partición global (o índice global) es un segmento de índice que asigna varios segmentos de partición de tabla. El índice de partición local (o índice local) es una correspondencia uno a uno entre el segmento de índice y el segmento de partición de tabla. Un índice local es equivalente a particionar el índice.

Si se elimina una partición en este momento, el índice de partición local correspondiente también se eliminará y otras particiones e índices no se verán afectados.

create table sales
(
    tstamp  date    not null,
    empno   number(10)  not null,
    amount  number(10)  not null
)
partition by range (tstamp)
interval (numtoyminterval(1, 'YEAR'))
(
    partition p2009 values less than (DATE '2010-01-01'),
    partition p2010 values less than (DATE '2011-01-01'),
    partition p2011 values less than (DATE '2012-01-01'),
    partition p2012 values less than (DATE '2013-01-01')
);

create index sales_ix on sales(tstamp) local;

insert into sales values(DATE '2010-01-01', 100, 100);
insert into sales values(DATE '2011-01-01', 200, 200);
insert into sales values(DATE '2012-01-01', 300, 300);

alter table sales drop partition p2009;

select partition_name, status from user_ind_partitions
where index_name = 'SALES_IX';

El resultado es el siguiente, puede ver que después de eliminar la partición, los índices de otras particiones aún están disponibles:

PARTITION_NAME	STATUS
P2010			USABLE
P2011			USABLE
P2012			USABLE

3 rows selected.

Con índices locales, ILM se vuelve más conveniente:


alter table sales move partition p2010 compress;

alter table sales move partition p2010 tablespace ts_archive;

-- 这样RMAN就不会反复备份
alter tablespace ts_lowperformance read only;

alter table sales modify partition p2010 unusable local indexes;

alter table sales modify partition p2010 indexing off;

Las particiones más antiguas tienen prohibido la indexación local porque la mayoría de estos datos antiguos se utilizan para el análisis. Por supuesto, también es posible prohibir que se indexe la partición actual, lo que es aplicable en el caso de usar Database In-Memory.

No se introducirá la partición de intercambio.

Índices locales versus índices globales/índices locales y globales

Los dos videos anteriores analizan varias limitaciones aparentes de los índices globales y muchas ventajas de los índices locales. Esto parece sugerir que los índices locales siempre deben usarse con preferencia a los índices globales. Este no es el caso, y cada técnica de indexación debe emplearse para adaptarse mejor a los requisitos de la aplicación que tiene. Un índice local puede ser la elección absolutamente incorrecta para determinados tipos de consultas. Sin embargo, a veces es posible que deba elegir algunos compromisos de diseño si desea obtener lo mejor de ambos mundos: independencia de partición pero aún con un diseño de base de datos declarativo sólido.

Esta sección describe cómo elegir entre índices locales y globales. Ambos tienen sus propios escenarios aplicables.

video

punto clave:

  • Multiplicador de lectura (Read Multiplier)
    Tenga en cuenta que las búsquedas de índice no degradan el rendimiento debido a una elección incorrecta de la estrategia de partición del índice local.
  • Compensaciones de diseño
    A veces puede ser beneficioso incluir la clave de partición en el diseño físico de la clave principal, incluso si el diseño de la base de datos lógica no lo requiere.
  • 12c y superior
    La limpieza asincrónica de índices globales hace que la distinción entre índices locales y globales sea menos obvia.

La elección de un índice local o global depende de si la eliminación de partición está en vigor, es decir, si la clave de partición está incluida en el predicado.

Por ejemplo, para un índice local, si no hay una clave de partición en la condición de consulta y la clave de índice no está en la clave de partición, todas las particiones deben atravesarse, lo que es muy costoso, especialmente para las particiones compuestas. En este momento, es más apropiado utilizar un índice global.

Los índices locales también pueden resultar útiles si hay una clave de partición disponible en el predicado del filtro. El índice local es muy adecuado para ILM, mientras que el índice global es conveniente para la búsqueda rápida. El uso específico depende del escenario de la aplicación.

Casos de uso de indexación especial/ Casos de uso de indexación especial

Esta serie de tutoriales ha cubierto las estrategias comunes de partición adoptadas por los desarrolladores para crear aplicaciones exitosas en grandes volúmenes de datos. Sin embargo, hay casos específicos (特殊情况) que también deben tenerse en cuenta. Por ejemplo, podría tener un índice particionado en una tabla que no está particionada. De manera similar, es posible que la estrategia de partición del índice no se alinee con la estrategia de partición de la tabla subyacente. Tales ejemplos suelen ser raros, sin embargo, hay un caso importante en el que la partición hash de un índice es fundamental para lograr niveles extremos de rendimiento de OLTP .

video

punto clave:

  • Flexibilidad
    No es necesario que la partición de un índice coincida con la partición de la tabla en la que se basa, pero esto rara vez ocurre.
  • La partición de contención
    es una forma eficaz de distribuir la actividad entre varios segmentos para reducir la contención de bloques de datos comunes .
  • La palabra clave GLOBAL
    Aunque la mayor parte de la sintaxis de partición es simplemente PARTICIÓN POR, para particionar un índice necesita prefijarlo con la palabra clave GLOBAL.

Para las tablas no particionadas, no existe el concepto de índices locales, pero los índices aún se pueden particionar, la sintaxis es la siguiente:

create index idx1 on sales(id) 
GLOBAL PARTITION BY ...

Otro caso de uso especial es el índice particionado hash.
Para la inserción de alta frecuencia, dado que la inserción es secuencial, la operación de escritura se concentrará en el bloque hoja inicial del índice, lo que provocará problemas de concurrencia.
Si se utiliza el índice particionado hash, los puntos de acceso se distribuirán (habrá 8 bloques de hoja principales indexados en este momento):

create index idx1 on sales(id) 
GLOBAL PARTITION BY HASH(txn_id) partitions 8;

Consultar tablas particionadas/Consultar tabla de particiones

Quizás el beneficio más atractivo del particionamiento es que se pueden lograr mejoras de rendimiento al consultar tablas particionadas. La poda de particiones es el término que se usa para describir cuando los predicados de una consulta son tales que no es necesario escanear toda la tabla, sino solo un subconjunto de las particiones. La detección de la eliminación de particiones se basa en comprender el plan de ejecución de una consulta determinada.

video

punto clave:


  • La columna Pstart/ Pstop en el plan de ejecución Pstart/ Pstop muestra el rango de particiones escaneadas. Cuando se muestra KEY, significa que la decisión se toma en el momento de la ejecución en lugar del momento del análisis .
  • La poda de partición de enlace
    se aplica a las variables de enlace, así como a los valores literales de los predicados de clave de partición.
  • Intervalo
    Dado que una tabla de particiones de intervalo ha definido lógicamente el millón de particiones (límite superior en el número de particiones), los valores de Pstart/Pstop pueden ser engañosos.
  • La poda poderosa
    puede ocurrir con predicados de igualdad, predicados de rango, expresiones en lista y muchas otras permutaciones.

Consulta de eficiencia = datos requeridos / datos escaneados

drop table DEMO purge;

create table DEMO
(
    tstamp timestamp not null,
    empno   number(10) not null,
    ename   varchar2(10) not null,
    deptno  varchar2(10) not null
)
partition by range(tstamp)
(
    partition p00 values less than (timestamp '2010-01-01 00:00:00'),
    partition p01 values less than (timestamp '2010-02-01 00:00:00'),
    partition p02 values less than (timestamp '2010-03-01 00:00:00'),
    partition p03 values less than (timestamp '2010-04-01 00:00:00'),
    partition p04 values less than (timestamp '2010-05-01 00:00:00'),
    partition p05 values less than (timestamp '2010-06-01 00:00:00'),
    partition p06 values less than (timestamp '2010-07-01 00:00:00'),
    partition p07 values less than (timestamp '2010-08-01 00:00:00'),
    partition p08 values less than (timestamp '2010-09-01 00:00:00'),
    partition p09 values less than (timestamp '2010-10-01 00:00:00'),
    partition p10 values less than (timestamp '2010-11-01 00:00:00'),
    partition p11 values less than (timestamp '2010-12-01 00:00:00'),
    partition p12 values less than (timestamp '2011-01-01 00:00:00')
);

insert /*+ APPEND */ into DEMO
select 
    trunc(date '2010-01-01', 'YYYY') + mod(rownum, 360),
    rownum,
    rownum,
    mod(rownum, 1000)
from dual
connect by level <= 1000000;

commit;

Mirando el plan de ejecución, PARTITION RANGE SINGLE significa que solo se ha consultado una única partición:

set lines 140
set autotrace on
select count(*) from DEMO where tstamp = to_date('01-JUN-2010');

------------------------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    11 |    91   (2)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE         |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE SINGLE|      |  2778 | 30558 |    91   (2)| 00:00:01 |     7 |     7 |
|*  3 |    TABLE ACCESS FULL    | DEMO |  2778 | 30558 |    91   (2)| 00:00:01 |     7 |     7 |
------------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"=TIMESTAMP' 2010-06-01 00:00:00')

El siguiente SQL no puede usar la eliminación de particiones debido al uso de funciones, por lo que PARTITION RANGE ALL:

set lines 140
set autotrace on
select count(*) from DEMO where trunc(tstamp) = to_date('01-JUN-2010');

---------------------------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     1 |    11 |  1176   (4)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE      |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE ALL|      | 10000 |   107K|  1176   (4)| 00:00:01 |     1 |    13 |
|*  3 |    TABLE ACCESS FULL | DEMO | 10000 |   107K|  1176   (4)| 00:00:01 |     1 |    13 |
---------------------------------------------------------------------------------------------

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

   3 - filter(TRUNC(INTERNAL_FUNCTION("TSTAMP"))=TO_DATE(' 2010-06-01 00:00:00',
              'syyyy-mm-dd hh24:mi:ss'))

Las variables de vinculación también pueden usar la eliminación de particiones, la palabra clave KEY indica que la eliminación de particiones se determina en tiempo de ejecución en lugar de tiempo de análisis:

set lines 140
set autotrace on

SQL> variable b1 varchar2(20);
SQL> begin
  2  :b1 := '01-JUN-2010';
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> print b1

B1
--------------------------------------------------------------------------------------------
01-JUN-2010

SQL> select count(*) from DEMO where tstamp = :b1;

  COUNT(*)
----------
         0


Execution Plan
----------------------------------------------------------
Plan hash value: 1642956652

------------------------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    11 |    91   (2)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE         |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE SINGLE|      |  2778 | 30558 |    91   (2)| 00:00:01 |   KEY |   KEY |
|*  3 |    TABLE ACCESS FULL    | DEMO |  2778 | 30558 |    91   (2)| 00:00:01 |   KEY |   KEY |
------------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"=TO_TIMESTAMP(:B1))

La escena de PARTITION RANGE OR, a saber, KEY(OR) :

select count(*) from DEMO
where 
	tstamp between to_date('12-JAN-2010') and to_date('07-FEB-2010')
or 
	tstamp between to_date('03-JUN-2010') and to_date('06-AUG-2010');

--------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    11 |     3   (0)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE     |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE OR|      |   247K|  2658K|     3   (0)| 00:00:01 |KEY(OR)|KEY(OR)|
|*  3 |    TABLE ACCESS FULL| DEMO |   247K|  2658K|     3   (0)| 00:00:01 |KEY(OR)|KEY(OR)|
--------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP">=TIMESTAMP' 2010-06-03 00:00:00' AND "TSTAMP"<=TIMESTAMP'
              2010-08-06 00:00:00' OR "TSTAMP"<=TIMESTAMP' 2010-02-07 00:00:00' AND
              "TSTAMP">=TIMESTAMP' 2010-01-12 00:00:00')

Escenarios para PARTITION RANGE INLIST:

select count(*) from DEMO
where 
	tstamp in (
	to_date('12-JAN-2010'),
	to_date('07-FEB-2010')
	);

------------------------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    11 |     2   (0)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE         |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE INLIST|      |  5556 | 61116 |     2   (0)| 00:00:01 |KEY(I) |KEY(I) |
|*  3 |    TABLE ACCESS FULL    | DEMO |  5556 | 61116 |     2   (0)| 00:00:01 |KEY(I) |KEY(I) |
------------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"=TIMESTAMP' 2010-01-12 00:00:00' OR "TSTAMP"=TIMESTAMP'
              2010-02-07 00:00:00')

Mire un plan de ejecución cuyos datos no están en la partición:

select count(*) from DEMO where tstamp = to_date('01-JUN-2022');
-----------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     1 |    11 |     2   (0)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE        |      |     1 |    11 |            |          |       |       |
|   2 |   PARTITION RANGE EMPTY|      |     1 |    11 |     2   (0)| 00:00:01 |INVALID|INVALID|
|*  3 |    TABLE ACCESS FULL   | DEMO |     1 |    11 |     2   (0)| 00:00:01 |INVALID|INVALID|
-----------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"=TIMESTAMP' 2022-06-01 00:00:00')


La partición de intervalo es especial, Pstop es 1048575, es decir, 1024 por 1024 menos 1:

drop table demo purge;

create table DEMO
(
    tstamp timestamp not null,
    empno   number(10) not null,
    ename   varchar2(10) not null,
    deptno  varchar2(10) not null
)
partition by range(tstamp)
interval (numtoyminterval(1, 'MONTH'))
(
	partition p00 values less than
	(timestamp '2010-01-01 00:00:00')
);

select * from demo;

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 2349549400

--------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    40 |     2   (0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE ALL|      |     1 |    40 |     2   (0)| 00:00:01 |     1 |1048575|
|   2 |   TABLE ACCESS FULL | DEMO |     1 |    40 |     2   (0)| 00:00:01 |     1 |1048575|
--------------------------------------------------------------------------------------------

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

-- 最后看一个数据不在分区中的执行计划
select count(*) from DEMO where tstamp = to_date('01-JUN-2022');

------------------------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    13 |     2   (0)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE         |      |     1 |    13 |            |          |       |       |
|   2 |   PARTITION RANGE SINGLE|      |     1 |    13 |     2   (0)| 00:00:01 |   151 |   151 |
|*  3 |    TABLE ACCESS FULL    | DEMO |     1 |    13 |     2   (0)| 00:00:01 |   151 |   151 |
------------------------------------------------------------------------------------------------

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

   3 - filter("TSTAMP"=TIMESTAMP' 2022-06-01 00:00:00')

Consultas de partición con uniones/consultas de partición con uniones

Las tablas particionadas rara vez se consultan de forma aislada y, por lo tanto, obtener los beneficios de la eliminación de particiones, incluso cuando una tabla particionada se une a otra tabla o está involucrada con una subconsulta, es una característica fundamental de Oracle Database. Al utilizar una estructura de memoria conocida como filtro Bloom , la base de datos puede identificar rápidamente qué particiones se necesitarán para satisfacer una consulta determinada.

video

punto clave:

  • Una entrada de prefijo BF
    : BF[dígito] en el plan de ejecución indica que se está utilizando un filtro Bloom, p.:BF0001
  • Los filtros Efficient
    Bloom utilizan estructuras eficientes en memoria para identificar qué particiones se pueden eliminar.
  • Negativo/Positivo
    Con los filtros Bloom, puede haber falsos positivos, por lo que la operación de combinación aún puede procesar un poco más de datos, pero los falsos negativos no son posibles, por lo que su consulta no será un resultado incorrecto.

La definición de Bloom Filter en Wikipedia:

Un filtro Bloom es una estructura de datos probabilísticos eficiente en el espacio, concebida por Burton Howard Bloom en 1970, que se utiliza para probar si un elemento es miembro de un conjunto. Las coincidencias de falsos positivos son posibles, pero los falsos negativos no.

Para falso positivo y falso negativo, el autor dio un ejemplo. Comprando boletos de cine, el sitio web dice que hay boletos, pero en realidad no hay boletos, este es el primero;

Otras oportunidades de desempeño/Otras oportunidades de desempeño

Cuando dos tablas tienen definiciones de partición idénticas , la base de datos puede aprovechar este conocimiento. Si tuviéramos tablas de animales, entonces el sentido común nos dice que nunca emparejaremos razas entre la partición de perros en una mesa y la partición de gatos en la otra mesa. Una unión debería poder detectar cuándo dos particiones dispares nunca podrían tener una fila coincidente y, por lo tanto, eliminarse de una operación de unión. Esto se conoce como combinación de partición (基于分区的join或分区join) .

punto clave:

  • Las definiciones de partición idénticas
    deben estar perfectamente alineadas para que se produzcan uniones de partición completas.
  • Referencia
    Una partición de referencia es un candidato perfecto para una unión particionada, ya que las particiones entre dos tablas relacionadas son, por definición, las mismas
  • Jerarquía del plan
    Sepa dónde se colocan las líneas JOIN y PARTIION en el plan de ejecución para detectar uniones de partición.

El
autor del video dio un ejemplo de emparejamiento de calcetines, pero no lo entendí.

select ... from SOCKS n, SOCKS s
where n.style_size = s.style_size;

Después de 2 días, finalmente terminé de aprender.

Supongo que te gusta

Origin blog.csdn.net/stevensxiao/article/details/127836162
Recomendado
Clasificación