**优化--后端**: 计数缓存counter_cache; rack-mini-profiler(2300) ; bullet(5000✨):侦测N+1query...

rack-mini-profiler 这个 gem,可以永远显示网页的加载时间。(2300✨)开发环境和产品环境都可以用。(生成非常详细的报告)

  • development环境,直接使用gem 'rack-mini-profiler'
  • production环境,
  • 1.  gem 'rack-mini-profiler', require: false ,让
  • 2.  然后运行bundle exec rails g rack_profiler:install, 这样在开发环境下也可以使用。

 

以下服务可以收集网站实际营运的数据,找出哪些部分是效能不好的地方加以改善:

  • New Relic
  • Skylight
  • Scout

后端效能提速的方向

对后端来说,一个方向是提供 Rails 和 Ruby 代码的效能,一个方向是提供数据库方面的效能。

主要是query查询SQL的效能提升空间大。

 

1. 避免N + 1的query

includes():让你存取Post模型的关联对象user的属性,却不会产生额外的查询语句。用于简单的join的执行速度的改进。

可以嵌套,可以指定多个关系,可以附加where(具体看API)

 

对关联,可以使用:

def index
 @posts = Post.includes(:user).page(params[:page])
end

 SELECT "posts".* FROM "posts" LIMIT ? [["LIMIT", 2]]
 SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 5)

def show
  @post = Post.find(params[:id])
  @comments = @post.comments.includes(:user)
end

 

多个关联,和加嵌套:

def index
  @posts = Post.includes(:user, :liked_users, {:comments => :user}).page(params[:page])
end

{:comments => :user}是因为post关联comments,下一层comments关联了user。

<td><%= post.comments.map{ |c| c.user.display_name }%></td>

 

加上条件查询:

可以在include()后面直接加上where()。

使用scope(name, body, &block),预先设置查询的rails语法. 就是一个类方法。返回一个ActiveRecord::Relation。

可以把返回结果当array,用Enumerable的方法。

scope可以连接scope或类方法。

 

例子:

如果希望在index页面只显示公开的comments,即comment的status属性的值是public,
同时不希望产生N+1query,

改MVC模型:

则在Models/post.rb中添加:

has_many :comments, -> { where( status: "public")}, class_name: "Comment"

在Controllers/posts_controller.rb中的index方法中修改:

{:comment => :user}改为{:visible_comment => :user}

在views/posts/index.html.erb中修改:

post.comments为post.visble_comments

 


 

Bullet在开发时协助侦测 N+1 queries 

gem https://github.com/flyerhzm/bullet (5500✨)

注意:在配置时,注释掉不需要的gem的功能。然后打开页面,如果遇到N+1 query就会弹出窗口。

Rails.application.configure do

config.after_initialize do
  Bullet.enable = true
  Bullet.alert = true     #弹出提示框。
  Bullet.bullet_logger = true
  Bullet.console = true
  Bullet.rails_logger = true  #在terminal显示提升
  Bullet.add_footer = true   #在当前页角显示提升
end

end

 


 

activerecord内存的优化

1.不要用all,可以使用分页的gem。 will_paginate 或 kaminari

 

2. 真要捞全部数据,使用批处理方法 find_each ,find_in_batches。

例子: 在数据库内新增一个字段,并赋值。

db/migrate/2017XXXXXXXXXX_add_date_to_posts.rb

   class AddDateToPosts < ActiveRecord::Migration[5.1]def change
+      add_column :posts, :date, :date
+
+      Post.find_each do |post|
+        post.date = post.created_at.to_date
+        post.save( :validate => false )
+      endend
   end

 

3. Preload技术,使用复杂的语法糖(复杂的Sql)一次性从数据库捞出所有想要的数据,存入了内存。

对不同的需求,使用不同的调用方法从内存中获取记录。

def show
@post = Post.find(params[:id])

  if current_user
    all_comments = @post.comments.where("status = ? OR (status = ? AND user_id = ?)","public", "private", current_user.id).includes(:user)
    @comments = all_comments.select{ |x| x.status == "public" }
    @my_comments = all_comments.select{ |x| x.status == "private" }
  else
    @comments = @post.comments.visible.includes(:user)
  end
end

对应的查询语法:

SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? AND (status = 'private' OR (status = 'public' AND user_id = 1))  [["post_id", 1]]

 

4. count和size: size可以返回已经加载的collection的数量,如果没有加载则调用count的sql方法。所以在collection中都用size,

 

总结:sql优化,是优化已经实现的功能。不可能有先见之明。

 

5. pluck()选择字段,在只需要记录的部分字段的情况下使用的。可以节省内存。直接返回一个数组。

Post.all.map{|x| x.id } 是先捞出输出生成ActiveRecord,然后再生成数组

Post.pluck(:id)直接捞需要的数据生成数组,不会生成ActiveRecord。

 

pluck()和select()用有区别,select()有2个用法:

1。select()返回的是activerecord对象集合。

⚠️比较好的写法是: Post.select(:id, :column_name, ...)这样会带上id。否则返回的activerecord对象不带id索引.

2。select{|x| 条件 } :这是另一种用法,返回数组对象集合

 


 

数据库的sql优化:

 

使用复杂的sql语法,进行如报表之类的计算。因为在数据库中的计算,比调出数据再用Ruby计算的速度快很多。

因此, 如COUNT, MAX, MIN, SUM, AVG等都直接用数据库计算比较好。

对应的Rails语法糖:count(), maximum(), minimum(), sum(), average().

 

例子:

SQL:

SELECT  posts.*, COUNT(subscriptions.id) as subscriptions_count FROM "posts" INNER JOIN "subscriptions" ON "subscriptions"."post_id" = "posts"."id" GROUP BY posts.id

Rails语法糖:

Post.joins(:subscriptions).group("posts.id").select("posts.*, count(subscriptions.id) as subscriptions_count")

group()和select()可以换顺序,不影响SQL。

 

解释:group by 用于集合计算的函数并分组显示结果。

 


 

使用计数缓存counter_cache 

 

比如以前没有使用counter_cache,现在已经有了一堆相关的记录。可以:

1. rails g migration AddXxxToXxxs xxx:type,然后在db/migrate/XXX_add_xxx中添加:

2. add_column :users, :posts_count, :integer, default: 0, null: false

   User.pluck(:id).each  {|id| User.reset_counters(id, :posts)} 

⚠️:reset_counters(id, *counters, touch: nil)

3. 在posts.rb中加上 belongs_to :user, counter_cache: true

4. 在user的posts数量发生变化时,可以更新它使用update_counters(id, counters, touch: nil)

User.update_counters(user.id,  posts_count: 1 )

⚠️:id, 想要更新一个counter的对象的id或者,一数组ids

⚠️:counters是hash, 要更新的字段的名字做key,字段的值是value.

⚠️: 如果设置选项touch:true代表更新时间戳updated_at。

 

Rails 内建的 Counter Cache 功能比较简单,如果你需要更多功能,请参考 https://github.com/magnusvk/counter_culture 这个 gem。

 

小结论:什么时候用逆规范化做优化?

如果不常显示该数据, 可以用纯 SQL 的方式来解决。但是如果需要经常显示该数据,就可以考虑用逆规范化的方式,将数据缓存下来。这样效能可以更好。但是缺点就是需要维护该数据的正确性,要写的 Ruby 代码也比较多。

考量:读取的频率 v.s. 更新缓存数据的成本。

比如:点赞数,关注数。无需时时更新。可以用逆规范化。

 


 

 

改进render partial的效能(比较小)

<% @posts.each do |post| %>
  <%= render partial: "post", locals: {post: post}%>
<% end %>

改为用a collection of partials:
<%= render partial: "post", collection: @posts, as: :post%>


 

数据库索引 (加快查询速度,但会占内存)

add_index(table_name, column_name, options={}) ,各种例子的用法见API

 

以下需要加上index:

  • 外部键(Foreign key)
  • 会被排序的字段(被放在order方法中)
  • 会被查询的字段(被放在where方法中)
  • 会被group的字段(被放在group方法中)
  • order("id desc")等同于created_at desc,效能更好。这是因为id是integer格式,而created_at是datetime格式。因此⚠️index对不同的数据格式的效率也不同。

效率上Boolean > integer > String > Date > Datetime


内存缓存

超高流量的网站会需要用到缓存来进一步提升后端效能。本教程没有提及如何做缓存,有兴趣的同学请直接看老师的Rails 实战圣经。

 

Rails的各类内存缓存,比如俄罗斯套娃机制,可以看guide。博客里也有相应文章。

 

转载于:https://www.cnblogs.com/chentianwei/p/9398380.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366321.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python学习日记(匿名函数)

匿名函数 简介 匿名函数&#xff1a;为了解决那些功能很简单的需求而设计的一句话函数。 python 使用 lambda 来创建匿名函数。 所谓匿名&#xff0c;意即不再使用 def 语句这样标准的形式定义一个函数。 1 lambda 只是一个表达式&#xff0c;函数体比 def 简单很多。 2 lambda…

列表相关元素及其属性

HTML5保留了如下几个列表相关元素&#xff1a;无序列表<ul>、有序列表<ol>、自定义列表<dl> 1、<ul>&#xff1a;定义无序列表&#xff0c;可以指定id、style、class等属性&#xff0c;还可以指定onclick等事件属性。 2、<ol>&#xff1a;定义有…

唯一的hashCodes不足以避免冲突

有一个常见的误解&#xff0c;即如果您具有唯一的hashCode&#xff08;&#xff09;&#xff0c;则不会发生冲突。 虽然唯一或几乎唯一的hashCodes很好&#xff0c;但这还不是故事的结局。 问题在于HashMap的大小不是无限的&#xff08;或大小至少为2 ^ 32&#xff09;&#x…

Spring Boot使用AOP实现拦截某个方法

1、引入.jarl文件依赖 <!-- Spring Boot Web 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 从依赖信息里移除 Tomcat配置 --><exclusions><exc…

DB 数据同步到数据仓库的架构与实践

背景 在数据仓库建模中&#xff0c;未经任何加工处理的原始业务层数据&#xff0c;我们称之为ODS&#xff08;Operational Data Store&#xff09;数据。在互联网企业中&#xff0c;常见的ODS数据有业务日志数据&#xff08;Log&#xff09;和业务DB数据&#xff08;DB&#xf…

起名与选择器~(总结类、持续更新系列)

废话没有&#xff0c;直接干活 一、起名方式&#xff1a;1.元素自身的标签名&#xff1b; 2.利用 class 属性自定义名称&#xff1b; 3.利用 id 属性自定义名称。共三种&#xff0c;其中class使用居多。 起名规范:不能用汉字起 第一个字母不能是特殊符号 第一个字母必须是英文字…

java log.error_Logger.error打印错误异常的详细堆栈信息

一、问题场景使用Logger.error方法时只能打印出异常类型&#xff0c;无法打印出详细的堆栈信息&#xff0c;使得定位问题变得困难和不方便。二、先放出结论Logger类下有多个不同的error方法&#xff0c;根据传入参数的个数及类型的不同&#xff0c;自动选择不同的重载方法。当e…

笔记本电脑怎么清理灰尘_手机声音越用越小怎么办?一段黑科技音波就能清理扬声器灰尘...

大家好&#xff0c;欢迎收看科技狐&#xff0c;我是小狐。我们都知道&#xff0c;随着手机的使用时间越来越长&#xff0c;手机扬声器里面会积赞一些灰尘。因此手机的声音就会变得越来越小。有时候连电话铃声都听不清楚&#xff0c;说实话我就是这个样子&#xff0c;为此我困扰…

Java方法中的参数太多,第8部分:工具

在我的系列文章的前七篇文章中&#xff0c;有关处理Java方法中期望的参数过多的内容集中在减少方法或构造函数期望的参数数量的替代方法上。 在本系列的第八篇文章中&#xff0c;我将介绍一些工具&#xff0c;这些工具可帮助您确定可能存在过多参数的情况&#xff0c;并在出现这…

Java _类 相关知识

成员变量java对象的属性就是成员变量,其实成员变量就是指普通的变量,可以设置初始值,也可以不设置.当不设置时,会被设置为默认值.(当成员变量前面有private关键词时,说明定义了一个私有成员) java中使用class关键词定义一个类, 成员方法java语言中使用成员方法来对应于类对象的…

linux操作系统好吗_国内可以通过安卓+termux打造出适用手机平板和电脑全平台最好的操作系统...

我觉得国内可以基于安卓上的termux要搞一个termux软件商店&#xff0c;用户可以从termux软件商店里安装c,c,java,python,vim,jupyter notebook,php&#xff0c;R等软件&#xff0c;并在安卓手机和安卓平板桌面上生成这些软件的图标&#xff0c;用户可以直接点击这些python,vim,…

java it_关于 Java Iterator(迭代器)学习笔记

相信 “迭代” 对于Java程序员来说并不陌生(当然&#xff0c;其他语言的程序员也是如此)&#xff0c;在处理数据时&#xff0c;不可避免地会存在对数据的大量遍历操作。对于我自己而言&#xff0c;学习使用Java语言两年时间&#xff0c;对于“迭代”的概念还停留在 for:each,fo…

scss-!optional

optional翻译成汉语具有"可选的"的意思。顾名思义&#xff0c;!optional标记前面的扩展不必须生成一个新的选择器。看一段SCSS代码片段&#xff1a; p{color:red;extend .notice } 由于并不存在一个名为notice的样式类&#xff0c;所以上述代码会报错。代码修改如下&…

Http(s)与后台交互方式

前言 Http(s)是前后端交互的主要方式之一&#xff0c;交互技术主要有:Ajax(XMLHttpRequest)、Fetch、地址跳转(window.open、location.href)。Http(s)与后台交互传递数据的部分有:请求网址、请求头、请求主体、响应头、响应主体 请求网址 请求网址是通过pathname或search进行传…

Python的字符串格式化

Python的字符串格式化有两种方式: 百分号方式、format方式 百分号的方式相对来说比较老&#xff0c;而format方式则是比较先进的方式&#xff0c;企图替换古老的方式&#xff0c;目前两者并存。 This PEP proposes a new system for built-in string formatting operations, in…

使用Spring Form标签探索Spring Controller

在上一篇文章中 &#xff0c;我向您展示了如何使用Spring控制器处理纯HTML表单。 但是处理表单的更强大的方法是使用Spring的ModelAttribute及其spring:form标签。 我将向您展示如何通过修改上一篇文章的项目设置从这里开始。 我们将简单地修改Comment表单和控制器以使用此功能…

predict函数 R_学习|R语言做机器学习的常用函数总结

预测函数&#xff1a;predict() type"prob"判别该量度的昆虫归类为A、B和C的概率&#xff1b;type"response"&#xff1a;判别该量度的昆虫的类别&#xff1b;预测分类的概率的函数predict(…, type)参数type&#xff1a;R语音里面不同模型&#xff0c;参数…

微信小程序:动画(Animation)

简单总结一下微信动画的实现及执行步骤。 一、实现方式 官方文档是这样说的&#xff1a;①创建一个动画实例 animation。②调用实例的方法来描述动画。③最后通过动画实例的 export 方法导出动画数据传递给组件的 animation 属性。 因为小程序是数据驱动的&#xff0c;给这句话…

java map clone_Java中HashMap的clone()方法: java.util.HashMap.clone() - Break易站

Java中的HashMapjava.util.HashMap.clone()方法用于返回所提到的哈希映射HashMap的浅表副本。它只是创建了map的副本。HashMap.clone句法&#xff1a;Hash_Map.clone()参数&#xff1a;该方法不接受任何参数。返回值&#xff1a;该方法只返回HashMap的副本。下面的程序用于说明…

计算模块分析

模块需要实现的功能&#xff1a;1、实现原始深度计算&#xff1a;五个输入、一个输出值和一个输出使能信号&#xff1b; 2、计算幅度值&#xff08;这个功能是一个使能控制的&#xff09;这个的话就是一个行结束信号和一个行输出使能信号&#xff1b; 3、需要告诉相连的模块什么…