java.lang.OutOfMemoryError при сохранении большого количества записей

У меня проблема с сохранением огромного количества записей в базе данных с помощью CFWheels. Вот пример:

<cfloop from="1" to="10000" index="i">
 <cfset var newUser = model("user").new()>
 <cfset newUser.name = "Test"&i>
 <cfset newUser.save()>
</cfloop>

Это вызывает java.lang.OutOfMemoryError

Пожалуйста, помогите мне, как решить эту проблему.


person balytskyi    schedule 01.06.2011    source источник


Ответы (3)


Здесь происходит пара довольно неэффективных вещей. Во-первых, он генерирует 1000 объектов user, что на самом деле не очень хорошая идея делать в одном запросе в ColdFusion. Во-вторых, выполняется 1000 запросов к базе данных, что на самом деле не очень хорошая идея для любого языка программирования.

Я бы прекратил использовать объекты модели для такого случая и выяснил, как сжать логику в один запрос к базе данных. ORM в Wheels, как правило, очень полезен, но в подобных ситуациях он имеет свои ограничения.

Например, если вы используете SQL Server 2008, вы можете сделать это внутри своей модели user, чтобы все было под одним вызовом cfquery:

<cffunction name="batchCreate">
    <cfquery datasource="#get('dataSourceName')#">
        INSERT INTO
            #this.tableName()# (#this.columnNameForProperty("name")#)
        VALUES
            <cfloop from="1" to="10000" index="i">
                (<cfqueryparam cfsqltype="cf_sql_varchar" value="Test#i#">)
                <cfif i lt 10000>,</cfif>
            </cfloop>
    </cfquery>
</cffunction>

Конечно, запрос будет выглядеть по-другому, если вы используете MySQL или другой движок базы данных.

person Chris Peters    schedule 02.06.2011

Зацикливание нескольких вызовов базы данных, ведущих к OOM, является известной ошибкой ColdFusion. К счастью, есть обходной путь, используйте <cfthread/>. Вы должны иметь возможность изменить свой код как таковой:

<cfloop from="1" to="10000" index="i">
 <cfset threadName = "thread" & createUuid()>
 <cfthread name="#threadName#">
  <cfset var newUser = model("user").new()>
  <cfset newUser.name = "Test"&i>
  <cfset newUser.save()>
 </cfthread>
 <cfthread action="join" name="#threadName#">
</cfloop>

В этой ситуации вы используете поток исключительно из-за его побочного эффекта, работая в другом контексте, чтобы он не оставался в куче. Таким образом, немедленное соединение сразу после объявления потока, поэтому на самом деле ничего не выполняется параллельно.

person orangepips    schedule 01.06.2011
comment
@Henry: cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker / — зарегистрированная ошибка. Насколько я знаю, Adobe не подтвердила и не прокомментировала эту проблему. На практике встречается относительно редко. - person orangepips; 01.06.2011

Можно попробовать запустить сборщик мусора: http://www.beetrootstreet.com/blog/index.cfm/2009/6/25/Clearing-ColdFusion-memory-using-garbage-collection-when-memory-gets-low

person LarZuK    schedule 01.06.2011
comment
Это в лучшем случае опасно. Существует множество статей о том, почему попытка приложения собрать саму JVM — плохая идея. Вместо этого узнайте, как и почему память заполняется, используя такие инструменты, как VisualVM и Eclipse Memory Analyzer. - person orangepips; 01.06.2011