0

我正在使用 ExecuteReader() 命令读取包含一些表的数据库。根据我在两个不同表中读取的第一个 Read() 结果的结果,因为我需要在第一个查询中返回的 ID 来运行第二个查询。

问题是这个搜索非常慢。

tuCommand.CommandText = "SELECT * FROM tblTranslationUnit WHERE DocumentId = " + doc.DocumentId;
var tuReader = tuCommand.ExecuteReader();
while (tuReader.Read())
{
    var tu = new TranslationUnit
     {
         TranslationUnitId = tuReader.GetInt64(0),
         DocumentId = tuReader.GetInt64(1),
         Raw = tuReader.GetString(2),
         IsSegmented = tuReader.GetBoolean(3),
         Reader = this, // Ryan: Fixed so that it sets the reader to itself
     };

    using (var propCommand = _dbConn.CreateCommand())
    {
        propCommand.CommandText = "SELECT * FROM tblTranslationUnitProperties WHERE TranslationUnitId = " + tu.TranslationUnitId;
        var propReader = propCommand.ExecuteReader();
        while (propReader.Read()) tu.Properties.Add(GetProperty(propReader));
    }
    yield return tu;
}

如果我删除第二个 ExecuteReader() 查询真的很快

我还尝试使用新连接和新事务放置第二个 ExecuteReader() 但结果几乎相同

任何想法或线索?我该如何进行这种搜索?有没有更好的方法?(我想是的)。


更多细节数据库结构是:

  - Document
      - properties
      - errors
    -TranslationUnits
        - properties
        - errors
      - Segments
          - properties
          - errors

所以在代码的某些部分我们会有这样的结构

  foreach (document in db)
      foreach (property in document)
      foreach (error in document)
    foreach (translationunit in document)
        foreach (property in translationunit)
        foreach (error in translationunit)
      foreach (segment in translationunit)
          foreach (property in segment)
          foreach (error in segment)

基于此,如果问题只是 SQLite 配置问题,那么使用连接返回所有内容并不是一个好主意。我的意思是,如果可以添加任何参数或类似参数来告诉系统我们将使用多个指针

现在我们正在转向数据表解决方案:

  1. 打开连接
  2. 读取表的 1000 个条目
  3. 关闭连接
  4. 打开一个新的连接
  5. 读取子表的 1000 个条目
  6. 关闭新连接
  7. ...
4

5 回答 5

1

听起来您有可伸缩性问题。SQLite 中包含“Lite”一词是有原因的。它缺乏某些东西,例如高并发性、细粒度的访问控制、丰富的内置函数集、存储过程、深奥的 SQL 语言特性、XML 和/或 Java 扩展、TB 或 PB 可伸缩性等等。我建议为初学者更改数据库。

我也不清楚您的问题,为什么您需要一次在内存中保存 1000 个文档,尤其是 1000 个包含 1000 个部分和 1000 个以上部分的文档,全部都在内存中。我不知道你的 UI 要求,但在我 15 年多的编程生涯中,我不记得在没有某种分页机制的情况下,我必须在单个网页或表单上显示 1000 个内容,所以你真的需要得到一次从数据库中获取 1000 * 1000 * 1000 个实体?

我认为您需要重新审视 UI、当前模型和数据层,以寻找在不牺牲大量性能的情况下交付尽可能少的内容的方法。考虑使用延迟加载、预读缓冲区、缓存、分页、搜索方法、共享静态数据等模式来降低前期成本。

考虑买房。我们大多数人都没有钱预先支付房子,所以我们得到了抵押贷款。抵押贷款是随着时间的推移分摊前期成本的方式。所有抵押贷款都会产生负面影响,称为利息。现在,我的总成本不是 100,000,而是 250,000,但因为我能负担当前的付款,我并没有真正注意到额外的 150,000,因为随着时间的推移,额外的成本会以小幅增量被吸收。另请注意,如果我在 5 年内卖掉我的房子,而不是在贷款的期限内留下来,我什至可能无法偿还全部 250,000。

这里的要点是,您可以分摊建立额外连接以检索较小记录集的成本,并且仍然为用户提供他们现在需要的东西。这将减少总体的前期成本,但会增加正在检索的单个记录集的额外成本。

于 2012-04-19T11:21:49.027 回答
1

嗨,我将在此添加我的发现(我正在和大卫一起工作)

我修改了我们使用 david 描述的缓冲区从数据库读取表的方式,因此没有同时连接或读取器同时执行。它似乎有点快,但非常明显。这里有一些数字。

我在 2.5 秒内用 5000 个翻译单元填充数据库(所有表)。然后,当我遍历 TranslationUnit 表(大约 5000 行)时,阅读时间非常惊人:0.07 秒。代码如下:

foreach (var tu in document)
{
   ... do something ...
}

如果我像这样阅读每个翻译单元的片段:

foreach (var tu in document)
{
    foreach (var seg in tu)
    {
        ... do something ...
    }
}

阅读时间开始变得难看:大约 10 秒。请注意,每个翻译单元恰好有 2 个段(尽管我们在设计中不限制这一点)

对于 10000 个翻译单元,填充数据库大约需要 6 秒,读取数据库大约需要 2 分钟。(如果只有 1 个 foreach 阅读翻译单元,几乎是即时的)

对于 50000 个翻译单元,填充大约需要 32 秒,在等待阅读完成 1 小时后我放弃了。(如果只有 1 个 foreach 阅读翻译单元,几乎是即时的)

所以我的猜测是阅读时间成本呈指数增长。是否有理由认为这是因为它必须将数据库指针更改为不同的表?(在翻译单元和段表之间)。

于 2012-04-21T22:37:19.780 回答
0

一次读取第一个查询的所有结果,关闭DataReader然后枚举内存中的结果。

于 2012-04-19T11:16:01.383 回答
0

首先,您可以编写 select with join 并通过一个查询获取所有内容

SELECT * FROM tblTranslationUnit join tblTranslationUnitProperties on
tblTranslationUnitProperties.TranslationUnitId = tblTranslationUnit.id 
WHERE DocumentId = @docID //<= use parameter

如果它没有帮助,也许你需要索引你的表。

于 2012-04-19T11:12:39.620 回答
0

您尝试过简单的“JOIN”吗?还是我在您的问题中遗漏了什么?

SELECT tbl2.* 
    FROM tblTranslationUnit tbl1 
    JOIN tblTranslationUnitProperties tbl2 ON tbl2.TranslationUnitId = tbl1.TranslationUnitId 
于 2012-04-19T11:09:59.373 回答