【问题标题】:Visual FoxPro OLE DB Performance issues looping on DBF fileVisual FoxPro OLE DB 性能问题在 DBF 文件上循环
【发布时间】:2012-07-19 15:40:59
【问题描述】:

我有一个超过一百万行的 .DBF 文件(嗯,6 个具有相同结构的 DBF 文件)。我还有一个 C# 应用程序,它使用我们制作的 API 将这些数据迁移到 SQL 数据库中。

程序启动很快,每秒可以处理 30 或 40 行,但随着时间的推移逐渐变慢,我不知道为什么。我相信我处理物品的速度很好。

我在 vfpoledb 中使用的连接字符串是

"Provider=vfpoledb;Data Source=" + sourceDBFolder + ";Collating Sequence=machine;MVCOUNT=32000;ENGINEBEHAVIOR=90;TABLEVALIDATE=0;REFRESH=0";

其中 sourceDBFolder 是磁盘上的路径。在开始任务之前我还执行了以下代码:

System.Data.OleDb.OleDbCommand oRefreshCommand = oConn.CreateCommand();
oRefreshCommand.CommandText = "EXECSCRIPT([SET REFRESH TO 0,0])";
oRefreshCommand.ExecuteNonQuery();

这里是相关代码。 select语句中的where子句如果不按下面的顺序排列会造成瓶颈。

string[] noteTables = new string[] { "note1", "note2", "note3", "note4", "note5", "note6" };
foreach (long lNoteKey in oCaseLookupTable.Keys) {
  for (int y = 0; y <= 5; y++) {
    System.Data.OleDb.OleDbCommand oNotesCmd = oConn.CreateCommand();
    oNotesCmd.CommandText = "SELECT NOTEDATE, NOTEDESC, ENTEREDBY FROM " + noteTables[y] + " WHERE NOTEPOINT = " + lNoteKey.ToString() + " AND NOTEDESC NOT LIKE 'Folder accessed%'";
    DataTable oNotesTable = new DataTable();
    oNotesTable.Load(oNotesCmd.ExecuteReader());
    foreach (DataRow oRow in oNotesTable.Rows) { 
      //Do processing on rows, Note is my created class.
      Note oNote = new Note();
      oNote.NoteValue = oRow["NOTEDESC"].ToString().Trim();
      oNote.ReferenceID = oCaseLookupTable[lNoteKey];
      DateTime createdDate;
      if (DateTime.TryParse(oRow["NOTEDATE"].ToString().Trim(), out createdDate))
        oNote.CreatedDate = createdDate;
      else
        oNote.CreatedDate = DateTime.Now;
      Result oNoteResult = oNote.Insert();
    }
    oNotesTable.Dispose();
    oNotesCmd.Dispose();
  }
}

简单地说,我不明白为什么这会越来越慢。 PerfMon 没有显示任何随时间增长的托管内存块。我尝试通过连续调用 DBF 文件来保持我的 DataTable 很小。一般来说,查询返回的最大行数是 1000。

【问题讨论】:

  • 你为什么不通过一个选择来做到这一点,例如:WHERE (NOTEPOINT = 'note1' or NOTEPOINT = 'note2'....) AND NOTEDESC NOT LIKE ...
  • 好吧,oCaseLookupTable.Keys 集合中有大约 6300 个值。我想我可以只为每个表使用一个选择,抓取该表中的所有行,如果我在我的 Keys 集合中找不到匹配项,则跳过该行,但我以前这样做过,它同样慢.
  • ONote.Insert 是做什么的?似乎插入中可能发生了导致速度变慢的事情。是否有一些查询运行时间越长,越多的记录放入 SQL 表中?
  • oNote.Insert 创建一个新的 SqlConnection 和 SqlCommand,并执行 SqlCommand.ExecuteScalar。命令本身由两部分组成:一个包含大约 10 列的基本插入语句,然后调用 SELECT SCOPE_IDENTITY() 以获取我们刚刚输入的 Note 的 ID 值(在这种情况下实际上不需要,所以如果这是导致减速的主要原因,则可以将其删除)
  • 我看不到任何地方 SELECT SCOPE_IDENTITY 会随着时间的推移导致性能大幅下降,但假设您插入了 500,000 行,并且每次都调用它。它会让每个后续调用变得越来越慢吗?

标签: c# .net foxpro visual-foxpro


【解决方案1】:

您正在执行 Rushmore 的查询是否经过优化,即表上是否有字段 NOTEPOINT 和 NOTEDESC 的索引?

【讨论】:

  • NOTEDESC 是一个备注字段,没有索引。 NOTEPOINT 确实有一个索引,但是索引的 Expression 字段(我正在使用 DBF Manager 查看它)是 STR(NOTEPOINT) + RIGHT(NOTEDATE, 4) + LEFT(NOTEDATE, 2) + SUBSTR (NOTEDATE, 4, 2 )
  • 在这种情况下,即使是部分优化,您的 SELECT 语句也必须匹配该索引表达式。所以我想知道它是否因此而陷入困境。相反,是否可以向 VFP 表添加另一个索引以匹配您的查询?
  • 你也可以让它完全 rushmore 优化,只包括 WHERE 子句中的整个关键表达式,虽然我不认为这是发生减速的地方,值得一试...“SELECT NOTEDATE , NOTEDESC, ENTEREDBY FROM " + noteTables[y] + " WHERE STR(NOTEPOINT) + RIGHT(NOTEDATE, 4) + LEFT(NOTEDATE, 2) + SUBSTR (NOTEDATE, 4, 2) = " + lNoteKey.ToString() + " AND NOTEDESC NOT LIKE 'Folder accessed%'";
  • 修改 NOTEPOINT 字段的索引,使其与 NOTEDATE 字段没有任何关系会更好吗?比如只做NOTEPOINT的索引,就,NOTEPOINT?我正在处理数据的副本,因此如果需要,我可以进行修改。
  • 添加另一个索引是可以接受的,但不一定是更好的方法。请记住,在插入记录时,每个索引都会增加一些开销。使用“STR(NOTEPOINT) + RIGHT(NOTEDATE, 4) + LEFT(NOTEDATE, 2) + SUBSTR (NOTEDATE, 4, 2)”索引不会影响查询的性能。查询期间不会进行计算,它只会告诉 VFP 在过滤数据时使用现有索引。
【解决方案2】:

将代码转换为使用DataReader 而不是DataTable。使用while 循环迭代结果。我相信DataTable 上的foreach 循环也在减慢速度。由于您只向前读取值,这将提高性能:

查看Performance Comparison: Data Access Techniques了解更多信息,尤其是DataReader vs. DataSet部分

DataReader 是需要的应用程序的更好选择 优化的只读和只进数据访问。越早加载 从 DataReader 中取出数据,关闭 DataReader,然后关闭 数据库连接,获得更好的性能。因为 DataReader 持有一个不能用于任何数据库连接 应用程序读取数据时的其他目的,它可能会限制 如果应用程序持有 DataReader 足够长的时间 发生争用。 DataSet 只需要在保持连接的同时 它正在被填充。填满后,连接可能会关闭 并回到了游泳池。延迟从 DataSet 读取数据 不会导致发生争用,因为连接可能已经 已经回到游泳池了。

【讨论】:

  • 我更改了代码,以便 oNotesCmd.ExecuteReader 创建一个我显式实例化的对象。然后我只需做一段时间(Reader.Reader()) 并使用相同的列,而不是 foreach。实际上我的表现更差。我想我不明白您将 DataReader 的结果放入 DataSet 是什么意思。在我现在的代码中,这不是使用 DataReader 对象来填充 DataTable 吗?此时,DataTable 完全在内存中,我没有访问数据库。
  • 与其使用我的 long 列表中的第一个 foreach 循环,一次访问整个 note 表会更好吗?并为此调用DataReader?我也有一部分人认为 NOTEPOINT 上的索引不寻常,可能是问题的一部分。
【解决方案3】:

问题是 SQL 端表上的一些数据库触发器。在操作期间禁用它们(在这种情况下不需要它们)解决了这个问题。

【讨论】:

    猜你喜欢
    • 2011-06-30
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    相关资源
    最近更新 更多