Ayuda con los hilos o ejecutores de Java: ejecutando varias selecciones de MySQL, insertos y actualizaciones simultáneamente

StackOverflow https://stackoverflow.com/questions/2953955

Pregunta

Estoy escribiendo una aplicación para analizar una base de datos MySQL, y necesito ejecutar varios DML de forma simultánea; por ejemplo:

// In ResultSet rsA: Select * from A;
rsA.beforeFirst();
while (rsA.next()) {
   id = rsA.getInt("id");
   // Retrieve data from table B: Select * from B where B.Id=" + id;
   // Crunch some numbers using the data from B
   // Close resultset B
}

Estoy declarando una variedad de objetos de datos, cada uno con su propia conexión a la base de datos, que a su vez llama a varios métodos para el análisis de datos. El problema es que todos los hilos usan la misma conexión, por lo tanto, todas las tareas arrojan excepto los tiempos: "Bloquear el tiempo de espera de espera excedido; intente reiniciar la transacción"

Creo que hay una manera de escribir el código de tal manera que cualquier objeto dado tenga su propia conexión y ejecute las tareas requeridas independientemente de cualquier otro objeto. Por ejemplo:

DataObject dataObject[0] = new DataObject(id[0]);
DataObject dataObject[1] = new DataObject(id[1]);
DataObject dataObject[2] = new DataObject(id[2]);
...
DataObject dataObject[N] = new DataObject(id[N]);
// The 'DataObject' class has its own connection to the database, 
// so each instance of the object should use its own connection. 
// It also has a "run" method, which contains all the tasks required.
Executor ex = Executors.newFixedThreadPool(10);

for(i=0;i<=N;i++) {
   ex.execute(dataObject[i]);
}
// Here where the problem is: Each instance creates a new connection,
// but every DML from any of the objects is cluttered in just one connection
// (in MySQL command line, "SHOW PROCESSLIST;" throws every connection, and all but
// one are idle).

¿Me puede apuntar en la dirección correcta?

Gracias

¿Fue útil?

Solución 2

Después de un tiempo de ruptura del cerebro, descubrí mis propios errores ... quiero poner este nuevo conocimiento, así que ... aquí voy

Hice un muy gran error Al declarar el objetón de conexión como un objeto estático en mi código ... así que obviamente, a pesar de que creé una nueva conexión para cada nuevo objeto de datos que creé, cada transacción pasó por una sola conexión estática.

Con ese primer problema corregido, volví a la tabla de diseño y me di cuenta de que mi proceso era:

  1. Lea una ID de una tabla de entrada
  2. Tome un bloque de datos relacionados con la ID que lee en el Paso 1, almacenado en otras tablas de entrada
  3. Números Crunch: lea las tablas de entrada relacionadas y procese los datos almacenados en ellas
  4. Guarde los resultados en una o más tablas de salida
  5. Repita el proceso mientras tengo identificaciones pendientes en la tabla de entrada

Simplemente utilizando una conexión dedicada para la lectura de entrada y una conexión dedicada para la escritura de salida, el rendimiento de mi programa aumentó ... ¡pero necesitaba mucho más!

Mi enfoque original para los pasos 3 y 4 fue ahorrar en la salida cada uno de los resultados tan pronto como los tuve ... pero encontré un mejor enfoque:

  • Lea los datos de entrada
  • Crunch los números y coloque los resultados en un montón de colas (una para cada tabla de salida)
  • Un hilo separado está verificando cada segundo si hay datos en cualquiera de las colas. Si hay datos en las colas, escríbalos en las tablas.

Entonces, dividiendo tareas de entrada y salida utilizando diferentes conexiones, y redirigiendo la salida del proceso central a una cola, y al usar un subproceso dedicado para tareas de almacenamiento de salida, finalmente logré lo que quería: ¡ejecución de DML multithreaded!


Sé que hay mejores enfoques para este problema en particular, pero este funciona bastante bien.

Entonces ... si alguien está atrapado con un problema como este ... Espero que esto ayude.

Otros consejos

Creo que el problema es que has confundido mucho nivel medio, transaccional y lógica persistente en una sola clase.

Si está tratando directamente con ResultSet, no está pensando en las cosas de una manera muy orientada a objetos.

Eres inteligente si puedes descubrir cómo hacer que la base de datos haga algunos de tus cálculos.

Si no, recomendaría mantener las conexiones abiertas por el tiempo mínimo posible. Abra una conexión, obtenga el conjunto de resultados, málelo en un objeto o estructura de datos, cierre el conjunto de resultados y la conexión en el alcance local y devuelva la estructura de objetos/datos asignados para su procesamiento.

Mantiene la persistencia y el procesamiento de la lógica separada de esta manera. Te ahorras mucho dolor al mantener las conexiones de corta duración.

Si una solución de procedimiento almacenado es lenta, podría deberse a una indexación deficiente. Otra solución funcionará igualmente mal, si no peor. Intente ejecutar el plan Explicar y vea si alguna de sus consultas está utilizando el escaneo de la tabla. En caso afirmativo, tiene algunos índices para agregar. También podría deberse a grandes registros de reversión si sus transacciones son de larga duración. Hay muchas cosas que podría y debe hacer para asegurarse de que ha hecho todo lo posible con la solución que tiene antes de cambiar. Podría hacer un gran esfuerzo y aún no abordar la causa raíz.

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