Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Запись в DB, интенсивного потока данных 
:(
    Опции темы
COVD
Дата 8.2.2006, 19:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Вот столкнулся с интересной задачей.

Java приложение получает через сокет интенсивный (до несколько тысяч строк в секунду) поток данных и эти строки надо вставлять в таблицу базы данных.

Вставлять по одной строке через INSERT.. невозможно, так как база не успевает и данные теряются. Однако есть команда BULK INSERT [table] FROM [имя файла], которая работает раз в 10 быстрее. Поэтому возможно решить задачу путем записи потока в файлы (например, открывать ежесекундно новый файл) и закачивать каждый файл в базу. Таким образом данные будут поступать в базу порциями и с некоторой задержкой.

Такой подход работает, но возникают проблемы синхронизации - некоторые файлы база не может найти и т.д. Очевидно, что это решаемо, но требует больших усилий.

Какие другие подходы здесь возможны?
PM MAIL   Вверх
sergejzr
Дата 8.2.2006, 20:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 6
Всего: 360



А кэшировать в памяти никак? smile

Вообще отдельные insertы не обязательно писать.
Код

CREATE table xxx (year integer, name varchar(20) );
INSERT INTO xxx VALUES(3,'aaa',4,'asdfasdfas',5,'uuuuuu',3,'aaa',4,'asdfasdfas',5,'uuuuuu');

это создаст тебе 6 строк сразу.

Если данные приходят с паузами и операционки хватеае - кэшируй в памяти. даже 100 МБ это очень много строк, а памяти - ерунда.


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
COVD
Дата 8.2.2006, 20:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Такой инсерт, наверное, не всеми базами поддерживается? На MSSQL так не получается.
За идею спасибо ! Но кеширование на диске выглядит предпочтительнее.

Это сообщение отредактировал(а) COVD - 8.2.2006, 20:32
PM MAIL   Вверх
sergejzr
Дата 8.2.2006, 20:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 6
Всего: 360



Цитата(COVD @ 8.2.2006, 18:27 Найти цитируемый пост)

Но кеширование на диске выглядит предпочтительнее.

Можно поинтересоваться, почему? Диск в сотни раз медленнее памяти. Если боитесь потери данных - так на то трансакции и существуют.
Как вариант для вашего решения - просто файлы в памяти. Хотя простое кэширование лучше конечно.


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
Stampede
Дата 8.2.2006, 20:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Гносеолог
**


Профиль
Группа: Участник Клуба
Сообщений: 963
Регистрация: 25.4.2005
Где: Calgary, Alberta, Canada

Репутация: 24
Всего: 144



COVD, попробуй использовать пакетный режим, предусмотренный JDBC:

Сначала команды напихиваются в пакет, а потом дружно отправляются не сервер:


Код

int BATCH_SIZE = 100;

List list = getRecordList();

for (int i = 0, n = list.size(); i < n; i += BATCH_SIZE) {
  int k = i + BATCH_SIZE;
  k = (k > n)? n : k;
  Statement stm = con.createStatement();
  for (int j = i; j < k; j++) {
    Record rec = (Record) list.get(j);
    String insert = getInsert(rec);
    stm.addBatch(insert);
  }
  stm.executeUpdate();
  stm.close();
}



В случае использования PreparedStatement алгоритм примерно тот же, только во внутреннем цикла нужно добавить код по присвоению параметров, а вместо stm.addBatch(String sql) использовать беспараметрный pstm.addBatch().

Попробуй поиграться с размером пакета и посмотри, насколько будет быстрее. Ну и ним сообщи, нам тоже интересно smile

Это сообщение отредактировал(а) Stampede - 8.2.2006, 20:56


--------------------
"If you want something done right, do it yourself"
По секрету: выучить английский - реально!
PM WWW   Вверх
COVD
Дата 8.2.2006, 21:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Цитата(sergej.z @ 8.2.2006, 20:42)
Цитата(COVD @  8.2.2006,  18:27 Найти цитируемый пост)

Но кеширование на диске выглядит предпочтительнее.

Можно поинтересоваться, почему? Диск в сотни раз медленнее памяти. Если боитесь потери данных - так на то трансакции и существуют.
Как вариант для вашего решения - просто файлы в памяти. Хотя простое кэширование лучше конечно.

Я бы кешировал в памяти, но из памяти быстро в базу не получается вставить. Конструкция вставки нескольких строк одной командой INSERT у меня не получается, а BULK только под файлы заточен. А скорость записи из java в файл меня вполне устраивает. И заодно решается проблема бекапа данных и некоторые другие.
PM MAIL   Вверх
COVD
Дата 8.2.2006, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Цитата(Stampede @ 8.2.2006, 20:54)
COVD, попробуй использовать пакетный режим, предусмотренный JDBC:

Сначала команды напихиваются в пакет, а потом дружно отправляются не сервер:


Код

int BATCH_SIZE = 100;

List list = getRecordList();

for (int i = 0, n = list.size(); i < n; i += BATCH_SIZE) {
  int k = i + BATCH_SIZE;
  k = (k > n)? n : k;
  Statement stm = con.createStatement();
  for (int j = i; j < k; j++) {
    Record rec = (Record) list.get(j);
    String insert = getInsert(rec);
    stm.addBatch(insert);
  }
  stm.executeUpdate();
  stm.close();
}



В случае использования PreparedStatement алгоритм примерно тот же, только во внутреннем цикла нужно добавить код по присвоению параметров, а вместо stm.addBatch(String sql) использовать беспараметрный pstm.addBatch().

Попробуй поиграться с размером пакета и посмотри, насколько будет быстрее. Ну и ним сообщи, нам тоже интересно smile

Спасибо, надо посмотреть.

Я уже сравнивал скорость вставки строк в таблицу через обычный Statement, Prepared и Callable. Измерял время вставки 1000 одинаковых строк. К своему удивлению обнаружил что разницы ощутимой нет. И даже от количества параметров не зависит - что 1, что 20. Обычно первый эксперимент длился несколько дольше, скажем 1000 мс, а все последующие около 800 мс. Очевидно, умный ДБ сервер кэширует запросы.

Вместе с тем, запись в файл и вставка в таблицу через BULK INSERT проходят в 8-10 раз быстрее. Из чего я делаю вывод, что основные тормоза из-за того, что вставка через команду INSERT осуществляется в транзакции, которую отключить нельзя иначе как только применив BULK.

Если batch вставка выполняется как одна транзакция, то возможно и будет эффект.
PM MAIL   Вверх
LSD
Дата 8.2.2006, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(COVD @ 8.2.2006, 21:58 Найти цитируемый пост)
Вместе с тем, запись в файл и вставка в таблицу через BULK INSERT проходят в 8-10 раз быстрее. Из чего я делаю вывод, что основные тормоза из-за того, что вставка через команду INSERT осуществляется в транзакции, которую отключить нельзя иначе как только применив BULK.

Не знаю, что за СУБД, но вообще это отключается Connection.setAutoCommit(false), а потом ручной commit.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
sergejzr
Дата 8.2.2006, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 6
Всего: 360



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

В это мслучае надо попробовать начать транзакцию самому (ну и закончить тоже, конечно) когда надо

По идее это можно через SQL (не знаю насчёт MSSQL)

execute("BEGIN");
execute("INSERT...");
:
:
:
execute("COMMIT");


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
LSD
Дата 8.2.2006, 22:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(sergej.z @ 8.2.2006, 22:08 Найти цитируемый пост)
Другая возможность - транзакция вообще не была открыта и поэтому при каждом запросе BEGIN/COMMIT происходит заного.

Я честно говоря не знаю ни одной СУБД с поддержкой транзакций которая не начинала бы транзакцию при изменении данных.

В JDBC принудительно начать транзакцию можно connection.setSavepoint().


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
COVD
Дата 8.2.2006, 22:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



В книге по MSSQL написано что единственная возможность отключить транзакции - это перестартовать сервер в режиме что-то вроде SingleUserMode, т.е. это не рабочий режим.
PM MAIL   Вверх
batigoal
Дата 8.2.2006, 22:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 24
Всего: 151



Цитата(LSD @ 8.2.2006, 23:01 Найти цитируемый пост)

Не знаю, что за СУБД, но вообще это отключается Connection.setAutoCommit(false), а потом ручной commit.

А он по умолчанию true??? smile
(у меня появилась одна идея насчет геморроя на работе)


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
Stampede
Дата 8.2.2006, 22:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Гносеолог
**


Профиль
Группа: Участник Клуба
Сообщений: 963
Регистрация: 25.4.2005
Где: Calgary, Alberta, Canada

Репутация: 24
Всего: 144



Цитата(LSD @ 8.2.2006, 22:13 Найти цитируемый пост)

Я честно говоря не знаю ни одной СУБД с поддержкой транзакций которая не начинала бы транзакцию при изменении данных.


Крме того, явное выполнение команд BEGIN TRANSACTION и COMMIT может привести к непредсказуемым результатам. В JDBC на самом деле делать нужно так:

Прежде всего, нужно указать режим автокоммита: Connectuin.setAutoCommit(boolean b). По умолчанию он стоит в true, то есть каждая выполняемая команда инициирует новую транзакцию, которая по выполнении команды автоматически фиксируется. Тут важно отметить, что завершение транзакция знаменует не только сохранение изменений, но и ряд других важных действий, таких как освобождение выделенных ресурсов и - внимание! - снятие удерживаемых блокировок.

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

Почему, в таком случае, дефолтным режимом сделан автокоммит? А это сделано в расчете на начинающих, которые пробуют всякие датабазные примеры "хелло ворлд" - чтобы у них не случилось конфуза: вроде данные вставлял, а результатов не видно.

Другое дело, что у этих же начинающих потом происходит конфуз другого рода, когда они узнают, что при вставке тысячи записей у них инициируется тысяча транзакций, но, похоже, в Sun посчитали, что это меньшее зло.

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

Код

con.commit();


Обращаю внимание, не stm.execute("commit"), а посредством специально предусмотренного метода в JDBC API. Это нужно для того, чтобы и драйвер тоже знал, что транзакция - завершается. Потому что у драйвера - свое хозяйство, и его тоже надо содержать в чистоте и порядке smile

Вот так вот, если вкратце.

PM WWW   Вверх
LSD
Дата 8.2.2006, 22:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(Lamer George @ 8.2.2006, 22:47 Найти цитируемый пост)
А он по умолчанию true???

Да.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
COVD
Дата 9.2.2006, 00:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Прогнал тесты.

Время вставки 500 строк:

1. 843 мс - 500 раз выполнил st.executeUpdate("INSERT ..");
2. 532 мс - 500 раз выполнил st.executeUpdate("INSERT .."); но перед циклом сделал st.getConnection().setAutoCommit(false); а после цикла st.getConnection().сommit();
3. 390 мс - 500 раз выполнил st.addBatch("INSERT.."); и после цикла st.executeBatch(). Также отключил и сделал коммит. Если коммит в конце не делать, то быстрее на 10 %

Для 1000 строк:

1. 1300 ms
2. 900 ms
3. 400

Для 2000 строк:

1. 2800
2. 1500
3. 800

Таким образом, на MSSQL отключение транзакции средствами JDBC убыстряет запись в 1,5 - 2 раза. Batch вставка быстрее в 2 - 4 раза, причем отключение транзакции здесь существенно не влияет (убыстрение около 10 %).

Спасибо всем участникам обсуждения, особенно Stampede'у. smile

Это сообщение отредактировал(а) COVD - 9.2.2006, 00:40
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1138 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.