代码之家  ›  专栏  ›  技术社区  ›  XstreamINsanity

有更快的方法读取文件并插入到sqlite中吗

  •  0
  • XstreamINsanity  · 技术社区  · 16 年前

    大家下午好。我不太熟悉sqlite,所以我没有弄乱数据库的所有设置。我非常熟悉SQL Server、Oracle,甚至还有一些Access和MySQL。好吧,目前,我正在获取一个包含110000多条记录的文件,并逐行读取该文件,分析数据并运行一条INSERT语句到表中。表取决于记录类型,记录类型是行的第一个字段。嗯,我现在正在加载它,它已经运行了12分钟(在我写这个的时候),并且只导入了14000条记录。做数学,这意味着需要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执行查询方法

        #region public void ExecuteNonQuery(string strSQL)
        /*************************************************************************************
        *************************************************************************************/
        public void ExecuteNonQuery(string strSQL)
        {
            DbCommand dbCommand = _dbConnection.CreateCommand();
            dbCommand.CommandText = strSQL;
            dbCommand.Prepare();
            dbCommand.ExecuteNonQuery();
        }
        #endregion
    

    有人能从中看到任何可以改进的东西吗?后台工作人员是否有可以设置为更快工作的设置?后台工作人员是否有默认设置?数据库文件上有哪些设置可以更改(使用sqlite expert personal)以加快插入速度?它只是我文件的大小吗?现在,当我完成这项工作的时候,它刚过去22分钟,完成了24000个记录。这不是一个时间敏感的问题,所以你需要的时间都要花掉。谢谢。

    更新 :另外,我想我应该提到,在其中一个表中,我有一个整数主键(用作标识字段)。会不会有性能问题?

    4 回复  |  直到 12 年前
        1
  •  5
  •   Matthew Flaschen    16 年前

    使用单一 SQLiteTransaction 围绕整个插件。同样,它会在每次插入后强制对文件进行冲洗,以保持对酸的遵从性。与任何一样 DbConnection 和 DbTransaction ,你使用 BeginTransaction 当你完成后, Commit . 整个插入将成功或失败,并且它将具有更好的性能。

        2
  •  1
  •   Mark    16 年前

    提高插入性能的第一件事是只开始一个事务。这将导致插入速度加快一个数量级。

    见 here 用于描述此现象的常见问题解答条目。

        3
  •  1
  •   Bill Karwin    16 年前

    fwiw,sqlite的命令行客户机有一个数据加载内置命令。但是,如果您阅读该sqlite客户机的C代码,您会发现它没有做任何特殊的事情。它只是一行一行地读取数据文件,并在循环中执行insert。

    其他答案建议使用显式事务,这样可以避免在每行之后刷新I/O的开销。我同意这个建议,它肯定会有巨大的好处。

    您也可以禁用 rollback journal :

    PRAGMA journal_mode = OFF
    

    或将写入设置为 asynchronous ,允许操作系统缓冲I/O:

    PRAGMA synchronous = OFF
    

    这些pragma更改应该节省大量的I/O开销。但是如果没有回滚日志,回滚命令将无法工作,并且如果应用程序在进行中的事务期间崩溃,则数据库可能已损坏。如果没有同步写入,操作系统故障也可能导致数据丢失。

    不要试图吓唬您,但您应该知道性能和保证的I/O完整性之间存在权衡。我建议在大多数时候启用安全模式的情况下操作,并且仅在需要像现在这样进行大数据加载时短暂禁用它们——然后记住重新启用安全模式!

        4
  •  0
  •   chris    14 年前

    我一直在试验,发现用C将大型数据库导入sqlite的最快方法实际上是转储到csv,然后使用命令行sqlite3.exe工具

    在我的笔记本电脑上插入一个大约2500万行的大文件

    带.NET包装的优化插入:30分钟 (通过事务、参数化命令、日记账关闭等优化)

    转储到csv(2分钟),然后使用sqllite3.exe导入csv(5分钟):7分钟

    推荐文章