SQLite数据库(.db)的性能问题

时间:2014-07-04 05:03:31

标签: c# sql database sqlite spatialite

我遇到SQLite database (.db)

的性能问题

我正在尝试更新数据库(.db)中的1,00,000条记录,大约需要50分钟。太慢了。

我的代码如下::

        for (int q = 0; q < list.Count; q++) 
            { 
        ArrayList castarraylist = new ArrayList(); 
        castarraylist = (ArrayList)(list[q]); 

        using (var cmd = new SQLiteCommand(con)) 

            using (var transaction = con.BeginTransaction()) 
            { 
                cmd.Transaction = transaction; 

                for (int y = 0; y < castarraylist.Count; y++) 
                { 
                        cmd.CommandText = Convert.ToString(castarraylist[y]); 
                           cmd.ExecuteNonQuery(); 
                } 
                transaction.Commit(); 
                GC.Collect(); 
            } 
        } 

这里每个castarraylist包含5000条记录。用事务更新到数据库。所以循环经历20次并完成所有更新。 当我手动检查时间时,它会增加每次迭代5000次记录的时间。喜欢

1st 5000 records processing time > 1:11 minute

2nd 5000 records processing time > 1:25 minute

3rd  5000 records processing time > 1:32 minute 

4th 5000 records processing time > 1:40 minute 

5th 5000 records processing time > 1:47 minute 

6th 5000 records processing time > 1:52 minute 

...

... 

... 

17th 5000 records processing time > 3:32 minute 

18th 5000 records processing time > 3:44 minute

19th 5000 records processing time > 4:02 minute 

20th 5000 records processing time> 4:56 minute 

为什么会发生这种情况我无法理解。 我用C#和笔记本电脑配置编写的源代码是i5 2.6 GHz4 GB RAM500 GB HD

我在下面做了连接::

SQLiteConnection con = new SQLiteConnection("Data Source=" + fullPath + ";Version=3;Count Changes=off;Journal Mode=off;Pooling=true;Cache Size=10000;Page Size=4096;Synchronous=off"); 

(* fullpath - 是我的数据库路径)

我正在创建如下表格......

sqlquery2="Select LINK_ID from RDF_LINK string createLinkToPoly = "create table temp2 AS " + sqlquery2;

这将创建一个表并插入由sqlquery2传递的记录。

下面的语句在SQLite上扩展了Spatialite

ExecuteStatement("select load_extension('spatialite.dll')", con);

我的Update声明如下::

UPDATE temp2 SET GEOM = Transform(LineStringFromText('LINESTRING(4.38368 51.18109,4.38427 51.18165)',4326),32632)WHERE LINK_ID= 53841546

所以这种100000语句构建在不同的线程中并插入LIST

最后在上面的代码中执行UPDATE语句(现在使用Larry建议的代码)

4 个答案:

答案 0 :(得分:3)

首先,您应该尝试使用预准备语句以获得更好的性能。看一下System.Data.SQLite文档,这样就可以使用SQLiteParameter并在循环中设置参数值。

其次,ArrayList应该比List或数组慢。也许改变可以帮助。

第三,可以使用一些Pragma commands

编辑:我看到你已经关闭了同步和journal_mode,我不确定你应该使用其他任何编译指示。在某些情况下,locking_mode = EXCLUSIVE和temp_store = MEMORY可能会有所帮助。

答案 1 :(得分:3)

Currencly,事务是按查询运行的,这没有任何意义。

将主循环代码包含在事务中,并删除此GC.Collect()。

修改

据我所知,您不希望在发生错误时回滚全局更新。所以我稍微改了一下代码。

此外,我不确定通过更改CommandText并再次运行查询可以重用命令对象。这就是为什么我建议每次创建它。

using (var transaction = con.BeginTransaction()) 
{ 
    for (int q = 0; q < list.Count; q++) 
    { 
        var castarraylist = (ArrayList)(list[q]); 

        for (int y = 0; y < castarraylist.Count; y++) 
        { 
            using (var cmd = new SQLiteCommand(con)) 
            {
                cmd.Transaction = transaction; 
                cmd.CommandText = Convert.ToString(castarraylist[y]);
                try
                {
                    cmd.ExecuteNonQuery();
                }
                catch(Exception ex)
                {
                    // Log the update problem
                    Console.WriteLine("Update problem " + cmd.CommandText + " - Reason: " + ex.Message);
                }
            }
        }
    }

    transaction.Commit();
}

答案 2 :(得分:2)

您可能没有SQLite的性能问题;您几乎肯定会遇到自己代码的性能问题:

  • 几乎可以肯定没有必要调用GC.Collect()。你在这里做的不应该造成任何重大的内存压力,如果是这样的话,我强烈建议让垃圾收集者自己动手而不是强迫这个问题。更糟糕的是,您在循环的每次迭代上调用GC.Collect()。不要这样做!

  • 是否真的有必要在自己的交易中进行每次更新?你确实意识到,如果你的代码失败并在这个循环中途抛出一个异常,那么前半部分的更新将会被提交,但是你还没有办法从你离开的地方开始接收?你甚至没有一个简单的方法知道你离开的地方。

  • 您是否使用ArrayList而不是List&lt; T&gt;?这导致您需要执行强制转换并在内循环中调用Convert.ToString,这不是必需的(除非您有非常非常好的理由使用ArrayList)。

答案 3 :(得分:2)

UPDATE语句很慢,因为数据库必须扫描表中的所有记录才能找到任何匹配的LINK_ID值。 您需要LINK_ID列上的索引。

在进行更新之前手动创建它:

CREATE INDEX temp2_linkid ON temp2(LINK_ID);

或者在创建表时创建索引(这要求明确创建表):

CREATE TABLE temp2 ( LINK_ID INTEGER PRIMARY KEY );
INSERT INTO temp2(LINK_ID) SELECT LINK_ID FROM RDF_LINK;