Pregunta

Tengo una tabla de base de datos en la que cada fila tiene una clave primaria generada aleatoriamente, un mensaje y un usuario. Cada usuario tiene aproximadamente 10-100 mensajes pero hay 10k-50k usuarios.

Escribo los mensajes diariamente para cada usuario de una vez. Quiero tirar los mensajes antiguos para cada usuario antes de escribir los nuevos para mantener la tabla lo más pequeña posible.

Ahora mismo efectivamente hago esto:

delete from table where user='mk'

Luego escriba todos los mensajes para ese usuario. Veo mucha controversia porque tengo muchos hilos haciendo esto al mismo tiempo.

Tengo un requisito adicional para retener el conjunto de mensajes más reciente para cada usuario.

No tengo acceso a la base de datos directamente. Estoy tratando de adivinar el problema en base a algunos comentarios de segunda mano. La razón por la que me estoy centrando en este escenario es que la consulta de eliminación muestra mucho tiempo de espera (de nuevo, según mi leal saber y entender), además es una nueva funcionalidad añadida.

¿Alguien puede ofrecer algún consejo?

¿Sería mejor:

select key from table where user='mk'

¿Entonces eliminar filas individuales desde allí? Estoy pensando que eso podría conducir a un bloqueo menos brutal.

¿Fue útil?

Solución

No, siempre es mejor realizar una sola instrucción SQL en un conjunto de filas que una serie de "fila por fila". (o lo que Tom Kyte llama operaciones "lento por lento"). Cuando dices que estás viendo mucha contención, ¿qué estás viendo exactamente? Una pregunta obvia: ¿está indexada la columna USUARIO?

(¡Por supuesto, el nombre de la columna no puede ser USUARIO en una base de datos Oracle, ya que es una palabra reservada!)

EDITAR: ha dicho que la columna USUARIO no está indexada. Esto significa que cada eliminación implicará una exploración completa de la tabla de hasta 50K * 100 = 5 millones de filas (o, en el mejor de los casos, 10K * 10 = 100,000 filas) para eliminar solo 10-100 filas. Agregar un índice al USUARIO puede resolver sus problemas.

Otros consejos

Si hace esto todos los días para cada usuario, ¿por qué no simplemente eliminar cada registro de la tabla en una sola declaración? O incluso

truncate table whatever reuse storage
/

edit

La razón por la que sugiero este enfoque es que el proceso parece una carga diaria por lotes de mensajes de usuario precedida por la eliminación de los mensajes antiguos. Es decir, las reglas de negocio me parecen ser "la tabla contendrá mensajes de un solo día para cualquier usuario". Si este proceso se realiza para cada usuario, una sola operación sería la más eficiente.

Sin embargo, si los usuarios no reciben un nuevo conjunto de mensajes cada día y hay una regla subsidiaria que requiere que retengamos el conjunto de mensajes más reciente para cada usuario, luego eliminar toda la tabla estar equivocado.

¿Estás seguro de que estás viendo contención de bloqueo? Parece más probable que esté viendo contención de disco debido a demasiadas actualizaciones simultáneas (pero no relacionadas). La solución a esto es simplemente reducir la cantidad de subprocesos que está utilizando: menos contención del disco significará un rendimiento total más alto.

Creo que necesita definir sus requisitos un poco más claros ...

Por ejemplo. Si conoce a todos los usuarios para los que desea escribir mensajes, inserte las ID en una tabla temporal, indexe en ID y elimine por lotes. Entonces los hilos que estás disparando están haciendo dos cosas. Escriba el ID del usuario en una tabla temporal. Escriba el mensaje en otra tabla temporal. Luego, cuando los hilos hayan terminado de ejecutarse, el hilo principal debería

BORRAR * DE Mensajes INTERNOS ÚNETE TEMP_MEMBERS EN ID = TEMP_ID

INSERTAR EN MENSAJES SELECCIONAR * DESDE TEMP_messges

no estoy familiarizado con la sintaxis de Oracle, pero esa es la forma en que lo abordaría SI los mensajes de los usuarios se realizan en rápida sucesión.

Espero que esto ayude

HABLE CON SU DBA

Él está allí para ayudarte. Cuando los DBA le quitamos el acceso a los desarrolladores para algo como esto, se supone que le brindaremos el soporte para esa tarea. Si su código tarda demasiado en completarse y ese tiempo parece estar atado en la base de datos, su DBA podrá ver exactamente lo que está sucediendo y ofrecer sugerencias o incluso resolver el problema sin que cambie nada.

Solo mirando su enunciado del problema, no parece que esté viendo problemas de contención, pero no sé nada sobre su estructura subyacente.

Realmente, habla con tu DBA. Probablemente disfrutará viendo algo divertido en lugar de planificar la última implementación de CPU.

Esto podría acelerar las cosas:

Crear una tabla de búsqueda:

create table rowid_table (row_id ROWID ,user VARCHAR2(100));
create index rowid_table_ix1 on rowid_table (user);

Ejecute un trabajo nocturno:

truncate table rowid_table;
insert /*+ append */ into rowid_table
select ROWID row_id , user
from table;
dbms_stats.gather_table_stats('SCHEMAOWNER','ROWID_TABLE');

Luego, al eliminar los registros:

delete from table
where ROWID IN (select row_id
                from rowid_table
                where user = 'mk');

Tu propia sugerencia parece muy sensata. Bloquear lotes pequeños tiene dos ventajas:

  • las transacciones serán más pequeñas
  • el bloqueo se limitará a solo unas pocas filas a la vez

Bloquear lotes debería ser una gran mejora.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top