Optimización, rendimiento y escalabilidad en ActiveRecord
Emili Parreñowww.eparreno.com
Conferencia Rails 2008
Introducción
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
optimizar != escalar
“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
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.”
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
Optimización => Aumentar el rendimiento
Escalabilidad => Mantener el rendimiento
Introducción
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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')
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...
Tareas en background
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Opciones:
- script/runner- daemon_generator- BackgrounDRB- Spawn- Starling...
Í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
Índices
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Evitar consultas que recorran toda la tabla (*)
Í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
Í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)
Í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
Í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
Í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ʼ
Í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]
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)
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)
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)
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...
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Query Reviewer
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
New Relic
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Herramientas
Herramientas
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
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”
“Constraints force creativity.”
Resumen
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Getting Real - 37 Signals
Optimización, rendimiento y escalabilidad en AR Emili Parreño - www.eparreno.com
Emili Parreño - www.eparreno.com
Top Related