Доброго времени суток. В моем приложении каждую минуту происходит сканирование игровых серверов, с последующим занесением результатов в базу данных. Столкнулся с серьезной проблемой производительности. Занесение результатов в БД (около 2000 записей) занимает ~30 секунд.
Есть следующий код:
@Service
public class ServerScannerImpl implements ServerScanner
{
@Transactional
public void process()
{
...
... // Здесь код сканирования, далее занесение результата в БД
handleResult();
}
private void handleResult()
{
fillServerInfo(server, info); // Для каждого сервера (примерно 2000) вызывается этот метод
}
private void fillServerInfo(Server server, ServerInfo info)
{
...
...
Map map = mapService.getMapByHttpName(mapHttpName, game.getId());
if (map == null)
{
map = new Map();
map.setName(info.getMap());
map.setGame(game);
map.setHttpName(mapHttpName);
mapService.addMap(map);
}
server.setMap(map);
...
...
serverService.addSever(server);
}
@Autowired
private ServerService serverService;
@Autowired
private MapService mapService;
}
С помощью профайлера выяснил, что 80% времени выполнения приходится на этот метод:
Map map = mapService.getMapByHttpName(mapHttpName, game.getId());
@Service
@Transactional(readOnly = true)
public class MapServiceImpl implements MapService
{
public Map getMapById(Integer id)
{
return mapDAO.getMapById(id);
}
public Map getMapByHttpName(String httpName, Integer gameId)
{
return mapDAO.getMapByHttpName(httpName, gameId);
}
@Transactional(readOnly = false)
public void addMap(Map map)
{
mapDAO.addMap(map);
}
public List<Map> getAllMaps()
{
return mapDAO.getAllMaps();
}
@Transactional(readOnly = false)
public void removeMap(Map map)
{
mapDAO.removeMap(map);
}
@Autowired
private MapDAO mapDAO;
}
По call tree видно, что каждый раз, перед выполнением запроса к базе, происходит dirty checking:
org.hibernate.internal.SessionImpl.autoFlushIfRequired(Set)
А поскольку метод mapService.getMapByHttpName(mapHttpName, game.getId()) вызывается ~2000 раз, то львиная доля времени уходит на dirty checking.
Добавил в MapServiceImpl readOnly = true к аннотации @Transactional, но на ситуацию это не повлияло. Я новичек в этой области, поэтому не представляю как решить эту проблему. Спасибо за помощь.
Здравствуйте, InstantI, Вы писали:
II>[/java]
II>По call tree видно, что каждый раз, перед выполнением запроса к базе, происходит dirty checking:
II>II>org.hibernate.internal.SessionImpl.autoFlushIfRequired(Set)
II>
II>А поскольку метод mapService.getMapByHttpName(mapHttpName, game.getId()) вызывается ~2000 раз, то львиная доля времени уходит на dirty checking.
II>Добавил в MapServiceImpl readOnly = true к аннотации @Transactional, но на ситуацию это не повлияло. Я новичек в этой области, поэтому не представляю как решить эту проблему. Спасибо за помощь.
А что там делает MapDao.getMapByHttpName? session.createQuery()? Тогда там не столько dirty checking, сколько просто отправка изменений в БД. Т.е. Будет много перемежающихся между собой запросов SELECT и UPDATE. Ну тут только вручную загрузить нужные entity в память, применить к ним изменения и вызвать session.flush() или session.commit(). Причём, ещё и настроить batch update в Hibernate. Как узнать, какие именно записи нужно загружать в память — не знаю. Лично я с таким сталкивался на проводке документов. Я заранее знал перечень документов и выковыривал из их позиций перечень товаров/складов. В память, соответственно, загружал остатки по этим товарам/складам.
А вообще, как тут уже правильно сказали, задача слишком простая, чтобы в ней возиться ещё и с ORM.