使用C#将大数据导入性能提升到SQLite

Jon*_*Jon 18 .net c# sqlite system.data.sqlite

我正在使用C#导入一个包含6-8百万行的CSV.

我的表看起来像这样:

CREATE TABLE [Data] ([ID] VARCHAR(100)  NULL,[Raw] VARCHAR(200)  NULL)
CREATE INDEX IDLookup ON Data(ID ASC)
Run Code Online (Sandbox Code Playgroud)

我正在使用System.Data.SQLite来进行导入.

目前在Windows 7 32位,Core2Duo 2.8Ghz和4GB RAM上耗时6分55秒.这不是太糟糕,但我只是想知道是否有人能够更快地看到导入它的方法.

这是我的代码:

public class Data
{
  public string IDData { get; set; }
  public string RawData { get; set; }
}   

string connectionString = @"Data Source=" + Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + "\\dbimport");
System.Data.SQLite.SQLiteConnection conn = new System.Data.SQLite.SQLiteConnection(connectionString);
conn.Open();

//Dropping and recreating the table seems to be the quickest way to get old data removed
System.Data.SQLite.SQLiteCommand command = new System.Data.SQLite.SQLiteCommand(conn);
command.CommandText = "DROP TABLE Data";
command.ExecuteNonQuery();
command.CommandText = @"CREATE TABLE [Data] ([ID] VARCHAR(100)  NULL,[Raw] VARCHAR(200)  NULL)";
command.ExecuteNonQuery();
command.CommandText = "CREATE INDEX IDLookup ON Data(ID ASC)";
command.ExecuteNonQuery();

string insertText = "INSERT INTO Data (ID,RAW) VALUES(@P0,@P1)";

SQLiteTransaction trans = conn.BeginTransaction();
command.Transaction = trans;

command.CommandText = insertText;
Stopwatch sw = new Stopwatch();
sw.Start();
using (CsvReader csv = new CsvReader(new StreamReader(@"C:\Data.txt"), false))
{
   var f = csv.Select(x => new Data() { IDData = x[27], RawData = String.Join(",", x.Take(24)) });

   foreach (var item in f)
   {
      command.Parameters.AddWithValue("@P0", item.IDData);
      command.Parameters.AddWithValue("@P1", item.RawData);
      command.ExecuteNonQuery();
   }
 }
 trans.Commit();
 sw.Stop();
 Debug.WriteLine(sw.Elapsed.Minutes + "Min(s) " + sw.Elapsed.Seconds + "Sec(s)");
 conn.Close();
Run Code Online (Sandbox Code Playgroud)

Cza*_*zak 12

600万条记录的速度非常快.

看来你正在以正确的方式做到这一点,前段时间我在sqlite.org上看到,在插入记录时你需要将这些插入放在事务中,如果不这样做,你的插入将仅限于60每秒!这是因为每个插入都将被视为一个单独的事务,每个事务必须等待磁盘完全旋转.你可以在这里阅读完整的解释:

http://www.sqlite.org/faq.html#q19

实际上,SQLite很容易在普通的台式计算机上每秒执行50,000或更多INSERT语句.但它每秒只会进行几十次交易.交易速度受磁盘驱动器转速的限制.事务通常需要两个完整的磁盘盘旋转,这在7200RPM磁盘驱动器上限制您每秒约60个事务.

将您的时间与上述平均值进行比较:每秒50,000次=>应该花费2m 00秒.这比你的时间快一点.

事务速度受磁盘驱动器速度的限制,因为(默认情况下)SQLite实际上等待,直到事务完成之前数据确实安全地存储在磁盘表面上.这样,如果您突然断电或者操作系统崩溃,您的数据仍然是安全的.有关详细信息,请阅读SQLite中的原子提交.

默认情况下,每个INSERT语句都是自己的事务.但是如果用BEGIN ... COMMIT包围多个INSERT语句,则所有插入都被分组到一个事务中.提交事务所需的时间在所有随附的insert语句中分摊,因此每个insert语句的时间大大减少.

下一段中有一些提示可以尝试加速插入:

另一种选择是运行PRAGMA synchronous = OFF.此命令将导致SQLite不等待数据到达磁盘表面,这将使写入操作看起来更快.但是,如果在事务中间断电,则数据库文件可能会损坏.

我一直认为SQLite是为"简单的东西"而设计的,在我看来,600万条记录对于像MySQL这样的真实数据库服务器来说是一项工作.

计算具有如此多记录的SQLite表中的记录可能需要很长时间,仅为了您的信息,而不是使用SELECT COUNT(*),您总是可以使用非常快的SELECT MAX(rowid),但如果你正在删除该表中的记录.

编辑.

正如Mike Woodhouse所说,在插入记录后创建索引应该加速整个事情,这是其他数据库中的常见建议,但不能确定它在SQLite中是如何工作的.


Mik*_*use 6

您可能尝试的一件事是在插入数据创建索引- 通常,数据库在单个操作中构建索引要比在每次插入(或事务)后更新索引要快得多.

我不能说它肯定会与SQLite一起使用,但因为它只需要两行来移动它值得尝试.

我也想知道600万行事务是否会走得太远 - 您是否可以更改代码以尝试不同的事务大小?说100,1000,10000,100000?有"甜蜜点"吗?