![]() |
|
Модераторы: gambit |
![]()
|
|
| thomas |
|
|||
![]() Доцент... почти ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1385 Регистрация: 3.10.2006 Где: " Сказочное королевство" Репутация: 35 Всего: 65 |
Приветствую всех.
Ситуация. Написал программу, работающую с БД в Access. Программа создает в БД таблицы и заполняет их данными (список почтовых адресов). Т.е. в БД создается необходимая таблица, динамически указываются наименования и типы полей(на момент создания таблица пуста), далее в ДатаСет создается идентичная таблица и заполняется данными с генерацией первичного ключа(каждая строка имеет уникальный ID). Создается 30-40 тысяч строк. Сейчас реализовал вставку данных перебором строк таблицы ДатаСета через INSERT команду. На машине с CPU 1.4Гг и 1,25 гектара оперативной памяти эта операция занимает порядка 40 минут. Вопрос : Как ускорить вставку данных? -------------------- Крепко жму горло, искренне ваш Thomas. (С)vingrad Некоторые сорта флоры буквально за одно мгновение превращают нас в фауну! Проблемы негров шерифа не волнуют. |
|||
|
||||
| tol05 |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1632 Регистрация: 21.12.2006 Где: Харьков Репутация: 11 Всего: 170 |
thomas, ты же на форуме не первый день!..
"Я делаю таблицу и вставляю данные через INSERT. Почему так долго" Разве может тебе кто-нибудь ответить? Вряд ли тебя устроит ответ типа такого: "Нет, этого просто не может быть!!! MS SQL вместе с ADO.NET уже давно бы в трубу вылетели с такой производительностью. По миру пошли бы, даже мы с тобой их бы не купили..." Ну давай код показывай что-ли... Это сообщение отредактировал(а) tol05 - 25.11.2007, 15:08 -------------------- На хорошей работе и сны хорошие снятся. |
|||
|
||||
| farad |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 199 Регистрация: 1.11.2007 Репутация: 3 Всего: 6 |
через ADO.NET и insert - никак. Такой объем данных только через Ж. При работе с SQL Server создаю текстовый файл и потом с помощью BulkInsert делаю импорт данных. Даже с учетом такого гимороя производительность увеличивается на порядки. Такой же подход можно реализовать и в акцессе подняв его в проге через COM. Импорт из текстового файла акцесс делает так же очень на много быстрее... Возможно есть и менее кривые способы, хотелось бы узнать - тоже интересно
|
|||
|
||||
| unicuum |
|
||||||||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
Насмешили темой
Честно скажу не знаю какой способ быстрее, одно ясно, создавать отсоединённые объекты на типа DataSet полная глупость. База локальная, а DataSet жрёт оперативку гораздо больше чем занимает база на винте, плюс лишние операции. tol05, farad, вы не обратили внимание, что thomas использует локальную базу MS Access, а значит серверные базы MS SQL здесь вообще нипричём, в данном случае в ADO.NET применяется OleDB. Вот мне к тому же надо было не только загнать данные в БД, но ещё и выводить их в DataGridView, а там по запросу от 5 до 200 тысяч за раз приходит. А поступил я достаточно просто, открыл базу, прошёлся 10512000 по циклу и закрыл базу, что иллюстрирует кусок ниже.
dt имеет тип Date, gi, go, tp - Single, это аргументы передаваемые для ускорения по ссылке. А чтобы не терять времени базу данных открыл в другом месте, а здесь лишь вызвал то, что мне было нужно. Следующий код иллюстрирует как примерно вызывать такой метод.
Сразу оговорюсь, в изначальном виде код был немного другим, просто для улучшения восприятия идеи я его немного переделал для этого форума. О полях существующих в БД легко узнать по запросу, так что пояснять структуру таблицы не буду, так же не буду говорить о том как создавать запросами таблицы если их нет, это думаю и так понятно. Суть в следующем, когда программа пишет в базу, то никаких проблем нет, даже если она зависла, все равно не нужна, хотя это кому как. А вот если надо считывать потом в DataGridView, то подвисания просто не приемлимы. И тут на помощь пришёл BackgroundWorker, его можно и для записи использовать, хотя лично у меня не было необходимости. Не став играться с кучей классов отсоединённых объектов, я просто взял DataTable, и при помощи обработчиков событий объекта класса BackgroundWorker обновлял таблицу из запроса, впоследствии присваивая свойству DataSource объекта DataGridView новую таблицу, сбрасывая по умолчанию старую со всеми данными в мусор. Следующие куски кода частично показывают функциональность. часть кода на уровне модуля формы
часть кода в обработчике RunWorkerCompleted
часть кода из кнопки вызывающей DoWork, который и вызывает InvokeAsyncSelect
часть кода в InvokeAsyncSelect или говоря по простому в асинхронной выборке из БД обращаю внимание, что период (начальная и конечная даты) как и выше задаётся параметрами, просто в этом куске для наглядности изменено на строковые значения
Естественно когда идёт выборка кнопка её запускающая становится недоступной. В целом реализовано много чего ещё, но для первоначального понимания теми кто раньше не делал подобного хватит. Кстати сброс в мусор может и не лучший способ, но мне он показался очень быстрым, к тому же благодаря этому MyDataGridView воспринимает таблицу как новый источник, и обновляет всё сам. В целом повторюсь, не буду говорить что такой способ работы хорош, но из 10.5 миллионов записей выборка 5 тысяч делается за пару секунд, ну а вывод 200 тысяч записей секунд за тридцать, на вышеуказанном компьютере, я считаю вполне приемлимым, при этом прога естественно не виснет. И главное оператива не расходуется по пусту, потому что пытаться закачать в неё всю базу самоубийство. Ну, а вообще, я ещё новичок в базах, потому мне ещё набираться и набираться опыта, чтобы узнать самый эффективный способ работы с ними. И всё вышеперечисленное сделал можно сказать в первый раз, в том числе и асинхронный вызов запроса к БД. Надеюсь то, что сегодня написал будет кому-нибудь полезным, и плюсик мне плюсик -------------------- ![]() обычный день на винграде |
||||||||||||
|
|||||||||||||
| farad |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 199 Регистрация: 1.11.2007 Репутация: 3 Всего: 6 |
рад бы да форум не разрешает. а по теме код аналогичный тому что ты привел вставляет миллион строк около 25 минут. П4 2.2Ггц. Запускал вне отладчика - в отладчике не дождался когда загрузится. Тот же объем данных из текстового файла грузится 2секунды! Так что есть куда стремиться
вот код:
|
|||
|
||||
| unicuum |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
Меня заинтересовала не надолго тема ускорения, не то чтобы мне это надо, но...
В общем, я провёл исследования, пока что без чтения умных книг и прочих полезных источников. количество записей в примере - 1 051 200 6 - с транзакцией без параметров 40% 9 - с транзакцией с параметрами 60% 657 секунд (погрешность) + выбор по номеру 642 12 - без транзакции без параметров 80% 15 - без транзакции с параметрами 100% 1040 секунд 1. Вызов параметра не по строке, а по индексу почти не влияет на результат. 2. Использование в конце Select или Values ни на что не влияет. 3. Если убрать параметрический запрос и вместо него поставить фиксированные значения, плюс отменить сами параметры, то скорость возрастает аж на 20%, но в следствии того что параметры всё таки нужны это не имеет значения. 4. Внимание: если поставить транзакцию скорость возрастает на 40%!!! 5. С транзакцией и убиранием параметров скорость возрастает на 60%, но опять же в ущерб параметрам, что неприемлимо. А вывод, с транзакцией в моём примере в базу записывается 1 051 200 записей за 642 секунды (примерно 11 минут), то есть 1637 зап/сек. Тут следует учитывать что компьютер 2.2 ГГц не 3ГГц, но даже если предположить, что скорость записи пропорциональна частоте процессора (слишком смелое предположение А вот собственно и сам код, как всегда без исключений
Жаль, я то надеялся, что код farad окажется быстрее, но нет, ни первый, ни второй пункт себя не оправдали, по скорости он абсолютно такой же. Немного поюзал поисковик, есть какая-то штука INSERT BULK, надо его заюзать и тоже сюда результаты написать. В любом случае 10 млн записей добавятся примерно за 1 час 40 минут, что очень медленно учитывая что запрос по этим 10 млн идёт со скоростью копирования. Короче по сравнению с thomas с его 30-40 тысячами это хорошо, даже если бы на 3ГГц компьютере база заполнялась 18 минут, то общая скорость в лучшем случае составляла бы 37 зап/сек, против 1637 зап/сек, что в 44 раза медленней. А вообще база на 10 512 000 записей занимает 362 мегабайта и копируется секунд этак 18 или 584000 зап/сек, что в 356 раз быстрее от моей транзакции. Вывод: нужно изучать ускоренное добавление в БД, но я думаю не раньше чем оно реально понадобится. -------------------- ![]() обычный день на винграде |
|||
|
||||
| thomas |
|
||||||
![]() Доцент... почти ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1385 Регистрация: 3.10.2006 Где: " Сказочное королевство" Репутация: 35 Всего: 65 |
unicuum, farad
Приветствую. То что вы тут написали конечно заслуживает внимания. Но и вызывает вопросы. Для начала приведу пример для теста, который написал tol05.
Создается пустая таблица в БД. Далее в ДатаСет создается таблица, соответствующая таблице БД и заполняется данными. После этого вызывается метод Update для ДатаАдаптера. И Ву А Ля все данные закачаны в таблицу БД. Приведенные код написан для SQL сервера, но я у себя переделывал его под OleDb. farad, скорость такая же как и у тебя . Миллион строк за 25 минут, 40 тысяч за минуту (на моих 1,4 Ггерцах) А терерь вопросы. unicuum, Из твоего примера я понял, что в таблицу забиваются одни и теже данные 10 миллионов раз.
А что будет если нужно для каждой строки откуда-то уникальные данные считывать? Как это повлияет на скорость? И где ты эти 10 миллионов, готовых к заливке в БД, строк собираешься хранить? Ведь по твоим утверждениям
Ведь перед тем как заливать данные в БД их нужно как-то и где-то подготовить(создать, отредактировать). Они не появяться из неоткуда. При создании темы, я написал что использую команду INSERT для каждой строки. А потом задал вопрос "Как ускорить вставку данных? ". Возможно я не совсем развернуто его задал. Существуют ли другие методы вставить данные в пустую таблицу БД, кроме как INSERT команда? Можно ли за один раз "всем скопом" вставить данные в таблицу БД, а не построчно? С наилучшими пожеланиями Это сообщение отредактировал(а) thomas - 5.12.2007, 13:35 -------------------- Крепко жму горло, искренне ваш Thomas. (С)vingrad Некоторые сорта флоры буквально за одно мгновение превращают нас в фауну! Проблемы негров шерифа не волнуют. |
||||||
|
|||||||
| unicuum |
|
||||||||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
начнём попрядку
Там где написано
поставь
О результатах доложишь в этот раздел У меня комп есть Celeron 733 с 128 мегами оперативки, так вот перемолоть 40 тысяч записей ему раз плюнуть.
Скорость будет точно такая же, просто в InsertToDB введёшь свои аргументы. Заметь, я вообще не писал, что ввожу одинаковые данные, иначе зачем бы мне понадобилась функция с аргументами значений, которые помещаются в БД.
На диске в файле базы данных
У меня просто ситуация такая, для меня это оптимальный вариант, потому что данные генерируются спец. оборудованием по ходу дела. Ну а вообще если они уже где то есть, то никто тебе не мешает их вставлять. Можно запросом вынуть из какого-нибудь источника данных сразу много строк и добавить в БД. А можно из отсоединённых объектов закачивать, если ты их завёл. И вообще раз у тебя MS SQL надо точно INSERT BULK попробовать -------------------- ![]() обычный день на винграде |
||||||||||||
|
|||||||||||||
| farad |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 199 Регистрация: 1.11.2007 Репутация: 3 Всего: 6 |
имхо стандартными SQL командами низя. BULK INSERT, о чем упоминал unicuum и я предлагал как решение через ж.. доступна только в TSQL(это вообще-то вызов внешней утилиты поставляемой с SQL Server), возможно конечно есть аналоги и в других диалектах, но они не дадут возможности получить универсальное решение... |
|||
|
||||
| thomas |
|
|||
![]() Доцент... почти ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1385 Регистрация: 3.10.2006 Где: " Сказочное королевство" Репутация: 35 Всего: 65 |
Ай-ай кэптэн. А плюсик поставишь? -------------------- Крепко жму горло, искренне ваш Thomas. (С)vingrad Некоторые сорта флоры буквально за одно мгновение превращают нас в фауну! Проблемы негров шерифа не волнуют. |
|||
|
||||
| unicuum |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
Поставил
Добавлено через 4 минуты и 58 секунд thomas, кстати пока ответ на твой вопрос - использовать транзакцию. -------------------- ![]() обычный день на винграде |
|||
|
||||
| unicuum |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
Плюсик я тебе поставил, а о результатах ты так и не доложил Впрочем я и так могу сказать что будет, оперативка закончится и всё, ну на 10 лямах не закончится, так на 100 закончится, так что по любому хана, хотя такой способ записи в БД наверное самый быстрый. Добавлено через 19 секунд впрочем я не проверял -------------------- ![]() обычный день на винграде |
|||
|
||||
| D_F_V |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 50 Регистрация: 27.3.2007 Где: Vladivostok Репутация: нет Всего: 1 |
Полностью присоединяюсь к вопросу.... ИМХО 10 мл.. строк реальных данных так быстро ты не обработаешь без расхода памяти... тут или жертвовать памятью, или временем... данные в любом случае от кудато читать надо.. быстрее чем из памяти ты их не прочитаешь ведь так?
Если будешь подкачивать порциями (1, 10, 100... т.д..) то память конечно расходоваться не будет, но получишь совсем другие результаты по времени.... так что ИМХО тут не все так "красиво" в твоем примере... Это сообщение отредактировал(а) D_F_V - 12.12.2007, 02:59 |
|||
|
||||
| thomas |
|
||||||||
![]() Доцент... почти ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1385 Регистрация: 3.10.2006 Где: " Сказочное королевство" Репутация: 35 Всего: 65 |
Приветствую всех.
Вопрос решил. Процесс тормозило создание соединения с Базой. Было так
Этот код вызывается в форме приложения. А этот код находиться в классе, осуществляющем соединение и работу с БД
Тормоза были в том, что каждый раз при вставке строки открывалось и закрывалось соединение с базой. Переделал немного класс работы с БД
И соответственно изменил код в форме. Теперь сначала открываетм соединение с БД, далее построчно вставляем данные и закрываем соединение.
И в результате получили приемлимую скорость выполнения опреации (чуть больше минуты) заполнения таблицы БД данными(33000 адресов) и запись XML файла на основании этой таблицы. Всем спасибо за участие. С наилучшими пожеланиями. -------------------- Крепко жму горло, искренне ваш Thomas. (С)vingrad Некоторые сорта флоры буквально за одно мгновение превращают нас в фауну! Проблемы негров шерифа не волнуют. |
||||||||
|
|||||||||
| unicuum |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 830 Регистрация: 16.3.2005 Где: Рашка Репутация: нет Всего: 8 |
А у меня соединение сразу открывалось в другом месте, потом передавалось процедуре и при выходе из процедуры на уровень выше закрывалось, так что скорости это добавит твоему алгоритму, но не моему. Нужно вынести цикл вне процедуры, и тогда можно будет любые значения записывать, скорость останется примерно такой же, т.е. 10.5 млн строк за 11 минут, но есть сведения, что этот процесс можно ускорить в шесть раз, только я так и не понял как, так как в том примере была битая ссылка. У меня подозрения, что всё таки отсоединёными объектами. Для той программы которую я писал мой вариант наилучший, то есть там нужно именно присоединённо писать. Ну а для серваков которые гоняют инфу туда сюда, лучше вообще брать MS SQL 2005 и не мучаться, скорость будет как при записи файлов. Красивость тут ни причём, просто нужно выбирать вариант, вот и всё. -------------------- ![]() обычный день на винграде |
|||
|
||||
![]()
|
| Прежде чем создать тему, посмотрите сюда: | |
|
|
Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов. Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, stab, mr.DUDA, Exception. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Базы данных под .NET | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |