2010-02-17 4 views
1

Мне нужно удалить миллионы строк из таблицы из таймера EJB. Проблема заключается в том, что таймер имеет тайм-аут транзакции 90 секунд, поэтому я должен разделить работу на куски размером с укусом.Удалить огромное количество строк из таймера EJB

Поскольку я не знаю, сколько строк можно удалить за 90 секунд, алгоритм должен зацикливаться и удалять несколько за раз, пока время почти не достигнет.

Проблема в следующем: Как можно удалить количество строк для удаления в JPA? Удаление выполняется во всех строках, имеющих временную метку раньше определенной даты.

Я думаю, что можно найти 1000-й старейший ряд и DELETE WHERE timestamp <= {1000th-oldest-row.timestamp} Это, однако, не очень элегантно, и мне нужно будет добраться до последней строки в 1000, чтобы получить метку времени.

Во-вторых, таймер должен срабатывать сразу же, если таблица не очищается после 90 секунд. Это легко решить, но, опять же, не очень элегантно.

ответ

2

Вы по-прежнему сталкиваетесь с проблемами с истечением срока действия с решением, которое у вас есть.

Хитрость заключается в выполнении каждого фрагмента в отдельной транзакции, как показано ниже в pesudo-коде.

@Entity 

@NamedQueries (value = { 
    @NamedQuery (
     name = pagedDeleteExpiredItems 
     query= DELETE FROM MyTable 
      WHERE (<table key>) IN (
       SELECT <table key> FROM (
       SELECT ROWNUM AS row_num, <table key> FROM MyTable 
       WHERE timestamp <= :currentTime 
       ) 
       WHERE row_num < :pageSize 
      ) 
    ) 
}) 

public class MyEntity { 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    int doPagedDeleteExpiredItems(Date currentTime, int pageSize) { 
     Query query = em.createNamedQuery("pagedDeleteExpiredItems"); 
     query.setParameter("currentTime", currentTime); 
     query.setParameter("pageSize", pageSize); 
     int deleteCount = query.executeUpdate(); 
     return deleteCount; 
    } 
} 


@EJBTimer 
public class DeleteExpiredItemsTimer { 

    @EJB(beanName = "MyEntity") 
    MyEntity myEntity; 

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) 
    void handleTimeout(Timer timer) { 
     Date currentTime = getCurrentTime() 
     int pageSize = 100 
     int deleteCount; 
     do { 
      myEntity.doPagedDeleteExpiredItems(currentTime, pageSize); 
     } while(deleteCount>0); 
    } 
} 
+0

Ну, транзакции решены для меня в моем ограничении. Но ваше решение красиво. – Hugo

+0

Спасибо Hugo за ваш комментарий. –

1

У нас было аналогичное требование, и вот как мы его решили. Я использовал EJB 3.0.

  1. Таймер запускается приложением. сервер запускается (или модуль развернут) в ServletContextListener.
  2. Когда таймер срабатывает, он обрабатывает до 100 строк, которые находятся на рассмотрении. Затем вам нужно заказать результат запроса и ограничить количество строк.
  3. Если было 100 строк, таймер планирует следующий тайм-аут с 0ms. Что это, транзакция совершена, и таймер снова срабатывает в новой транзакции.
  4. Если было меньше 100 строк, таймер планирует следующий тайм-аут в 90sec.

Если есть, скажем, 250 строк, таймер срабатывает три раза в последовательности. Есть только незначительная проблема, если есть ровно 100 строк для обработки, и в этом случае таймер срабатывает дважды в последовательности, но 2-й огонь фактически не работает. Но в целом, он работал нормально.

+0

Да, именно так я хотел бы это сделать. Но как я могу ограничить количество удаленных строк? – Hugo

+0

Что-то вроде 'УДАЛИТЬ ИЗ ЗАКАЗА MyTable BY timestamp DESC LIMIT 100'. Точный синтаксис SQL будет зависеть от используемой вами базы данных. С помощью JDBC вы можете получить количество строк, на которые влияет DELETE, и действовать, как я описал. – ewernli

1

Один трюк я использовал в SQL является не DELETE TOP 1000 (или 100 или 10000, в зависимости от среднего количества строк в странице), например так:

DELETE top 1000 WHERE timestamp <= @ExpirationDate 

вызов этого пока нет строки удаляются (проверьте с @@ rowcount) или у вас закончилось время. Может ли этот метод быть реализован в JPA?

0

Решил проблему, получив отсортированный список строк, пригодных для очистки, и используя setFirstResult (int) до того же значения, что и setMaxResults (int). Таким образом, я получаю упорядочивание элемента приблизительно с maxCount шагов от самого старого.

Query expired = dm.createNamedQuery("getExpiredElements"); 
expired.setParameter("currentTime", getCurrentTime()); 
expired.setMaxResults(maxCount); 
expired.setFirstResult(maxCount); 
@SuppressWarnings("unchecked") 
List<Item> expiredChunk = (List<Item>) expired.getResultList(); 
long lastChunkEndTime = expiredChunk.get(0).getEndTime(); 
Query query = em.createNamedQuery("deleteExpiredItems"); 
query.setParameter("currentTime", lastChunkEndTime); 
int result = query.executeUpdate(); 
return result >= maxCount; 

Функция возвращает true (по крайней мере), если она должна быть выполнена снова.

Смежные вопросы