【问题标题】:SQLite .NET performance, how to speed up things?SQLite .NET 性能,如何加快速度?
【发布时间】:2010-12-05 08:33:21
【问题描述】:

在我的系统上,大约 86000 次 SQLite 插入最多需要 20 分钟,这意味着每秒大约 70 次插入。我必须做几百万,我怎样才能加快速度?对每一行的 SQLiteConnection 对象调用 Open() 和 Close() 会降低性能吗?交易有帮助吗?

单行的典型插入方式:

    public int InsertResultItem(string runTag, int topicId,
        string documentNumber, int rank, double score)
    {
        // Apre la connessione e imposta il comando
        connection.Open();

        command.CommandText = "INSERT OR IGNORE INTO Result "
          + "(RunTag, TopicId, DocumentNumber, Rank, Score) " +
            "VALUES (@RunTag, @TopicId, @DocumentNumber, @Rank, @Score)";

        // Imposta i parametri
        command.Parameters.AddWithValue("@RunTag", runTag);
        command.Parameters.AddWithValue("@TopicId", topicId);
        command.Parameters.AddWithValue("@DocumentNumber", documentNumber);
        command.Parameters.AddWithValue("@Rank", rank);
        command.Parameters.AddWithValue("@Score", score);

        // Ottieni il risultato e chiudi la connessione
        int retval = command.ExecuteNonQuery();
        connection.Close();

        return retval;
    }

如您所见,插入非常简单。

【问题讨论】:

    标签: c# .net performance sqlite insert


    【解决方案1】:

    你肯定需要一笔交易。如果您不这样做,SQLite 会为每个插入命令启动自己的事务,因此您实际上是按原样执行 86000 个事务。

    看起来您每次都在打开和关闭连接,并且每次都重置 CommandText。这是不必要的,无疑会减慢您的速度,如果您:

    • 打开一次连接
    • 构建命令一次,将参数添加一次。
    • 开始事务
    • 循环,仅在调用 ExecuteNonQuery 之前更改参数值
    • 提交事务。
    • 关闭连接。

    我认为您可以通过这种方式将 20 分钟缩短到几秒钟。

    编辑:这就是我的意思:

    public void InsertItems()
    {
        SQLiteConnection connection  = new SQLiteConnection(SomeConnectionString);
        SQLiteCommand command = connection.CreateCommand();
        SQLiteTransaction transaction = connection.BeginTransaction();
    
        command.CommandText = "INSERT OR IGNORE INTO Result "
    + "(RunTag, TopicId, DocumentNumber, Rank, Score) " +
      "VALUES (@RunTag, @TopicId, @DocumentNumber, @Rank, @Score)";
    
        command.Parameters.AddWithValue("@RunTag", "");
        command.Parameters.AddWithValue("@TopicId", "");
        command.Parameters.AddWithValue("@DocumentNumber", "");
        command.Parameters.AddWithValue("@Rank", "");
        command.Parameters.AddWithValue("@Score", "");
    
        foreach ( /* item to loop through and add to db */ )
        {
            InsertResultItem(runTag, topicId, documentNumber, rank, score, command);
        }
    
        transaction.Commit();
        command.Dispose();
        connection.Dispose();
    }
    
    public int InsertResultItem(string runTag, int topicId, string documentNumber, int rank, double score, SQLiteCommand command)
    {
        command.Parameters["@RunTag"].Value = runTag;
        command.Parameters["@TopicId"].Value = topicId;
        command.Parameters["@DocumentNumber"].Value = documentNumber;
        command.Parameters["@Rank"].Value = rank;
        command.Parameters["@Score"].Value = score;
        return command.ExecuteNonQuery();
    }
    

    它只使用一个连接、一个事务和一个命令,因此您每次更改的只是参数值。

    【讨论】:

    • 我不能,只是因为循环在另一个类中......如何实现 IDisposable 并在 Dispose() 上提交事务?
    • 我认为你如何实现它的细节真的取决于你,只要你只使用一个连接/事务/命令,它就会比你得到的要快得多无论如何都要出现。
    • 完成。从 20 分钟减少到仅 1 分钟。太棒了,感谢所有人!
    • 通过使用这种方法和更大的准备好的 INSERT 语句,我已经能够进一步减少 1/4 的时间,该语句一次插入 10 行并且具有 10 倍的参数。我认为它更快,因为它减少了本机互操作调用。
    • 我的性能指标:在不使用事务的情况下插入 100 行需要 19832.0 毫秒。使用事务插入 10000 行需要 797.0 毫秒。看起来交易是唯一的一个主要因素。
    【解决方案2】:

    使用事务。这应该使事情变得更快。另外我会向您推荐以下模式:

    public int InsertResultItem(string runTag, int topicId,
        string documentNumber, int rank, double score)
    {
        // Apre la connessione e imposta il comando
        using (var connection = new SQLiteConnection(SomeConnectionString))
        using (var command = new connection.CreateCommand())
        {
            connection.Open();
            using (var tx = connection.BeginTransaction())
            {
                command.CommandText = "INSERT OR IGNORE INTO Result "
                    + "(RunTag, TopicId, DocumentNumber, Rank, Score) " +
                    "VALUES (@RunTag, @TopicId, @DocumentNumber, @Rank, @Score)";
    
                // Imposta i parametri
                command.Parameters.AddWithValue("@RunTag", runTag);
                command.Parameters.AddWithValue("@TopicId", topicId);
                command.Parameters.AddWithValue("@DocumentNumber", documentNumber);
                command.Parameters.AddWithValue("@Rank", rank);
                command.Parameters.AddWithValue("@Score", score);
    
                // Ottieni il risultato e chiudi la connessione
                var retval = command.ExecuteNonQuery();
                tx.Commit();
                return retval;
            }
        }
    }
    

    【讨论】:

    • 由于 InsertResultItem 似乎会在循环中被调用,这仍然是打开一个新连接,开始一个新事务并为每个插入创建一个新命令。速度将与 OP 的代码相同。
    • @miket2e,这不是打开新连接,ADO.NET 使用连接池。启动新事务也正是加快速度的原因。
    • 它为每个插入创建一个事务。如果您没有明确说明,SQLite 无论如何都会隐式执行此操作 (sqlite.org/lang_transaction.html),这就是我所说的相同的意思。它需要是一个围绕所有插入的事务才能有所作为。我不确定他是否使用 ADO.NET,如果他使用嵌入在应用程序中的 SQLite(没有 ADO.NET),那么只需连接一次会更快。
    • 是的,每次获取新行时,循环都会调用 InsertResultItem。所以用事务没用?
    • 不,它不是没用的,一个事务会让它变得更快。您只需要稍微重构您的代码,以便您只使用一个事务,而不是数千个事务。比如说,先创建命令,然后将其作为参数传递给 InsertResultItem - 不要每次都重新创建它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多