前段时间遇到的一个问题,在这里记录下来。
需求:批量的将表A中的status 创建 到表B中 主键为id
例:
表offers id status --> 表offer_scores id offer_id status
1 online 1
2 offline 2
想要通过表offers得到表offer_scores,也就是批量创建。
看起来没有什么难度,尝试写一下试试看
# offers数据迁移至offer_scores Offer.find_each(batch_size: 1000) do |offer| OfferScore.create(offer_id: offer.id) end
好了 运行起来也没问题。
不过当我的数据有6万条的时候 (我的实际情况),性能问题就出现了,它跑得太慢了(大约跑了一个半小时还在跑,最后我不得不中断了它)
思考了一下之后,我决定优化一下这段语句
我查找了一下 发现了一个 好用的方法 叫做 find_in_batches
并且配合了一个gem 叫做 activerecord-import
去文档查看了一下用法, 用法很简单 和 find_in_batches 搭配很不错
这里边记录一下 find_in_batches的用法
通过查询文档 我们可以看出
find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
Person.where("age > 21").find_in_batches do |group| sleep(50) # Make sure it doesn't get too crowded in there! group.each { |person| person.party_all_night! } end Person.find_in_batches.with_index do |group, batch| puts "Processing group ##{batch}" group.each(&:recover_from_last_night!) end
它的查询是可以附带参数的batch_size 这个可以看出 默认的参数是一次查询1000条,恩符合我们要求,
另外 查询之后他会将我们查询的1000条数据放入 group中,好了,现在我们就可以对第一个创建方法进行改良了
Offer.find_in_batches do |group| scores = [] scores = group.map { |offer| OfferScore.new(offer_id: offer.id)} OfferScore.import scores end
首先我们按照一次查询1000的条件 把1000条查询出来,然后新建了 数组,将group 进行遍历 并new了一个 目标对象 存入了 details 中,然后执行 OfferScoreDetail.import details, 这是gem提供的方法,后边是附带参数,这个方法会将我们提供的details集合中的所有对象一次性创建出来。
然后我将上面的代码重运行一次,6w条数据大约执行了100秒左右,相对于第种还是有很大提升的
优化的思路:
如果逐条执行,将会产生大量的数据库查询和创建语句,并且rails每次和数据库交互都会产生链接语句
这些语句如果过多都会产生数据库性能问题,所以类似此类问题,可以优先尝试降低数据库连接次数,查询 次数来提高速度。
上边说了批量创建的问题,下边来说一下批量更新的问题。
假设还是上边的两张表
例:
表offers id status --> 表offer_scores id offer_id status
1 online 1 1
2 offline 2 2
我们的目标是把表offers中的 status更新到表offer_scores中
Offer.find_each(batch_size: 1000) do |offer| OfferScore.find_by(offer_id: offer.id).update_columns(status: offer.status) end
这句已经可以完成我们想要的情况了(我们此次更新使用了update_columns 不触发回调),如果只是简单的更新数据到另一个表 这样子虽然可行,但是显而易见的 性能还是很差,试着跑一下6W条数据,还是非常慢的。
如果不触发回调机制,也不更新索引只是单纯的更新数据,利用SQL语句是不是要更好呢?来试试看另一种方法。
ActiveRecord::Base.connection.exec 利用这个的方法我们就可以直接写SQL 语句了
ActiveRecord::Base.connection.execute("UPDATE offers JOIN offer_scores ON offers.id = offer_scores.offer_id SET offers.status = offer_scores.score")
利用上边这条SQL 6w条数据大约不到2秒就能执行完毕,速度是非常快了,但是有时候我们的业务很复杂并不是简简单单的更新数据就可以,有时候我们需要更新索引的时候就不能采用这种方式,我们要触发更新索引的回调。
例:
表offers id status --> 表offer_scores id offer_id status
1 online 1 1
2 offline 2 2
我们的目标是把表offers中的 status更新到表offer_scores中
同样的任务,但是这次我们需要触发回调
Offer.find_each(batch_size: 1000) do |offer| OfferScore.find_by(offer_id: offer.id).update_attributes(status: offer.status) end
利用 update_attributes就可以触发回调了,当然问题还是性能,首先我们考虑到是不是可以采用事务将更新语句打包一起提交而不是逐条更新呢?尝试一下。
Offer.find_each(batch: 1000) do |offer| ActiveRecord::Base::transaction do OfferScore.find_by(offer_id: offer.id).update_attributes(status: offer.status) end end
这样我们将所有的更新语句都放在事务中,不过这样子虽然所有的更新请求都是一起提交,减少了链接数据库的次数,但是如果一个事务中条目过多恐怕也会处理不过来,不如把每1000条作为一个事务发起请求。
Offer.find_in_batches do |offer_group| ActiveRecord::Base::transaction do offer_group.each do |offer| OfferScore.find_by(offer_id: offer.id).update_attributes(status: offer.status) end end end
这样 我们每次查询1000条打包扔到事务中,事务中将会产生1000条更新请求,我们作为一个请求,链接数据库进行更新。
优化数据库连接方法还有很多,以上方法还有很多可优化之处,以后再来添加。
相关推荐
配置完成后,Rails应用程序将能够根据这些参数与数据库建立连接,并执行CRUD(创建、读取、更新、删除)操作。Rails的ActiveRecord库提供了方便的数据模型抽象,使得开发者可以使用Ruby代码轻松操作数据库表。 总的...
Rails是Ruby on Rails的简称,它是一个基于Ruby语言的开源Web应用框架,而DSL则是用于简化数据库操作的语言特性。在Rails中,数据库DSL使得开发者可以更直观、简洁地编写数据库查询。 首先,让我们深入了解Rails中...
标题中的“Rails相关电子书汇总二”表明这是一个关于Ruby on Rails框架的电子书籍集合,特别是与Active Record数据库交互相关的主题...对于那些寻求深入理解Rails数据库操作的开发者来说,这本书无疑是一份宝贵的资源。
在Ruby on Rails框架中,数据库迁移是开发过程中不可或缺的一部分,用于管理数据库模式的变化。本文将深入探讨Rails中的rake工具和数据库迁移的概念,以及如何正确地利用它们。 首先,Rails中的Migration是数据库...
Rails的数据库操作主要通过ActiveRecord实现,它是一种ORM(对象关系映射)工具,使得Ruby对象可以直接操作数据库记录。 生成Web程序是Rails的一大特色。通过`rails generate`命令,可以自动生成控制器、模型、视图...
在 Ruby on Rails 3.1.0 中,对于数据库的操作非常便捷,主要包括数据的保存、创建、查找等功能。 1. **保存数据**: - `a = Category.new(:name => 'Ruby', :position => 1)` - `a.save` - `save` 方法用于保存...
默认情况下,Rails仅支持单个主数据库,但Multiverse允许开发者定义和管理多个数据库,使数据分片和扩展变得更加简单。 在Rails中使用Multiverse,首先需要安装相应的gem。在Gemfile中添加`gem 'multiverse'`,然后...
ActiveRecord是Rails的核心组件之一,它负责数据库操作和模型之间的关系管理。通过SecondBase,开发者可以方便地在不同的数据库之间切换,执行特定的数据操作,比如创建、迁移和进行测试。 创建数据库时,...
GORM,全称Grails Object Relational Mapping,是Grails框架中的数据持久化工具,它提供了简洁的API,使得开发者可以方便地进行数据库操作,类似于Java的Hibernate或Ruby on Rails的ActiveRecord。GORM不仅支持关系...
本压缩包包含了多个关键主题的详细文档,涵盖了从数据库操作到应用调试等多个方面。让我们逐一探索这些文件所涵盖的知识点。 1. **Active Record 查询接口**: Active Record 是 Rails 的核心组件之一,它负责模型和...
在"语言后端:Lin-guage应用程序的Ruby on Rails数据库"这一主题中,我们将深入探讨如何使用Rails构建一个语言学习应用的后端系统,特别是涉及到数据库的设计和管理。 首先,Rails遵循MVC(Model-View-Controller)...
3. **ActiveRecord**:Rails中的ActiveRecord是ORM(对象关系映射)库,它允许开发者用Ruby类和对象直接操作数据库,简化了数据库交互。 4. **路由**:Rails的路由系统是连接URL与控制器动作的桥梁,定义了URL模式...
在Ruby on Rails框架中,开发人员经常选择使用关系型数据库如SQLite、PostgreSQL或MySQL来存储数据。然而,随着NoSQL数据库的兴起,MongoDB因其灵活性和非结构化数据处理能力,也成为了许多Web应用程序的选择。本文...
### Rails 101 入门电子书知识点详解 #### 一、简介 ...通过以上内容的学习,初学者可以全面掌握Ruby on Rails的基础知识,包括环境搭建、基本操作、高级特性等,为后续更深入的学习打下坚实的基础。
1. **ActiveRecord**:这是Rails框架中的ORM(Object-Relational Mapping)组件,它负责处理数据库操作。通过ActiveRecord,开发者可以使用面向对象的方式来操作数据,而无需编写SQL语句。 2. **ActionController**...
布里洛Brillo是Rails数据库清理器和加载器,用于为开发机器制作生产数据库的轻量级副本,并且混淆了敏感信息。 大多数配置是通过YAML完成的:指定要备份的模型,想要与它们的关联以及应该混淆哪些字段(以及如何混淆...
Rails(Ruby on Rails)是一个采用Ruby语言编写的开源Web应用框架,它遵循模型-视图-控制器(MVC)的架构模式,设计用来快速开发数据库驱动的动态网页。随着Rails版本的更新迭代,此书聚焦于一个特定的版本,帮助...
Rails使用ActiveRecord作为ORM,它使得Ruby类可以直接与数据库表进行交互。在这个项目中,`User`和`Image`模型可能会与数据库中的相应表关联,通过定义属性和关系,如`has_many :images`表示一个用户可以拥有多个...
在数据库操作方面,你可以使用以下命令连接到数据库控制台: ```bash sudo rails dbconsole ``` 如果你在Ubuntu上,可能需要使用`sudo`以获得必要的权限。此外,Rails的迁移(Migrations)系统允许你对数据库结构...