Используя ScrollableResults Hibernate для медленного чтения 90 миллионов записей

Я просто должен читать, каждая строка в таблице в моем использовании базы данных MySQL В спящем режиме и пишут файл на основе его. Но существует 90 миллионов строк, и они являются довольно большими. Таким образом, казалось, что следующее будет соответствующим:

ScrollableResults results = session.createQuery("SELECT person FROM Person person")
            .setReadOnly(true).setCacheable(false).scroll(ScrollMode.FORWARD_ONLY);
while (results.next())
    storeInFile(results.get()[0]);

Проблемой является вышеупомянутое, попытается загрузить все 90 миллионов строк в RAM перед хождением дальше к циклу с условием продолжения..., и это уничтожит мою память с OutOfMemoryError: исключения пространства "кучи" Java :(.

Таким образом, я предполагаю, что ScrollableResults не то, что я искал? Что надлежащий путь состоит в том, чтобы обработать это? Я не возражаю, если этот цикл с условием продолжения занимает дни (хорошо, я любил бы его к не).

Я предполагаю единственный другой способ обработать, это должно использовать setFirstResult, и setMaxResults, чтобы выполнить итерации через результаты и просто использовать регулярный В спящем режиме результаты вместо ScrollableResults. Это чувствует, что будет неэффективно, хотя и начнет занимать много времени смехотворно, когда я называю setFirstResult на 89 миллионных строках...

ОБНОВЛЕНИЕ: setFirstResult/setMaxResults не работает, это, оказывается, неприменимо занимает много времени для получения до смещений как, я боялся. Здесь должно быть решение! Разве это не довольно стандартная процедура?? Я готов воздержаться, в спящем режиме и используют JDBC или любой ценой.

ОБНОВЛЕНИЕ 2: решение, которое я предложил, какие работы хорошо, не большой, в основном имеют форму:

select * from person where id > <offset> and <other_conditions> limit 1

Так как у меня есть другие условия, даже все в индексе, это все еще не с такой скоростью, как я хотел бы, чтобы это было..., так все еще открываются для других предложений..

51
задан at. 13 May 2010 в 07:25
поделиться

4 ответа

Использование setFirstResult и setMaxResults - ваш единственный вариант, о котором я знаю.

Традиционно прокручиваемый набор результатов передавал бы строки клиенту только по мере необходимости. К сожалению, MySQL Connector/J фактически симулирует это, он выполняет весь запрос и передает его клиенту, поэтому драйвер на самом деле имеет весь набор результатов, загруженный в оперативную память, и будет передавать его вам по капле (об этом свидетельствуют ваши проблемы с нехваткой памяти). У вас была правильная идея, это просто недостатки java-драйвера MySQL.

Я не нашел способа обойти это, поэтому перешел на загрузку больших кусков с помощью обычных методов setFirst/max. Извините, что приношу плохие новости.

Просто убедитесь, что используете сессию без статического состояния, чтобы не было кэша на уровне сессии или грязного отслеживания и т.д.

EDIT:

Ваш UPDATE 2 - это лучшее, что вы можете получить, если только не выберетесь из MySQL J/Connector. Хотя нет причин, по которым вы не можете увеличить лимит на запрос. При условии, что у вас достаточно оперативной памяти для хранения индекса, это должно быть несколько дешевой операцией. Я бы немного изменил его, и захватил бы партию за раз, и использовал бы самый высокий id этой партии для захвата следующей партии.

Примечание: это будет работать только если other_conditions используют равенство (не допускаются условия диапазона) и имеют последний столбец индекса как id.

select * 
from person 
where id > <max_id_of_last_batch> and <other_conditions> 
order by id asc  
limit <batch_size>
29
ответ дан 7 November 2019 в 10:15
поделиться

С 90 миллионами записей, похоже, вы должны группировать свои SELECT. Я покончил с Oracle при первоначальной загрузке в распределенный кеш. Глядя на документацию MySQL, похоже, что эквивалент использует предложение LIMIT: http://dev.mysql.com/doc/refman/5.0/en/select.html

Вот пример:

SELECT * from Person
LIMIT 200, 100

Это вернет строки с 201 по 300 таблицы Person .

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

Другим преимуществом этого является параллелизм - вы можете выполнять несколько потоков параллельно для более быстрой обработки.

Обработка 90 миллионов записей также не кажется оптимальным вариантом для использования Hibernate.

1
ответ дан 7 November 2019 в 10:15
поделиться

Я успешно использовал функцию прокрутки Hibernate, прежде чем она не считала весь набор результатов. Кто-то сказал, что MySQL не работает делают настоящие курсоры прокрутки, но он утверждает, что основан на JDBC dmd.supportsResultSetType (ResultSet.TYPE_SCROLL_INSENSITIVE), и поиск вокруг него кажется, что другие люди использовали его. Убедитесь, что он не кэширует объекты Person в сеансе - я использовал его в SQL-запросах, где не было объекта для кеширования. Вы можете вызвать evict в конце цикла, чтобы убедиться, или протестировать с помощью sql-запроса.Также поэкспериментируйте с setFetchSize, чтобы оптимизировать количество обращений к серверу.

0
ответ дан 7 November 2019 в 10:15
поделиться

Проблема может заключаться в том, что Hibernate сохраняет ссылки на все объекты в сессии до тех пор, пока вы не закроете сессию. Это не имеет никакого отношения к кэшированию запросов. Возможно, поможет выселение() объектов из сессии, после того как вы закончили запись объекта в файл. Если они больше не являются ссылками сессии, сборщик мусора сможет освободить память, и память больше не будет заканчиваться.

1
ответ дан 7 November 2019 в 10:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: