下午大家。我对SQLite并不熟悉所以我没有搞乱数据库的所有设置。我比较熟悉SQL Server,Oracle甚至一些Access和mySQL。好吧,目前,我正在获取一个包含110,000多条记录的文件并逐行读取文件,解析数据并对表运行insert语句。该表取决于作为行的第一个字段的记录类型。好吧,我现在正在加载它,并且它已经运行了12分钟(正如我写的那样)并且只导入了14,000条记录。算一算,这意味着它需要1小时到15分钟到1小时30分钟。取决于我的系统其余部分当时的行为方式。因为有不同的记录类型,如果SQLite有一个选项(不确定是否有),我无法进行批量插入。这是作为后台工作者运行的。下面是拉取和解析数据的函数,以及将其插入数据库的函数。请记住,这是一个MVC格式的C#应用程序(当我控制它并且没有时间重组它时就像这样):
MainForm.cs后台工作程序函数
#region Background Worker Functions
#region private void InitializeBackgroundWorker()
/*************************************************************************************
*************************************************************************************/
private void InitializeBackgroundWorker()
{
backgroundWorker.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
#endregion
/*****************************************************************************************************************************************************************************************************/
#region private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
/*************************************************************************************
*************************************************************************************/
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
//Creates a static singleton file list. Remains on the stack and can be accessed anywhere without
// reinstatiating
object[] obj = (object[])e.Argument;
string fileName = obj[0].ToString();
DataController controller = new DataController(worker, e);
controller.FileName = fileName;
try
{
if (strProcess == "Import")
{
controller.Import();
}
else if (strProcess == "Export")
{
controller.ExportToExcel();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
#endregion
/*****************************************************************************************************************************************************************************************************/
#region private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
/*************************************************************************************
*************************************************************************************/
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.StackTrace);
}
else
{
this.toolStripStatusLabel1.Text = "Import complete";
generateReport();
treeViewFigure.Nodes.Clear();
BuildTree();
treeViewFigure.TopNode.ExpandAll();
labelIPBNumber.Text = controller.IPBNumber;
this.Text += "IPB: " + labelIPBNumber.Text;
cmbIndentureLevel.Items.Clear();
}
}
#endregion
/*****************************************************************************************************************************************************************************************************/
#region private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
/*************************************************************************************
*************************************************************************************/
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string stat = e.UserState.ToString();
this.toolStripStatusLabel1.Text = "";
this.toolStripStatusLabel1.Text = stat;
this.toolStripProgressBar1.Value = e.ProgressPercentage;
}
#endregion
#endregion
Importer.cs导入功能
#region public void Import(string fileName)
/*************************************************************************************
*************************************************************************************/
public void Import(string fileName)
{
if (!File.Exists(fileName))
{
throw new FileNotFoundException();
}
StreamReader read = File.OpenText(fileName);
List<RecordBase> List = new List<RecordBase>();
DataFactory factory = DataFactory.BuildFactory();
int nLines = 0;
while (!read.EndOfStream)
{
read.ReadLine();
nLines++;
}
read.Close();
read = File.OpenText(fileName);
factory.lstObservers = _observers;
factory.ClearDB();
int count = 1;
while (!read.EndOfStream)
{
string[] fields = read.ReadLine().Split('|');
List<string> lstStr = new List<string>();
foreach (string str in fields)
{
lstStr.Add(str);
}
lstStr.RemoveAt(fields.Length - 1);
fields = lstStr.ToArray();
string strValues = string.Join("','", fields);
strValues = "'" + strValues + "'";
if (fields.Length >= 39 && fields[0] == "03")
{
factory.ImportTaggedRecord(fields[38], count);
int nIndex = strValues.IndexOf(fields[38]);
strValues = strValues.Substring(0, nIndex - 2);
}
factory.ImportIPB(strValues, fields[0], count);
progress.ProgressComplete = (count * 100) / nLines;
progress.Message = "Importing Record: " + count++.ToString();
Notify();
}
}
#endregion
DataFactory.cs ImportIPB函数
#region public void ImportIPB(string strValues, string strType)
/*************************************************************************************
*************************************************************************************/
public void ImportIPB(string strValues, string strType, int nPosition)
{
string strCommand = string.Empty;
switch (strType)
{
case "01":
strCommand = Queries.strIPBInsert;
break;
case "02":
strCommand = Queries.strFigureInsert;
break;
case "03":
strCommand = Queries.strPartInsert;
break;
}
ExecuteNonQuery(strCommand + strValues + ", " + nPosition.ToString() + ")");
}
#endregion
Database.cs ExecuteNonQuery方法
#region public void ExecuteNonQuery(string strSQL)
/*************************************************************************************
*************************************************************************************/
public void ExecuteNonQuery(string strSQL)
{
DbCommand dbCommand = _dbConnection.CreateCommand();
dbCommand.CommandText = strSQL;
dbCommand.Prepare();
dbCommand.ExecuteNonQuery();
}
#endregion
任何人都可以从提供的内容中看到可以改进的内容吗?是否有可以设置为更快工作的后台工作程序的设置?是否有后台工作人员的默认设置? db文件中有哪些设置可以更改(使用SQLite Expert Personal)以使插入更快?这只是我文件的大小吗?现在,当我完成这个时,它只是超过了22分钟,它完成了24,000条记录。这不是一个时间敏感的问题,所以请花费你所需的时间。感谢。
更新:另外,我想我应该在其中一个表中提到我有一个整数主键(充当身份字段)。这会有任何性能问题吗?
答案 0 :(得分:5)
在整个插页周围使用一个SQLiteTransaction
。因此,它会在每次插入后强制刷新文件以保持ACID兼容性。与任何DbConnection
和DbTransaction
一样,您使用BeginTransaction
,然后在完成后Commit
。整个插入将成功或失败,并且它将具有更好的性能。
答案 1 :(得分:1)
增加插入性能的首要任务是仅开始单个事务。它将为您的插入物带来数量级的加速。
有关描述此现象的常见问题条目,请参阅here。
答案 2 :(得分:1)
FWIW,SQLite的命令行客户端有一个数据加载内置命令。但是,如果您阅读该SQLite客户端的C代码,您会发现它没有做任何特殊的事情。它只是逐行读取您的数据文件并在循环中执行INSERT。
其他答案建议使用显式事务,这样可以避免每行后I / O刷新的开销。我同意这个建议,肯定会有很大的好处。
您还可以停用rollback journal:
PRAGMA journal_mode = OFF
或设置写入asynchronous,允许操作系统缓冲I / O:
PRAGMA synchronous = OFF
这些pragma更改应该可以节省大量的I / O开销。但是如果没有回滚日志,则ROLLBACK命令将不起作用,如果您的应用程序在正在进行的事务期间崩溃,则您的数据库可能已损坏。如果没有同步写入,操作系统故障也可能导致数据丢失。
不要试图吓唬你,但你应该知道在性能和有保证的I / O完整性之间存在权衡。我建议在大多数情况下启用安全模式,并在需要像你一样做大数据负载时短暂禁用它们 - 然后记得重新启用安全模式!
答案 3 :(得分:0)
我一直在试验并发现用C#将大型数据库导入SQLite的最快方法实际上是转储到csv然后使用命令行sqlite3.exe工具
在我的笔记本电脑上插入一个包含大约2500万行的大文件
使用.NET包装器优化插入:30分钟 (针对事务,参数化命令,日志关闭等进行了优化)
转储为CSV(2分钟),然后使用sqllite3.exe导入CSV(5分钟):7分钟