将CSV数据解析到Activerecord(MySQL)太慢,需要几个小时

将CSV数据解析到Activerecord(MySQL)太慢,需要几个小时,mysql,ruby-on-rails,ruby-on-rails-3,activerecord,Mysql,Ruby On Rails,Ruby On Rails 3,Activerecord,我目前正在开发一个用于内部目的的定制票证系统,为此我编写了一个RAILS应用程序。票证数据来自另一个位置,我只能从中获取每日CSV摘录。我编写了一个任务,FTP在本地保存CSV文件,然后运行下面的任务,使用ActiveRecord将其导入MySQL数据库 但是它非常慢!我每天收到的每个CSV文件包含大约20000-40000行,总数据大小为8-10MB。每一行都包含一个在过去一天中创建或修改的票证,这解释了为什么我要检查票证是否已经存在于下面的代码中 在没有调试输出的生产模式下运行并没有多大区别

我目前正在开发一个用于内部目的的定制票证系统,为此我编写了一个RAILS应用程序。票证数据来自另一个位置,我只能从中获取每日CSV摘录。我编写了一个任务,FTP在本地保存CSV文件,然后运行下面的任务,使用ActiveRecord将其导入MySQL数据库

但是它非常慢!我每天收到的每个CSV文件包含大约20000-40000行,总数据大小为8-10MB。每一行都包含一个在过去一天中创建或修改的票证,这解释了为什么我要检查票证是否已经存在于下面的代码中

在没有调试输出的生产模式下运行并没有多大区别

desc 'Takes Orion csv file and parses into DB.'
    task :importcsv, [:local_file_path] => :environment do |t, args|
        require 'csv'
        @error_count = 0
        @success_count = 0

        csv = CSV.read(args.local_file_path, col_sep: ",", encoding: "ISO8859-1", headers: true)

        csv.each do |row|
            if(/PR(.*)/.match(row[0])? true : false) # Skip PR tickets because they're a waste of space right now
                @error_count += 1
                next
            end

            if(row[0] == " ") # Break loop if ticketid is just whitespace
                break
            end

            if(row[0].empty?) # Break loop if no ticketid
                break
            end

            ticket = Ticket.find_or_create_by_ticketid(row[0],  :severity => row[7],
                                                                :status => row[1],
                                                                :causecode => row[17],
                                                                :title => row[25],
                                                                :reportergrp => row[18],
                                                                :resolvergrp => row[5],
                                                                :resolvername => row[27],
                                                                :opendate => row[14],
                                                                :closedate => row[13],
                                                                :accountname => row[23],
                                                                :resolutiondesc => row[26] )

            @success_count += 1
        end
        Rails.logger.info " #{@success_count} out of #{@error_count + @success_count} tickets were added or updated."
    end
以下是开发调试输出的示例:

############### START PARSING ORION DATA ###############
Fetching data for date 2012-02-01...
    Data already exists locally. Did not download.
    Adding data to DB...
  [1m[36mTicket Load (99.8ms)[0m  [1mSELECT `tickets`.* FROM `tickets` WHERE `tickets`.`ticketid` = '03052019' LIMIT 1[0m
  [1m[35m (0.3ms)[0m  BEGIN
  [1m[36mTicket Exists (24.2ms)[0m  [1mSELECT 1 FROM `tickets` WHERE `tickets`.`ticketid` = BINARY '03052019' LIMIT 1[0m
  [1m[35mSQL (1.4ms)[0m  INSERT INTO `tickets` (`accountname`, `causecode`, `closedate`, `created_at`, `opendate`, `reportergrp`, `resolutiondesc`, `resolvergrp`, `resolvername`, `severity`, `status`, `ticketid`, `title`, `updated_at`) VALUES ('WESTPAC', 'AP_DATA', '2010-12-30 00:00:00', '2012-02-21 04:55:09', '2010-05-19 00:00:00', 'HDNZ', '-', 'DINZ', 'Sam Gardner', 3, 'CLOSED', '03052019', 'HTML GENERATED REPORTS CONT. OF FAULT: 03042', '2012-02-21 04:55:09')
  [1m[36m (2.3ms)[0m  [1mCOMMIT[0m
  [1m[35mTicket Load (69.1ms)[0m  SELECT `tickets`.* FROM `tickets` WHERE `tickets`.`ticketid` = '03089753' LIMIT 1
  [1m[36m (0.4ms)[0m  [1mBEGIN[0m
  [1m[35mTicket Exists (19.8ms)[0m  SELECT 1 FROM `tickets` WHERE `tickets`.`ticketid` = BINARY '03089753' LIMIT 1
  [1m[36mSQL (0.9ms)[0m  [1mINSERT INTO `tickets` (`accountname`, `causecode`, `closedate`, `created_at`, `opendate`, `reportergrp`, `resolutiondesc`, `resolvergrp`, `resolvername`, `severity`, `status`, `ticketid`, `title`, `updated_at`) VALUES ('WESTPAC', 'SW_PROGRAMCODE', NULL, '2012-02-21 04:55:09', '2010-07-20 00:00:00', 'HDNZ', '-', 'IANZ', 'Mitch Bell', 3, 'RESTORED', '03089753', 'CEE: EDS ERROR', '2012-02-21 04:55:09')[0m
  [1m[35m (1.7ms)[0m  COMMIT
  [1m[36mTicket Load (66.2ms)[0m  [1mSELECT `tickets`.* FROM `tickets` WHERE `tickets`.`ticketid` = '03236150' LIMIT 1[0m
  [1m[35m (0.2ms)[0m  BEGIN
  [1m[36mTicket Exists (21.5ms)[0m  [1mSELECT 1 FROM `tickets` WHERE `tickets`.`ticketid` = BINARY '03236150' LIMIT 1[0m
  [1m[35mSQL (0.4ms)[0m  INSERT INTO `tickets` (`accountname`, `causecode`, `closedate`, `created_at`, `opendate`, `reportergrp`, `resolutiondesc`, `resolvergrp`, `resolvername`, `severity`, `status`, `ticketid`, `title`, `updated_at`) VALUES ('WESTPAC', 'AP_DATA', '2011-12-12 00:00:00', '2012-02-21 04:55:09', '2011-03-04 00:00:00', 'HDNZ', '-', 'DINZ', 'Liam Fitzpatrick', 3, 'CLOSED', '03236150', 'SAMETIME CONNECTION ISSUES', '2012-02-21 04:55:09')
  [1m[36m (1.5ms)[0m  [1mCOMMIT[0m
  [1m[35mTicket Load (64.5ms)[0m  SELECT `tickets`.* FROM `tickets` WHERE `tickets`.`ticketid` = '03261509' LIMIT 1
  [1m[36m (0.2ms)[0m  [1mBEGIN[0m
  [1m[35mTicket Exists (20.8ms)[0m  SELECT 1 FROM `tickets` WHERE `tickets`.`ticketid` = BINARY '03261509' LIMIT 1
  [1m[36mSQL (0.4ms)[0m  [1mINSERT INTO `tickets` (`accountname`, `causecode`, `closedate`, `created_at`, `opendate`, `reportergrp`, `resolutiondesc`, `resolvergrp`, `resolvername`, `severity`, `status`, `ticketid`, `title`, `updated_at`) VALUES ('WESTPAC', ' ', NULL, '2012-02-21 04:55:09', '2011-05-08 00:00:00', 'OPSNZ', '-', 'ANNZ', 'Anusha Konduti', 3, 'OPEN', '03261509', 'P2PTSM002:-INFOMAN ONLY (TONZ): ANR2578W SCHEDULE WEEKLY_SYS', '2012-02-21 04:55:09')[0m
  [1m[35m (1.4ms)[0m  COMMIT

也许您可以尝试将行批量插入数据库,而不是单行插入。我很幸运,使用这种方法提高了性能


有一种很好的宝石叫做。只需将所有新对象收集到一个数组中,并在循环结束时批量插入它们

也许您可以尝试在数据库中批量插入行,而不是单行插入。我很幸运,使用这种方法提高了性能


有一种很好的宝石叫做。只需将所有新对象收集到一个数组中,并在循环结束时批量插入它们

无论出于什么原因,InnoDB都花了很长时间。我切换到了iSAM,速度完全快了很多。

无论出于什么原因,InnoDB都花了很长时间。我切换到了iSAM,速度完全快了很多。

您是否考虑过直接将CSV导入MySQL,使用SQL清理,然后将清理后的内容复制到最终位置?我喜欢Ruby,但MySQL在数据争用方面会快得多。我不同意你的说法。不过,如果可能的话,我想尽量将其保存在一个包中,也就是说,我想将其与RAILS应用程序隔离。您可以从RAILS内部执行任意SQL。您是否考虑过将CSV直接导入MySQL,使用SQL进行清理,然后将清理后的内容复制到其最终位置?我喜欢Ruby,但MySQL在数据争用方面会快得多。我不同意你的说法。不过,如果可能的话,我想尽量将其保存在一个包中,也就是说,我想将其与RAILS应用程序隔离。您可以从RAILS内部执行任意SQL。哦,太好了!我一定要试一试。我认为这样的解决方案是因为它当前的循环非常重。我的数据的一个方面是,某些行包含一个新的票证,而其他行将是我已经添加但已更新数据的票证。批量插入会自动覆盖数据吗?我相信可以选择同步现有记录,但我个人没有这方面的经验。检查源代码哦,很好!我一定要试一试。我认为这样的解决方案是因为它当前的循环非常重。我的数据的一个方面是,某些行包含一个新的票证,而其他行将是我已经添加但已更新数据的票证。批量插入会自动覆盖数据吗?我相信可以选择同步现有记录,但我个人没有这方面的经验。检查来源