47
Optimización, rendimiento y escalabilidad en ActiveRecord Emili Parreño www.eparreno.com Conferencia Rails 2008

Optimización, rendimiento y escalabilidad en ActiveRecord

Embed Size (px)

DESCRIPTION

Presentación de la ponencia que realicé en la conferencia Rails 2008 titulada: "Optimización, rendimiento y escalabilidad en ActiveRecord"

Citation preview

Page 1: Optimización, rendimiento y escalabilidad en ActiveRecord

Optimización, rendimiento y escalabilidad en ActiveRecord

Emili Parreñowww.eparreno.com

Conferencia Rails 2008

Page 2: Optimización, rendimiento y escalabilidad en ActiveRecord

Introducción

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

optimizar != escalar

Page 3: Optimización, rendimiento y escalabilidad en ActiveRecord

“La optimización es el proceso de búsqueda de la mejor manera de realizar un proceso, con respecto a uno o más recursos, que pueden ser: tiempo de ejecución, uso de memoria, uso de CPU...”

Introducción

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 4: Optimización, rendimiento y escalabilidad en ActiveRecord

Introducción

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

“La escalabilidad es la propiedad deseable de un sistema, que indica su habilidad para, o bien manejar el crecimiento continuo de trabajo de manera fluida, o bien para estar preparado para hacerse más grande con un impacto mínimo en el redimiento.”

Page 5: Optimización, rendimiento y escalabilidad en ActiveRecord

Introducción

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

“El rendimiento es la relación entre los resultados obtenidos y los recursos utilizados.”

e = resultados / recursos

Page 6: Optimización, rendimiento y escalabilidad en ActiveRecord

Optimización => Aumentar el rendimiento

Escalabilidad => Mantener el rendimiento

Introducción

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 7: Optimización, rendimiento y escalabilidad en ActiveRecord

Reducir el tamaño de las consultas

Consulta ineficiente:

def index @users = User.find(:all, :limit => 20)end

SELECT * FROM users LIMIT 0,10 ORDER BY id desc

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 8: Optimización, rendimiento y escalabilidad en ActiveRecord

Reducir el tamaño de las consultas

Optimización:

def index @users = User.find(:all, :select => “id, name”, :limit => 20)end

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 9: Optimización, rendimiento y escalabilidad en ActiveRecord

Reducir el tamaño de las consultas

Benchmark (75000 registros):

#1 User.find(:all, :limit => 20)

#2 User.find(:all, :limit => 20, :select => “id, name, surname”)

user system total real#1 0.000000 0.000000 0.000000 ( 0.000927)#2 0.000000 0.000000 0.000000 ( 0.000552)

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 10: Optimización, rendimiento y escalabilidad en ActiveRecord

Reducir el tamaño de las consultas

Benchmark (75000 registros):

#1 User.find(:all)

#2 User.find(:all, :select => “id, name, surname”)

user system total real#1 6.520000 0.350000 6.870000 ( 7.945950)#2 2.140000 0.040000 2.180000 ( 2.931969)

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 11: Optimización, rendimiento y escalabilidad en ActiveRecord

Categorias - Ruby (1345) - Rails (2389) - Testing (345) - Performance (34)

for cat in @categories puts “#{cat.name} (#{cat.posts.count})”end

=> select count(*) from posts where category_id = id

500.000 posts x 4 categorias = 2.000.000 de registros consultados!!

Contadores

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 12: Optimización, rendimiento y escalabilidad en ActiveRecord

Optimización:

class Post < ActiveRecord::Base belongs_to :category, :counter_cache => trueend

create_table :categories do |t| t.string :name ... t.integer :posts_count, :default => 0 t.timestampsend

Contadores

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 13: Optimización, rendimiento y escalabilidad en ActiveRecord

for cat in @categories puts “#{cat.name} (#{cat.posts_count})”end

Evitamos los 2.000.000 de registros recorridos en 4 consultas

Contadores

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 14: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Consulta ineficiente:

def index @posts = Post.find(:all, :limit => 10)end

for post in @posts puts “Titulo:” + post.title puts “Autor:” + post.user.nameend

=> 1+10 querys

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 15: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Optimización:

def index @posts = Post.find(:all, :limit => 10, :include => :user)end

for post in @posts puts “Titulo:” + post.title puts “Autor:” + post.user.nameend

=> 1+1 querys

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 16: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Optimización:

def index @posts = Post.find(:all, :limit => 10, :select => “posts.id, posts.title”, :include => :user)end

for post in @posts puts “Titulo:” + post.title puts “Autor:” + post.user.nameend

=> 1+1 querys

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 17: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Problema:

Podríamos hacer...

def index @posts = Post.find(:all, :limit => 10, :select => “posts.id, posts.title, user.id, user.name”, :include => :user)end

Pero no funciona :(

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 18: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Eager Loading

Optimización:

def index @posts = Post.find(:all, :limit => 10, :select => "posts.title, users.name AS user_name", :joins => [:user])end

for post in @posts puts “Titulo:” + post.title puts “Autor:” + post.user_nameend

=> 1 query

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 19: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Consulta ineficiente:

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

for comment in @post.comments puts comment.body puts “Autor:” + comment.user.nameend

=> 1 query para el post=> 1 query para los comentarios=> N querys para los usuarios

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 20: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Optimización:

def index @post = Post.find(params[:id]) @comments = @post.comments.find(:all, :include => :user)end

for comment in @comments puts comment.body puts “Autor:” + comment.user.nameend

=> 1 query para el post=> 1 query para los comentarios=> 1 query para los usuarios

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 21: Optimización, rendimiento y escalabilidad en ActiveRecord

Eager Loading

Optimización:

def index @post = Post.find(params[:id],

:include => [:user, :comments])end

puts “Post:” + @post.bodyputs “Autor:” + @post.user.name

for comment in @post.comments puts “Comentario:” + comment.bodyend

=> 3 querys

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 22: Optimización, rendimiento y escalabilidad en ActiveRecord

Tareas en background

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Si con lo anterior no es suficiente para optimizar una consulta siempre nos queda el Método de toda la vida:

User.find_by_sql(SELECT id, name, surname WHERE user.name = 'Pepe')

Page 23: Optimización, rendimiento y escalabilidad en ActiveRecord

Tareas en background

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Sacar tareas fuera del ciclo del request

- Envío de emails- Cálculos- Tareas de mantenimiento...

Page 24: Optimización, rendimiento y escalabilidad en ActiveRecord

Tareas en background

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Opciones:

- script/runner- daemon_generator- BackgrounDRB- Spawn- Starling...

Page 25: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Consulta ineficiente:

@users = User.find(:all, :conditions => “name = Pepe”)

Recorre toda la tabla

Page 26: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Evitar consultas que recorran toda la tabla (*)

Page 27: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Restricciones:

- No utilizar índices en columnas que se actualizan frecuentemente

- No utilizar índices en columnas con poca variación (p.e. booleanos)

- No utilizar Índices en tablas pequeñas

- No utilizar índices muy grandes

Page 28: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Optimización

Añadimos un índice en el campo “name” con una migración

add_index :users, :name

Benchmark 75000 usuarios

@users = User.find(:all, :conditions => “name = Pepe”)

user system total real#1 0.010000 0.000000 0.010000 ( 0.631635)#2 0.010000 0.000000 0.010000 ( 0.015232)

Page 29: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Podemos añadir índices multicolumna

add_index :users, [:name, :city]

No permite índices de más de 1024 bytes

En UTF-8 cada carácter necesita 3 bytes(256+256)*3 = 1536 bytes1536 x 500000 = 768 MB

Limitar el tamaño de los campost.column :login, :string, :limit => 10, :null => false

Page 30: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Definir la longitud del índice

def self.up execute "CREATE INDEX full_name ON users (name(10), surname(10))"end

Page 31: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Índices

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

“La regla de la izquierda”

Si se utilizan índices multicolumna en las cláusulas WHERE, hay que incluir siempre de izquierda a derecha las columnas indexadas

add_index :users, [:name, :surname, :city]

SELECT * from users WHERE city = ʻMadridʼSELECT * from users WHERE name = ʻPepeʼSELECT * from users WHERE name = ʻPepeʼ AND surname = ʻLopezʼ

Page 32: Optimización, rendimiento y escalabilidad en ActiveRecord

Índices y ordenación

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Podemos añadir índices multicolumna para ordenar

add_index :users, [:city, :created_at]

Page 33: Optimización, rendimiento y escalabilidad en ActiveRecord

MyISAM vs InnoDB

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Benchmark 75000 usuarios

User.count user system total real#1 0.000000 0.000000 0.000000 ( 0.663447) (InnoDB)#2 0.010000 0.000000 0.010000 ( 0.000688) (MyISAM)

User.find(:all) user system total real#1 11.040000 0.660000 11.700000 ( 12.585430) (InnoDB)#2 11.070000 0.670000 11.740000 ( 12.124938) (MyISAM)

User.find(:all, :conditions => “name = ‘Pepe’”) user system total real#1 0.010000 0.000000 0.010000 ( 0.015615) (InnoDB)#2 0.000000 0.000000 0.000000 ( 0.018247) (MyISAM)

Page 34: Optimización, rendimiento y escalabilidad en ActiveRecord

MyISAM vs InnoDB

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Benchmark 270.000 posts

Post.find_by_category_id(14)

#1 0.000000 0.000000 0.000000 ( 0.000865) (InnoDB)#2 0.000000 0.000000 0.000000 ( 0.001019) (MyISAM)

Page 35: Optimización, rendimiento y escalabilidad en ActiveRecord

MyISAM vs InnoDB

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Benchmark 270.000 posts

Post.find(:all, :conditions => 'body LIKE “Hello”’)

user system total real#1 0.000000 0.000000 0.000000 ( 3.471458) (InnoDB)#2 0.000000 0.000000 0.000000 ( 3.371270) (MyISAM)

Page 36: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Antes de empezar a optimizar: rellenar la base de datos

- populator- db-populate- babel...

Page 37: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Query Reviewer

Page 38: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 39: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

New Relic

Page 40: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 41: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 42: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 43: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Page 44: Optimización, rendimiento y escalabilidad en ActiveRecord

Herramientas

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Page 45: Optimización, rendimiento y escalabilidad en ActiveRecord

Resumen

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

• Optimizar es un proceso necesario y contínuo

• Optimizar a medida que desarrollamos o cuando refactorizamos

• Utilizar herramientas para encontrar “slow querys”

Page 46: Optimización, rendimiento y escalabilidad en ActiveRecord

“Constraints force creativity.”

Resumen

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Getting Real - 37 Signals

Page 47: Optimización, rendimiento y escalabilidad en ActiveRecord

Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com

Emili Parreño - www.eparreno.com