Пик памяти ColdFusion при чтении CSV-файла для импорта БД - PullRequest
1 голос
/ 30 декабря 2011

У нас есть скрипт ColdFusion 9, который регулярно выполняет чтение файла CSV и вставку записей в базу данных Oracle 11g. Файл CSV содержит около 50 столбцов, 8 из которых используются CF (формат CSV не может быть изменен). Общий поток ОВЛХ:

  1. Считать файл в переменную
  2. CFLOOP, используя эту переменную в качестве атрибута списка с CHR (13) в качестве разделителя
  3. Вызвать хранимую процедуру Oracle, вставляя различные значения из файла, используя ListGetAt

Хранимая процедура выполняет следующие действия:

  1. Вставляет запись с 2 полями в таблицу 1
  2. Вставляет запись с 8 полями (включая первичный ключ таблицы 1) в таблицу 2
  3. ничего не возвращает

В большинстве случаев это работает успешно, считывая файлы размером 400 КБ с сотнями записей всего за пару секунд. Тем не менее, иногда мы получаем большой объем и в результате получаем 13 КБ записи размером 5 МБ. Когда мы пытаемся обработать такой большой файл, я наблюдаю, как использование памяти JVM меняется с 90 МБ до 680 МБ в течение 10-15 секунд, после чего монитор сервера CF перестает отвечать (как и CF), заставляя нас перезапустить службу. Журналы сообщают об ошибке нехватки памяти JVM:

"Ошибка", "qtp4795249-38798", "28.12.11", "16:29:20" ,, "Превышен лимит накладных расходов ГХ" java.lang.OutOfMemoryError: превышен лимит накладных расходов GC

Наш размер кучи JVM в настоящее время составляет 768 МБ. Я не пытался увеличить его, так как даже если это действительно решит эту проблему, это не защитит нас в будущем, а остальная часть обычной нагрузки на сервер не требует почти так много. И я не решаюсь слишком много играть с настройками JVM, которые требуют перезапуска, чтобы вступить в силу на рабочей коробке.

Это сложно проверить, так как процесс импорта работает нормально с едва заметной загрузкой памяти на моем локальном компьютере разработчика и на нашей машине QA, но оба из них имеют гораздо более медленное соединение с базой данных и занимают 10-15 минут, чтобы полный.

Буду признателен за любые мысли, особенно о том, куда идет память. Я не могу понять, как 5 МБ данных превращаются в 700 МБ данных. У нас действительно включена отладочная информация, но IP-адрес вызывающего скрипта отсутствует в списке отладки, и я использовал тег cfsetting, чтобы отключить отладку для этой страницы. Ранее был этап 1.5, который превратил данные CSV в запрос ColdFusion, но я устранил это в попытке повысить эффективность. Оба способа приводят к ошибке OOM.

Ответы [ 4 ]

1 голос
/ 30 декабря 2011

Рассматривали ли вы прямой импорт в базу данных?Для MySQL это НАГРУЗКА ДАННЫХ INFILE , для SQL Server это НАЛИЧНАЯ ВСТАВКА .Если вам нужна дополнительная обработка, то возможный подход - это загрузить данные во временную таблицу, а затем обработать их с помощью CFML, что может быть легко выполнено пакетами для интенсивной обработки.

1 голос
/ 30 декабря 2011

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

Чтение данных файла по одной строке за раз с использованием тега CFFoop ColdFusion Или Java LineNumberReader

Также см. liveocs для cfloop , в частности атрибут файла:

0 голосов
/ 30 декабря 2011

Также обратите внимание на то, что запрос не зацикливается. Большую часть времени, затрачиваемого на выполнение запросов, занимает соединение с БД.

Каждый раз, когда мне нужно сделать несколько вставок из файла, я создаю каждый оператор вставки SQL и сохраняю их в переменную, разделенную точкой с запятой. Затем я выполняю все эти операторы одновременно каждые 100 операторов.

При этом мне пришлось переписать некоторые процедуры другого программиста, и я смог сократить время обработки на 90%. Это было в версии 6, поэтому совместное использование подключений могло улучшить это.

0 голосов
/ 30 декабря 2011

У нас есть приложение CF, которое импортирует списки объектов недвижимости MLS и столкнулось с подобными проблемами. Базовый файл, с которым мы работаем, загружает 100 МБ, а чтение и зацикливание сразу создает много проблем. В итоге мы сделали несколько вещей:

  1. Разделить файл на куски. Процесс импорта использует утилиту split.exe из cygwin, чтобы разбить файл на 4000 строк. Затем мы используем CFDIRECTORY, чтобы получить список блоков и обработать их по одному.

  2. Для каждого чанка мы читаем его, а затем разделяем содержимое файла на массив (используя listToArray () с chr (13) в качестве разделителя).

  3. Мы выполняем цикл от 1 до arrayLen (chunkArray), а не зацикливаемся непосредственно на содержимом файла. Это было сделано больше для скорости, чем что-либо еще. В этом цикле мы также разбиваем каждую строку на массив. Мы обнаружили, что делать это и получать доступ к значениям, так как thisRow [i] (где i - номер столбца в файле) было намного быстрее, чем многократный вызов listGetAt (). Файлы, которые мы импортируем, имеют более 90 столбцов.

  4. Мы увеличили допуск памяти JVM. Наши серверы довольно заняты, и это добавило некоторых накладных расходов. В итоге мы увеличили JVM до 32-битного сервера (около 2 ГБ), чтобы при необходимости была доступна память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...