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