1. ActiveRecord查询链优化
- 减少不必要的查询:
- 例如,假设我们有一个商品模型
Product
,在获取商品列表时,避免重复获取已经在关联表中缓存的信息。
- 优化前:
products = Product.all
products.each do |product|
puts product.category.name
end
- 这种方式会对每个产品执行一次查询获取其分类名称,N + 1问题。
- 优化后:
products = Product.includes(:category).all
products.each do |product|
puts product.category.name
end
- 使用
includes
方法进行预加载,只需要一次查询获取产品及其关联的分类信息。
- 性能测试数据:假设原来获取100个产品及其分类名称,优化前需要101次数据库查询,总时间为1000ms(假设每次查询10ms);优化后只需要2次查询,总时间为20ms,性能提升约98%。
- 合理使用索引字段:
- 在查询时,确保使用的字段上有索引。例如,如果经常按价格范围搜索商品:
- 优化前:
products = Product.where('price BETWEEN? AND?', 10, 100)
- 如果
price
字段没有索引,查询会很慢。
- 优化后:
- 先在数据库层面添加索引:
add_index :products, :price
- 再执行查询:
products = Product.where('price BETWEEN? AND?', 10, 100)
- 性能测试数据:假设优化前查询1000个符合价格范围的产品需要5000ms,添加索引后,查询时间缩短到500ms,性能提升90%。
2. Ruby对象的复用策略
- 对象池:
- 对于一些创建成本较高的对象,如数据库连接对象。假设我们有一个自定义的数据库连接类
CustomDBConnection
。
- 优化前:
def get_data
connection = CustomDBConnection.new
data = connection.query('SELECT * FROM products')
connection.close
data
end
- 每次调用
get_data
方法都会创建和销毁一个新的数据库连接对象。
- 优化后:
class ConnectionPool
def initialize(size)
@pool = Array.new(size) { CustomDBConnection.new }
end
def get_connection
@pool.pop || CustomDBConnection.new
end
def return_connection(connection)
@pool.push(connection)
end
end
pool = ConnectionPool.new(10)
def get_data
connection = pool.get_connection
data = connection.query('SELECT * FROM products')
pool.return_connection(connection)
data
end
- 通过对象池复用连接对象,减少创建和销毁的开销。
- 性能测试数据:假设优化前创建和销毁一个连接对象平均需要100ms,调用
get_data
方法100次共需要10000ms;优化后,复用连接对象,调用100次get_data
方法,除了初始化池的开销外,假设额外开销为1000ms,性能提升约90%。
- 缓存中间结果:
- 例如,在计算商品的一些复杂属性时,缓存计算结果。假设商品有一个复杂的评分计算方法
calculate_rating
。
- 优化前:
class Product
def calculate_rating
# 复杂的计算逻辑,例如根据销量、评论等计算
sleep(1) # 模拟复杂计算耗时
4.5
end
end
product = Product.new
puts product.calculate_rating
puts product.calculate_rating
- 每次调用
calculate_rating
都会重新计算,浪费时间。
- 优化后:
class Product
def calculate_rating
@rating ||= begin
# 复杂的计算逻辑,例如根据销量、评论等计算
sleep(1) # 模拟复杂计算耗时
4.5
end
@rating
end
end
product = Product.new
puts product.calculate_rating
puts product.calculate_rating
- 使用
@rating
变量缓存计算结果,第二次调用时直接返回缓存值。
- 性能测试数据:假设优化前两次调用
calculate_rating
共需要2000ms,优化后第二次调用几乎不耗时,总时间为1000ms,性能提升50%。
3. 利用Ruby的元编程能力进行整体优化
- 动态方法定义:
- 假设我们有一个商品模型
Product
,需要根据不同的条件动态生成查询方法。
- 优化前:
def get_products_by_condition(condition)
case condition
when 'high_price'
Product.where('price > 100')
when 'low_price'
Product.where('price < 50')
end
end
- 这种方式代码冗长,每次添加新条件都需要修改方法。
- 优化后:
class Product
def self.define_search_method(condition)
define_method("get_products_by_#{condition}") do
case condition
when 'high_price'
where('price > 100')
when 'low_price'
where('price < 50')
end
end
end
end
Product.define_search_method('high_price')
Product.define_search_method('low_price')
puts Product.get_products_by_high_price
puts Product.get_products_by_low_price
- 通过元编程动态定义方法,使代码更灵活,减少重复代码。
- 性能测试数据:假设优化前每次调用
get_products_by_condition
判断逻辑耗时100ms,优化后直接调用动态方法,假设方法调用开销为10ms,性能提升约90%。
- Aspect - Oriented Programming (AOP) 思想利用:
- 例如,我们可以通过元编程为商品模型的方法添加日志记录功能。
- 优化前:
class Product
def update_price(new_price)
# 更新价格逻辑
@price = new_price
end
end
class Product
def self.add_logging_to(method_name)
original_method = instance_method(method_name)
define_method(method_name) do |*args, &block|
Rails.logger.info("Calling method #{method_name} with args: #{args.inspect}")
result = original_method.bind(self).(*args, &block)
Rails.logger.info("Method #{method_name} returned: #{result.inspect}")
result
end
end
add_logging_to :update_price
def update_price(new_price)
# 更新价格逻辑
@price = new_price
end
end
- 虽然这种方式主要不是为了性能优化,但通过减少重复的日志记录代码,使代码结构更清晰,在大型项目中便于维护,从长远角度有助于整体性能管理。
- 性能测试数据:添加日志记录功能后,假设每次方法调用增加的日志记录开销为50ms,相对于整体业务逻辑执行时间,如果整体业务逻辑执行时间为1000ms,增加的开销占比5%,但带来了更好的可维护性。