草庐IT

导入方案的思考

Always_July 2023-03-28 原文

导入的背景

用户为了更方便进行批量的数据处理,系统提供导入功能来满足该需求。对研发人员来说,导入等价于批量处理数据。

导入存在的问题

系统层面:

  • 导入数据过多,导致内存溢出(OOM),系统负载飙升。
  • 大批量的事务提交。
  • 如果有对外依赖,外部依赖不稳定将导致 整个导入超时耗时。

用户层面:

  • 导入耗时过长,只能等待,无法做其他事情。如果存在超时的情况那更加难以接受。
  • 系统异常时,无法了解导入的结果。

方案

从用户层面的问题来看,长时间等待和无法了解导入的结果是无法忍受的。所以 系统应该 对导入任务进行异步处理,并提供导入结果查询。

从系统层面的问题来讲,既然是批量任务,那我们可以将其分解为小批量任务来处理。

一个实际的案例

背景:用户导入excel进行付款,这个过程需要根据导入的账单号批量到账单,然后对这个账单的金额修改,如果金额为0了,那么进行 账单状态的变更,并通知其他业务系统。

导入流程

  1. 提交异步任务
    初始化 导入任务,使用线程池 执行导入任务
  2. 解析
  3. 批量处理数据
    可以在解析到一定的条数时执行批量处理数据
  4. 插入导入结果
    一次批量处理完以后,插入导入结果。
  5. 完成任务

如上的方案是 解析一批数据,然后对这一批数据进行批量处理,并插入批量导入结果。

查询和下载

提供查询导入任务 和 下载导入结果以方便用户了解导入结果

表设计

使用的MySQL5.7.28

1.导入任务表

CREATE TABLE `t_import_task` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `file_name` varchar(50) NOT NULL COMMENT '导入文件名称',
  `type` tinyint(1) NOT NULL COMMENT '导入类型',
  `status` tinyint(1) NOT NULL COMMENT '导入状态,0 导入中 1 导入完成',
  `creator` bigint(20) NOT NULL COMMENT '操作人',
  `creator_name` varchar(20) DEFAULT NULL COMMENT '操作人姓名',
  `start_time` datetime NOT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '完成时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

2.导入任务详情表

CREATE TABLE `t_import_task_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `import_task_id` bigint(20) DEFAULT NULL COMMENT '导入任务ID',
  `row_index` int(11) DEFAULT NULL COMMENT '第几行 从1开始',
  `row_data` varchar(2000) DEFAULT NULL COMMENT '每一行的数据',
  `status` tinyint(1) NOT NULL COMMENT '导入状态,0 未导入 1 导入成功 2 导入失败',
  `failure_reason` varchar(200) DEFAULT NULL COMMENT '导入失败原因',
  PRIMARY KEY (`id`),
  KEY `idx_import_task_id` (`import_task_id`)
) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4

异常情况考虑

  1. 解析发生异常,会导致导入任务详情表中没有数据。此种情况用户也能观察到,因为导入的条数和下载的条数不相同。
  2. 解析到一半时,系统重启。此种情况和上面的情况一致。

进一步优化

  1. 多线程解析,加快解析速度。
  2. 多线程批量处理数据,加快处理速度。
  3. 导入结果数据过多,导致导入详情表数据过多,可以考虑定时清除导入数据。
  4. 在页面提供单行数据修改,并再页面提供单行数据重试功能。

有关导入方案的思考的更多相关文章

  1. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  2. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  3. Ruby 守护进程和 JRuby - 备选方案 - 2

    我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts

  4. Ruby:如何从另一个文件导入变量? - 2

    我正在尝试创建一个与compass一起使用的本地配置文件,这样我们就可以处理开发人员机器上的不同导入路径。到目前为止,我已经尝试将文件导入到异常block中,以防它不存在,然后进一步使用该变量:local_config.rbVENV_FOLDER='venv'config.rbVENV_FOLDER='.'beginrequire'local_config.rb'rescueLoadErrorendputsVENV_FOLDER通常我是一名Python开发人员,所以我希望导入将VENV_FOLDER的值更改为venv,但它仍然是。之后。有没有一种方法可以导入local_config.r

  5. ruby - Ruby 导入的方法总是私有(private)的吗? - 2

    最好用一个例子来解释:文件1.rb:deffooputs123end文件2.rb:classArequire'file1'endA.new.foo将给出错误“':调用了私有(private)方法'foo'”。我可以通过执行A.new.send("foo")来解决这个问题,但是有没有办法公开导入的方法?编辑:澄清一下,我没有混淆include和require。另外,我不能使用正常包含的原因(正如许多人正确指出的那样)是因为这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app--includefile1.rb”,应用程序的行为将根据他在file1

  6. ruby-on-rails - Rails 导入 CSV 错误 : invalid byte sequence in UTF-8 - 2

    尝试在我的Rails应用程序中导入CSV文件时,出现错误UTF-8中的无效字节序列。一切正常,直到我添加了一个gsub方法来将其中一个CSV列与我的数据库中的一个字段进行比较。当我导入CSV文件时,我想检查每一行的地址是否包含在特定客户端的不同地址数组中。我有一个带有alt_addresses属性的客户端模型,其中包含客户端地址的几种不同可能格式。然后我有一个引用模型(如果您熟悉本地SEO,您就会知道这个术语)。引用模型没有地址字段,但它有一个nap_correct?字段(NAP代表“姓名”、“地址”、“电话号码”)。如果CSV行的名称、地址和电话号码与我在该客户的数据库中拥有的相同,

  7. ruby-on-rails - 能够处理 rar/tar/zip/7z 的 Ruby/rubyzip 替代方案? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我想知道是否有人知道Ruby的ruby​​zip替代品,它可以处理各种格式,特别是zip/rar/7z?我知道libarchive,但它对我的目的来说并不完整(它是一个很好的gem)。(澄清一下,libarchive-对我不起作用-因为

  8. ruby-on-rails - 对于 Ruby 应用程序,是否有比 Sanitize 更好的替代方案? - 2

    我爱Sanitize.这是一个了不起的实用程序。我遇到的唯一问题是,它需要永远准备一个开发环境,因为它使用Nokogiri,这对编译时间来说是一种痛苦。是否有任何程序可以在不使用Nokogiri的情况下执行Sanitize的操作(如果没有别的,只是温和地执行它的操作)?这将以指数方式提供帮助! 最佳答案 Rails有自己的SanitizeHelper。根据http://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html,它将Thissanitizehe

  9. ruby-on-rails - rails3 中 cron 作业的解决方案 - 2

    我尝试每天在我的Rails应用程序中自动记录一些数据。我想知道是否有人知道一个好的解决方案?我找到了https://github.com/javan/whenever,但我想确保在选择之前了解所有选项。谢谢!艾略特 最佳答案 我真的很喜欢whenever-这是一个很棒的Gem,我已经在生产中使用了它。关于它还有一个很好的Railscasts插曲:http://railscasts.com/episodes/164-cron-in-ruby 关于ruby-on-rails-rails3中c

  10. ruby - git:从 bitbucket 导出并导入 github(带提交) - 2

    我在bitbucket上创建了一个私有(private)git存储库并提交了代码。现在我想导出所有(提交、代码、历史记录)并将其导入github上的gitrepo。有没有办法做到这一点?谢谢 最佳答案 在本地检查所有内容到您的计算机和gitpull。创建一个github存储库将此存储库添加为您的第二个远程(“使用gitremote添加githubURL”)推送到第二个Remote 关于ruby-git:从bitbucket导出并导入github(带提交),我们在StackOverflow

随机推荐