Sql 提高Ruby脚本处理CSV的性能

Sql 提高Ruby脚本处理CSV的性能,sql,ruby,performance,csv,sqlite,Sql,Ruby,Performance,Csv,Sqlite,我编写了一个Ruby脚本来执行以下操作: 将非常大(2GB/12500000行)的CSV读入SQLite3 查询数据库 将结果输出到新的CSV 在我看来,这似乎是最简单、最合乎逻辑的方法。这一过程需要可配置并定期重复,因此需要脚本。我之所以使用SQLite,是因为数据总是以CSV形式出现(无法访问原始数据库),而且更容易将处理转移到(易于更改的)SQL语句中 问题是步骤1和步骤2需要很长时间。我一直在寻找解决问题的办法。我已经实施了其中一些建议,但成效有限 SQLite3的内存中实例 使用事务

我编写了一个Ruby脚本来执行以下操作:

  • 将非常大(2GB/12500000行)的CSV读入SQLite3
  • 查询数据库
  • 将结果输出到新的CSV
  • 在我看来,这似乎是最简单、最合乎逻辑的方法。这一过程需要可配置并定期重复,因此需要脚本。我之所以使用SQLite,是因为数据总是以CSV形式出现(无法访问原始数据库),而且更容易将处理转移到(易于更改的)SQL语句中

    问题是步骤1和步骤2需要很长时间。我一直在寻找解决问题的办法。我已经实施了其中一些建议,但成效有限

    • SQLite3的内存中实例
    • 使用事务(步骤1左右)
    • 使用事先准备好的陈述
    • PRAGMA synchronous=OFF
    • PRAGMA journal\u mode=MEMORY
      (不确定使用内存数据库时是否有此帮助)
    在所有这些之后,我得到了以下几次:

    • 读取时间:17米28秒
    • 查询时间:14m 26s
    • 写入时间:0米4秒
    • 运行时间:31米58秒
    假设我使用的是与上述帖子不同的语言,并且存在编译/解释等差异,但是插入时间大约是79000次/s,而不是12000次/s,这比之前慢了6倍

    我还尝试为部分(或全部)字段编制索引。这实际上有相反的效果。索引需要很长时间,因此索引时间完全掩盖了查询时间的任何改进。此外,在内存DB中执行此操作最终会由于所需的额外空间而导致内存不足错误

    SQLite3不是适合这种数据量的数据库吗?我也尝试过使用MySQL,但是它的性能更差

    最后,这里是代码的简化版本(删除了一些不相关的细节)

    需要“csv”
    需要'sqlite3'
    inputFile=ARGV[0]
    outputFile=ARGV[1]
    准则1=ARGV[2]
    准则2=ARGV[3]
    准则3=ARGV[4]
    开始
    memDb=SQLite3::Database.new”:内存:
    memDb.execute“PRAGMA synchronous=OFF”
    memDb.execute“PRAGMA journal\u mode=MEMORY”
    memDb.execute“删除表(如果存在)区域”
    memDb.execute“如果不存在创建表区域(街道名称文本、街道类型文本、位置文本、状态文本、邮政编码整数、Criteria1实数、Criteria2实数、Criteria3实数)”
    insertStmt=memDb.prepare“插入区域值(?1、?2、?3、?4、?5、?6、?7、?8)”
    #从文件中读取值
    读取计数器=0
    memDb.execute“开始事务”
    blockReadTime=Time.now
    CSV.foreach(输入文件){行|
    读取计数器+=1
    如果读取计数器>100000,则中断
    如果读取计数器%10000==0
    formattedReadCounter=readCounter.to_.reverse.gsub(/…(?=)/,“\&”)。reverse
    打印“\r阅读行{formattedReadCounter}({Time.now-blockReadTime}s)”
    冲洗
    blockReadTime=Time.now
    结束
    insertStmt.execute(第[6]行、第[12]行、第[13]行、第[14]行、第[10]行、第[11]行、第[12]行、第[13]行、第[14]行)
    }
    memDb.execute“结束事务”
    insertStmt.close
    #过程值
    
    sqlQuery=通常,如果可能,您应该尝试使用
    UNION ALL
    而不是
    UNION
    ,这样就不必检查这两个子查询是否重复。 但是,在这种情况下,SQLite必须在单独的步骤中执行
    DISTINCT
    。这是否更快取决于您的数据

    根据我的
    EXPLAIN查询计划
    实验,以下两个索引对该查询最有帮助:

    CREATE INDEX i1 ON Area(Locality, State, PostCode);
    CREATE INDEX i2 ON Area(StreetName, StreetType, Locality, State, PostCode);
    

    可能是我缺乏Ruby技能,但您似乎没有使用batch insert?您尝试过类似的方法吗@Mirko你能解释一下批量插入是什么意思吗?我能看到的将数据推入SQLite3的唯一方法是使用insert语句。此外,从我所读到的内容来看,即使使用“INSERT-INTO-targetTable(fields)(values)[,(values)]*”格式也没有改进efficiency@Zach我试着比较MySQL的性能,虽然不是CSV存储引擎。我需要一个可移植的解决方案-即,我需要能够将脚本提供给任何人,而不需要他们安装其他软件。这就是选择SQLite3内存解决方案的原因。索引创建有多慢?首先,感谢您提到DISTINCT-我已经删除了它,因为它被GROUP by冗余了。此外,我还使用了UNION ALL来删除隐含的DISTINCT,因为这不是必需的。最后,我现在用多字段索引运行它。以前我使用了很多单字段索引。。。这表明我在SQL优化方面缺乏经验。下面是CL建议索引的新数字:创建索引i1(233.44s)创建索引i1(238.836s)读取时间:17m 40秒索引时间:7m 52秒查询时间:0m 54秒写入时间:0m 3秒经过时间:26m 31秒哇-索引产生了多大的差异!我不知道这是创建索引的正确方法。在此之前,出于绝望,我创建了8个单独的索引(每个字段1个),但创建它们花费了大量时间,对查询没有任何好处。
    CREATE INDEX i1 ON Area(Locality, State, PostCode);
    CREATE INDEX i2 ON Area(StreetName, StreetType, Locality, State, PostCode);