Administro una aplicación que tiene un back-end de base de datos Oracle muy grande (casi 1 TB de datos con más de 500 millones de filas en una tabla). La base de datos realmente no hace nada (sin SProcs, sin desencadenantes ni nada) es solo un almacén de datos.
Todos los meses estamos obligados a purgar registros de las dos tablas principales. El criterio para la purga varía y es una combinación de antigüedad de fila y un par de campos de estado. Por lo general, terminamos purgando entre 10 y 50 millones de filas por mes (agregamos alrededor de 3-5 millones de filas por semana a través de importaciones).
Actualmente tenemos que hacer esta eliminación en lotes de aproximadamente 50,000 filas (es decir, eliminar 50000, comit, eliminar 50000, commit, repetir). Intentar eliminar todo el lote al mismo tiempo hace que la base de datos no responda durante aproximadamente una hora (dependiendo del número de filas). Eliminar las filas en lotes como este es muy difícil para el sistema y, por lo general, tenemos que hacerlo "según lo permita el tiempo" en el transcurso de una semana; permitir que la secuencia de comandos se ejecute continuamente puede provocar una degradación del rendimiento que es inaceptable para el usuario.
Creo que este tipo de eliminación por lotes también degrada el rendimiento del índice y tiene otros impactos que eventualmente hacen que el rendimiento de la base de datos se degrade. Hay 34 índices en una sola tabla, y el tamaño de los datos del índice es en realidad mayor que los datos en sí.
Aquí está el script que una de nuestras personas de TI usa para hacer esta purga:
BEGIN
LOOP
delete FROM tbl_raw
where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;
exit when SQL%rowcount < 49999;
commit;
END LOOP;
commit;
END;
Esta base de datos debe estar al 99.99999% y solo tenemos una ventana de mantenimiento de 2 días una vez al año.
Estoy buscando un mejor método para eliminar estos registros, pero aún no he encontrado ninguno. ¿Alguna sugerencia?
fuente
Respuestas:
La lógica con 'A' y 'B' podría estar "oculta" detrás de una columna virtual en la que podría realizar la partición:
fuente
La solución clásica para esto es dividir sus tablas, por ejemplo, por mes o por semana. Si no los ha encontrado antes, una tabla particionada es como varias tablas idénticamente estructuradas con un implícito
UNION
al seleccionar, y Oracle almacenará automáticamente una fila en la partición apropiada al insertarla en función de los criterios de partición. Usted menciona los índices; bueno, cada partición también tiene sus propios índices particionados. Es una operación muy barata en Oracle dejar caer una partición (es análogo a unTRUNCATE
en términos de carga porque eso es lo que realmente está haciendo: truncar o soltar una de estas subtablas invisibles). Será una gran cantidad de procesamiento dividir "después del hecho", pero no tiene sentido llorar por la leche derramada: las ventajas de hacerlo hasta ahora superan los costos. Cada mes dividiría la partición superior para crear una nueva partición para los datos del próximo mes (puede automatizar fácilmente esto con aDBMS_JOB
).Y con las particiones también puede explotar la consulta paralela y la eliminación de particiones , lo que debería hacer que sus usuarios estén muy contentos ...
fuente
A
entoncesDateA
mayor de 3 años, se purga. Si el estado esB
yDateB
tiene más de 10 años, se purga. Si mi comprensión de la partición es correcta, entonces la partición no sería útil en una situación como esta (al menos en lo que respecta a la purga).Un aspecto a tener en cuenta es la cantidad de rendimiento de eliminación resultante de los índices y la cantidad de la tabla sin formato. Cada registro eliminado de la tabla requiere la misma eliminación de la fila de cada índice btree. Si tiene más de 30 índices btree, sospecho que la mayor parte de su tiempo se dedica al mantenimiento del índice.
Esto tiene un impacto en la utilidad de la partición. Digamos que tiene un índice de nombre. Un índice Btree estándar, todo en un segmento, podría tener que hacer cuatro saltos para pasar del bloque raíz al bloque hoja y una quinta lectura para obtener la fila. Si ese índice está dividido en 50 segmentos y no tiene la clave de partición como parte de la consulta, entonces será necesario verificar cada uno de esos 50 segmentos. Cada segmento será más pequeño, por lo que es posible que solo tenga que hacer 2 saltos, pero aún así puede terminar haciendo 100 lecturas en lugar de las 5 anteriores.
Si son índices de mapa de bits, las ecuaciones son diferentes. Probablemente no esté utilizando índices para identificar filas individuales, sino conjuntos de ellas. Entonces, en lugar de una consulta que usa 5 IO para devolver un solo registro, estaba usando 10,000 IO. Como tal, la sobrecarga adicional en particiones adicionales para el índice no importará.
fuente
la eliminación de 50 millones de registros por mes en lotes de 50,000 es solo 1000 iteraciones. si elimina 1 cada 30 minutos, debe cumplir con su requisito. una tarea programada para ejecutar la consulta que publicó pero eliminar el bucle para que solo se ejecute una vez no debería causar una degradación notable para los usuarios. Hacemos aproximadamente el mismo volumen de registros en nuestra planta de fabricación que funciona casi las 24 horas del día, los 7 días de la semana y satisface nuestras necesidades. De hecho, lo distribuimos un poco más de 10,000 registros cada 10 minutos, que se ejecuta en aproximadamente 1 o 2 segundos ejecutándose en nuestros servidores Oracle Unix.
fuente
Si el espacio en el disco no es muy importante, podría crear una copia de "trabajo" de la tabla, por ejemplo
my_table_new
, utilizando CTAS (Crear tabla como selección) con criterios que omitirían los registros que se eliminarán. Puede hacer la declaración de creación en paralelo, y con la sugerencia de agregar para hacerlo más rápido, y luego construir todos sus índices. Luego, una vez que haya terminado (y probado), cambie el nombre de la tabla existentemy_table_old
y cambie el nombre de la tabla de "trabajo"my_table
. Una vez que te sientas cómodo con tododrop my_table_old purge
para deshacerte de la vieja mesa. Si hay un montón de restricciones de clave externa, eche un vistazo aldbms_redefinition
paquete PL / SQL . Clonará sus índices, restricciones, etc. cuando use las opciones apropiadas. Este es un resumen de una sugerencia de Tom Kyte de AskTomfama. Después de la primera ejecución, puede automatizar todo, y la tabla de creación debería ir mucho más rápido, y se puede hacer mientras el sistema está activo, y el tiempo de inactividad de la aplicación se limitaría a menos de un minuto para cambiar el nombre de las tablas. Usar CTAS será mucho más rápido que hacer varias eliminaciones por lotes. Este enfoque puede ser particularmente útil si no tiene licencia de partición.CTAS de muestra, manteniendo filas con datos de los últimos 365 días y
flag_inactive = 'N'
:fuente
al soltar una partición, deja inutilizables los índices globales, que necesitan reconstruirse, la reconstrucción de los índices globales sería un gran problema, ya que si lo hace en línea, será bastante lento, de lo contrario necesitará tiempo de inactividad. en cualquier caso, no puede cumplir con el requisito.
"Normalmente terminamos purgando entre 10 y 50 millones de filas por mes"
recomendaría usar PL / SQL batch delete, varias horas está bien, creo.
fuente