Sqlite大数据写入性能优化

  众所周知,Sqlite是一个轻量级的数据库,仅仅需要一个exe文件就能运行起来。在处理本地数据上,我比较喜欢选择使用它,不仅是因为他与sql server有着比较相近的语法,还因为它不需要安装,仅需要通过命令行就能启动了,而且他在处理大数据时,性能比sql server好很多,好吧这里不继续争论性能优劣。

  首先,这次的问题是在一次项目中遇到的,项目要求能大量导入数据,而且由于项目性质(轻便,本地化),所以我选择sqlite来存放数据。

  第一版代码:

1 StringBuilder sql = new StringBuilder();
2 foreach (DataRow dr in dt.Rows)
3 {
4     sql.Append("INSERT INTO Info (Name,Code) VALUES('"+dr[0]+"','"+dr[1]+"') 
");    
5 }
6 sqlHelper.SqliteHelper.ExecuteNonQuery( sql.ToString(), CommandType.Text);
View Code

  从上面的代码看来,我没有进行任何优化工作,我用这种方式插入了1万条数据用了1分多钟,可见优化的重要性(百万级数据跑起来...不敢想象)。

  开启事务:

1 StringBuilder sql = new StringBuilder();
2 sql.Append("BEGIN;");//开启事务
3 foreach (DataRow dr in dt.Rows)
4 {
5      sql.Append("INSERT INTO Info (Name,Code) VALUES('"+dr[0]+"','"+dr[1]+"') 
");    
6 }
7 sql.Append("COMMIT;");//提交事务
8 sqlHelper.SqliteHelper.ExecuteNonQuery( sql.ToString(), CommandType.Text);
View Code

      由于sqlite的数据操作实质上是对于其数据文件的IO操作,频繁的插入数据会导致文件IO经常开闭,非常损耗性能能。事务作用便是使数据先缓存在系统中,提交事务时便提交所有的更改到数据文件,此时数据文件的IO只需要开闭一次,且避免了长期占用文件IO所导致性能低下的问题。此时,开启事务处理后性能上虽然有了大幅度的提升,但是结果仍旧不理想,哪还有什么在影响着性能呢?下面尝试开启预处理来解决问题。

  开启预处理:

 1  public static int ExecuteNonQuery(string commandText, CommandType commandType)
 2         {
 3             int result = 0;
 4             if (commandText == null || commandText.Length == 0)
 5                 throw new ArgumentNullException("sql语句为空");
 6             using (SQLiteConnection con = new SQLiteConnection(connectionString))
 7             {
 8                 SQLiteCommand cmd = new SQLiteCommand();
 9                 cmd.Connection = con;
10                 cmd.CommandText = commandText;
11                 cmd.Prepare();//开启预处理
12                 try
13                 {
14                     result = cmd.ExecuteNonQuery();
15                 }
16                 catch (Exception ex)
17                 {
18                     throw ex;
19                 }
20             }
21             return result;
22         }
23                     
View Code

  预处理的原理就是将一条语句先预编译到数据库,下次再次执行相同的语句时,就不用再次编译,节省了大量的时间。由此看来,代码似乎还没有优化完成。就第一版代码来说,由于批量插入的数据不尽相同,所以数据库会多次编译插入语句,性能会损耗非常多,也就造成插入需要的时间会比较多。有没有一条语句是可以循环使用的?答案是有的,使用参数化传值,就能使每一次的插入的sql语句都是相同的。

  参数化(数据库帮助类那边我就不写了):

 1 StringBuilder sql = new StringBuilder();
 2 SQLiteParameter[] sp = new SQLiteParameter[2];
 3 foreach (DataRow dr in dt.Rows)
 4 {
 5 sql.Clear();
 6 sql.Append("INSERT INTO Info (Name,Code) VALUES(@Name,@Code) 
");  
 7 sps[0] = new SQLiteParameter("@p1", dr["Name"]);
 8 sps[1] = new SQLiteParameter("@p2", dr["Code"]);
 9 sqlHelper.SqliteHelper.ExecuteNonQuery(sql.ToString(),sp, CommandType.Text);  
10 }
View Code

  经过一系列优化处理后,插入1万条数据只需要不到1秒,性能得到了极大的提升。

  另外,本人新手,如有不对,望不吝指教。

原文地址:https://www.cnblogs.com/yisen-code/p/6897524.html