2008-11-29 3 views
2

Я пытаюсь написать программу VB.Net, которая экономит 1-2 миллиона записей с 5 полями (плюс индексированный идентификатор) в таблице MSAccess каждый день. Процесс сохранения в настоящее время занимает 13-20 часов, что, очевидно, не может быть прав.Медленная запись на диск MSAccess

Его плоский стол с минимальным индексированием, в настоящее время только 156 МБ. За исключением одного двойного поля, поля представляют собой небольшие строки, даты или длинные. Сам диск представляет собой 15 000 SATA, которые используются только для этого файла. Компьютер и программа ничего не делают во время процедуры сохранения. Процедура сохранения - это простой цикл FOR-NEXT, который выдает короткий и простой оператор INSERT для каждой записи в наборе данных.

У кого-нибудь есть идеи о том, что мне нужно изменить, чтобы заставить это работать лучше?

+0

Нужно написать одну запись время, то есть, является добавлением запроса какого-либо описания неприемлемым? Вы каждый раз сжимаете базу данных до добавления? У этой ссылки могут быть некоторые подсказки: http://www.granite.ab.ca/access/performancefaq.htm – Fionnuala 2008-11-29 17:00:17

ответ

0

(Отказ от ответственности: я ничего не знаю много о доступе)

Что сказать, какие средства профилирования? (диспетчер задач даст вам несколько подсказок - добавьте больше столбцов на дисплей, чтобы увидеть входы/выходы, использование VM и т. д.)? Делает ли он много доступа к диску, или это весь процессор? Он потребляет огромное количество памяти?

Упоминание об индексе касается меня, поскольку потенциально это необходимо для обновления после каждого INSERT - можете ли вы отключить индекс, сделать создание, а затем проиндексировать полный файл?

Является ли линейным во времени? (т. е. если вы создаете файл размером 10% от размера, он занимает 10% времени)?

0

Спасибо за ваши вопросы, Пол.

Использование файла - 600 МБ, процессор - около 5% в большинстве случаев, с шипами до 80% диапазона каждые 20 секунд или около того. Память: всего 2G, доступно 1.3G, системный кеш - 1G.

Да, это кажется линейным, первые 15 000 записей занимают 10 минут.

В соответствии с индексом я этого не пробовал, но Access всегда жалуется, если вы не указали хотя бы поле ID.

Чтение ввода-вывода выглядит очень много, хотя почти через 6 минут после 20 минут работы и всего 25 000 записей.

0

Сначала попробуйте использовать один из многих вариантов импорта в Access. (Откуда берутся данные? Размещено ли оно или длина фейса? Как вы разбираете его с помощью VB?)

Вы должны иметь возможность создавать таблицу без индекса. путем отказа от предложения Access добавить его. Но сначала работайте с Импорт.

0

Спасибо, Doofledorfer.

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

+0

Есть виджет-импорт, который проходит через него. Или опубликуйте пару строк данных. – dkretz 2008-11-29 17:34:20

0

Вы можете попробовать записать данные в файл CSV, а затем данные должны быть легко импортированы в доступ с помощью одной команды. Это может значительно ускорить процесс.

0

Сколько соединений вы открыли для базы данных MSAccess (mdb)? Из того, что я видел, однопользовательский режим значительно быстрее, чем многопользовательский режим. Любая открытая программа MS Access рассчитывается как одно соединение (вы можете просмотреть файл ldb с помощью инструмента ldbview).

Используете ли вы блокировку на уровне строк или страниц? Начиная с некоторой версии Access (2000?), По умолчанию используется блокировка на уровне строк; Я предполагаю, что блокировка на уровне страницы будет быстрее.

У вас нет антивируса? Они могут перехватывать файловые операции и заметно замедлять весь процесс.

0

Открывается только одно соединение, и это однопользовательская система.

Пробовал отключить мой антивирус, но он сохраняет только 800 записей в секунду.

Я не знаком с «замок», но моя строка соединения OLEDB содержит «Блокировка Режим = 1»

Кстати, если его отношение, размер VM вырос до 157,000K после всего нескольких минут.

0

Вот несколько примечаний к запросу append для текстового файла с разделителями, VBScript, боюсь, но это может помочь.

Set cn = CreateObject("ADODB.Connection") 
strFile="C:\ltd.mdb" 
strCon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _ 
& strFile & ";" 

cn.Open strCon 

strSQL="INSERT INTO tableX (Name1,Name2) " _ 
& "SELECT Name1,Name2 " _ 
& "FROM [ltd.txt] IN '' [Text;Database=c:\docs\;HDR=YES;]" 

cn.Execute strSQL 
0

Try Locking mode = 0 - это уровень страницы. 00 rps (записи в секунду) дает 480000 записей за 10 минут - вы имели в виду 800 оборотов в минуту?

0

Майк, вирусный сканер отключил улучшенные вещи - но что-то все еще звучит из-за удара ... Согласитесь с @Remou, что объемная вставка будет намного лучше выполнять, если вы сможете это сделать.

Предполагая, что вы не можете вставлять вставки, просто сделал пример приложения (C#, извините - но VB.NET будет похож), чтобы создать каталог, таблицу и выполнить некоторые вставки. На данный момент я пропустил ограничение PK на ID.

Это давало мне около 1000 строк в 550 мс, работающих на виртуальной машине с антивирусным сканером на ноутбуке, работающем под управлением. Вы сможете легко справиться с этим быстрым приводом. Где различия?

Одна мысль - как вы вставляете ID? Автогенерировано и опущено из инструкции INSERT? Или вы вставили значение, а столбец обозначен PK? Последнее, несомненно, вызовет поиск индекса (ваш значительный IO?), Чтобы проверить уникальность данных по таблице?

using System; 
using System.Data; 
using System.Data.OleDb; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      String jetConnection = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\\jetsample.mdb;"; 

      ADOX.CatalogClass cat = new ADOX.CatalogClass(); 
      cat.Create(jetConnection); 

      using(OleDbConnection conn = new OleDbConnection(jetConnection)) 
      { 
       conn.Open(); 
       using(OleDbCommand cmd = new OleDbCommand("CREATE TABLE test ([ID] INTEGER, [TestDouble] DOUBLE, [TestText] TEXT, [TestDate] DATE, [TestInt] INTEGER)",conn)) 
       { 
        cmd.CommandType = CommandType.Text; 
        cmd.ExecuteNonQuery(); 
       } 

       using (OleDbCommand cmd = new OleDbCommand("INSERT INTO [Test] VALUES (@id, @testDouble, @testText, @testDate, @testInt)", conn)) 
       { 
        OleDbParameter id = cmd.Parameters.Add("@id", OleDbType.Integer); 
        OleDbParameter testDouble = cmd.Parameters.Add("@testDouble", OleDbType.Double); 
        OleDbParameter testText = cmd.Parameters.Add("@testText", OleDbType.VarWChar); 
        OleDbParameter testDate = cmd.Parameters.Add("@testDate", OleDbType.Date); 
        OleDbParameter testInt = cmd.Parameters.Add("@testInt", OleDbType.Integer); 

        DateTime start = DateTime.Now; 
        for (int index = 1; index <= 2000000; index++) 
        { 
         if (index % 1000 == 0) 
         { 
          System.Diagnostics.Debug.WriteLine(((TimeSpan)(DateTime.Now - start)).Milliseconds); 
          start = DateTime.Now; 
         } 

         id.Value = index; 
         testDouble.Value = index; 
         testText.Value = String.Format("{0} DBL", index); 
         testDate.Value = DateTime.Now.AddMilliseconds(index); 
         testInt.Value = index; 

         cmd.ExecuteNonQuery(); 
        } 
       } 
      } 

     } 
    } 
} 
0

ARVO; Отключение антивирусного сканера и переход в режим блокировки = 0 помогли ... его до 1300 записей в минуту (да, я имел в виду минуты), но это все еще довольно медленно.

STEPHBU: мои навыки C минимальны, но, насколько я могу видеть, что вы делаете вещи почти так же, как и я Ваш «JetConnection» строка значительно проще, чем у меня ... Вот мой

Me.connSPY.ConnectionString = "Jet OLEDB:Global Partial Bulk Ops=2;" & _ 
    "Jet OLEDB:Registry Path=;Jet OLEDB:" & _ 
    "Database Locking Mode=0;" & _ 
    "Data Source=""E:\SPIRE.mdb"";" & _ 
    "Mode=Share Deny None;" & _ 
    "Jet OLEDB:Engine Type=5;" & _ 
    "Provider=""Microsoft.Jet.OLEDB.4.0"";" & _ 
    "Jet OLEDB:System database=;" & _ 
    "Jet OLEDB:SFP=False" & _ 
    ";persist security info=False;" & _ 
    "Extended Properties=;" & _ 
    "Jet OLEDB:Compact Without Replica Repair=False;" & _ 
    "Jet OLEDB:Encrypt Database=False;" & _ 
    "Jet OLEDB:Create System Database=False;" & _ 
    "Jet OLEDB:Don't Copy Locale on Compact=False;" & _ 
    "User ID=Admin;" & _ 
    "Jet OLEDB:Global Bulk Transactions=1" 
+0

Если вы используете метод массового импорта - убедитесь, что для глобальных массовых транзакций установлено значение 2. http://msdn.microsoft.com/en-us/library/aa140022(office.10).aspx – stephbu 2008-11-29 18:42:27

0

ops, пропустил один из ваших вопросов STEPHBU ... Я разрешаю индексированный идентификатор автоматически увеличиваться, а не пытаться назначить его в инструкции INSERT. Но хорошая мысль!

+0

Я изменил свой table, чтобы добавить ограничение PK в таблицу - с PK моя производительность вставки была на 10% медленнее на пустой таблице и снизилась на 30% к моменту достижения 200K строк. Если это возможно, пропустите клавиши/ограничения. Я все равно поеду с решением Рему, если сможешь. – stephbu 2008-11-29 18:35:34

1

Вы должны действительно управлять объемной вставкой. Каждая вставка имеет кучу накладных расходов и, делая одну строку за раз в следующем цикле, вы теряете больше 2/3rds мощности компьютера. Если данные поступают в одну строку за раз, вам нужно будет создать буфер для его сбора, прежде чем вставлять массив в базу данных. Кибби предложил записать данные в файл csv, а затем сбросить их в базу данных, и если вам нужно записать данные, это хороший метод.Я бы рекомендовал собирать данные в памяти в течение нескольких минут за раз.

2

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

В частности, индексы могут быть убийцами производительности для последовательной инсерции, они быстрее, по крайней мере, на порядок (иногда 2!), Чтобы заполнить таблицу сначала, а затем создать индекс по уже заполненным данным, чем вставить с индексом на месте. В этом случае вам может потребоваться отбросить индекс, а затем воссоздать его.

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

И тогда вы могли бы получить еще один крошечный прирост с помощью DAO списков записей вместо ADO - я заметил это еще в те дни, когда я разработал в VB6, наверное, это не тот случай больше с ADO.NET

1

Хорошо, назад от долгого обеда.

PAUL, PINEDA, ваши предложения по индексированию ПК были проблемой. Избавьтесь от индекса и вдруг он хранит 40 000 записей в минуту, достаточно быстро, чтобы сделать целый день в течение часа. И это не влияет на скорость приложений, которые используют данные вообще.

Остальные из вас, щедрые люди ... Я буду отвлекаться на ваши предложения до конца дня, и, надеюсь, получить его еще лучше.

Вы были чрезвычайно полезны. Я все тебе пива.

0

Doofledorfer: здесь 5 строк ввода образца, как вы просили, хотя я думаю, что я нахожусь на правильном пути теперь с блоком вставки предложения и первичного ключа неиндексированных

INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume) 
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.63, 200); 

INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume) 
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 400); 

INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume) 
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 100); 

INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume) 
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 300); 

INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume) 
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 127); 
0

1 -2 миллиона записей из 5 полей (плюс индексированный идентификатор) в таблицу MSAccess каждый день.

С риском сообщить, что должно быть очевидно. Вы решаете неправильную проблему.

Дамп MS-Access и нажимайте их на сервер MS-SQL. Если вы ДЕЙСТВИТЕЛЬНО должны получить доступ к данным позже через MS-Access, просто создайте ссылку таблицы на таблицу сервера MS-SQL.

Даже в 5 байт на запись вы превысите ограничение на размер таблицы/размер базы данных MS Access 2003 за 2 года (к сожалению, это та же история для MS Access 2007).

+0

Действительно? В самом деле? -1-х? Вы считаете, что я ошибаюсь в том, что это плохая идея сбросить 2 миллиона записей в день в MS Access. Даже при 5 байтах на запись вы превысите лимит Ms Access 2003 на 2 ГБ менее чем за год (2007 год, одна и та же история). – BIBD 2008-12-02 15:54:15

+0

У кого-то был одуванчик. Несмотря на то, что Майк Мойл сказал, что я (и Пинеда) правильно определил проблему ... – 2008-12-02 19:35:02

+0

Там ... Я аннулировал три из них. – BIBD 2008-12-02 21:05:45

1

У вас включен режим автообмена?

Это действительно затормозило бы вас, так как каждая вставка должна была быть физически записана на диск до того, как будет обработана следующая.

Попробуйте вручную ввести около 1000 вставок.

0

CodeSlave .... Это действительная точка, и если все это сработает, мне придется потратить деньги на SQL Server или что-то подобное, не говоря уже о еще нескольких компьютерах.Пока я не хочу вкладывать деньги или кривую обучения.

Mr Anderson ... Пока не пробовал, и я это сделаю. Но на данный момент, другие предложения имеют мое 10-20-часовое экономия времени до 15 минут, так что я довольно счастливый турист.

0

Предлагаю использовать метод индексированного последовательного доступа - ISAM. Он позволяет напрямую передавать данные из подключенного источника записи во второй динамически подключенный источник данных.

  1. Используйте OleDbConnection или подобный предмет, чтобы открыть соединение

  2. Выполнить запрос от подключения с использованием формата ISAM вместо ОТ

Sytax, как это:

private void PopulateMDB(string ExportPath, int iID) 
{ 

    string cnnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ExportPath; 
    OleDbConnection oConn = new OleDbConnection(cnnStr); 
    string q = @"INSERT 
       INTO PensionData ([ID] 
         ,[Recipient Name] 
         ,[Gross Amt] 
         ,[Retirement Date] 
         ,[Plan]) 

      select id as [ID] 
        ,name as [Recipient Name] 
        ,gross_amt as [Gross Amt] 
        ,eff_dt as [Retirement Date] 
        ,pln as [Plan] 
       FROM [ODBC;Driver=SQL Server;SERVER=euddbms.d;DATABASE=DBName;UID=;PWD=;].tableName 
      WHERE id = " + iID; 

    oConn.Open(); 

    try 
    { 
    OleDbCommand oCmd = new OleDbCommand(q, oConn); 

    oCmd.ExecuteNonQuery(); 
    } 
    catch (Exception ex) 
    { 
    throw ex; 
    } 
    finally 
    { 
    oConn.Close(); 
    oConn = null; 
    } 

} 

ISAM материал::

CSV [Текст; Database = C: _path \; HDR = Yes]. [FILE.CSV]

Доступ [MS Access; Database = C: \ Путь \ файлу.mdb] [AccessTableName]

Excel [Excel 8.0; HDR = Yes; IMEX = 1; MaxScanRows = 16; Database = C:.. \ Путь \ File.xls] [Таблица $ ]

SQL Server [ODBC; Driver = SQL Server; SERVER =; DATABASE =; UID =; PWD =;]. [Таблица]

http://support.microsoft.com/kb/321686 http://support.microsoft.com/kb/200427

Смежные вопросы