busca_1 Atendimento   (11) 3681-1177   busca_3 busca_4
Logo Centro de Treinamento especializado em Ruby, Ruby on Rails e Metodologia ágil. rubyonrails

Pdf

Deixe o Postgres até 40% mais rápido em seu ActiveRecord

Últimamente notei que o postgres 8.3 estava superando o MySQL em meus testes de performance. Então resolvi testar os dois com ActiveRecord. Nesse caso o Postgres perdia para o MySQL então fui procurar onde estava o problema .
Eu já estava incomodado com o MySQL não suportar transação em alter tables e não possuir alter tables on-line (alter table on-line é quando ela é feita instânea, sem necessidade de cópia temporária da tabela). Isso atrabalha quando você tem migrations que falham durante a execução. Além disso, o fato das alterações de alter table não serem on-line pioram conforme os dados do seu cliente ficam maiores.
Sempre fiz testes comparando o MySQL x PostgreSQL x Firebird. Como todos os meus testes eram na versão 7 do Postgres (já faz algum tempo !) resolvi verificar no google se achava alguma coisa sobre a peformance do Postgres série 8.
Localizei no change log do Postgres 8.2 que havia melhoria na performance das queries e alguns artigos mostravam que a versão 8.3 era superior a 8.2, então resolvi compilar e testar.
Ao iniciar os testes fiquei espantado ao ver pela primeira vez o PostgreSQL bater o MySQL em velocidade.
Como os testes que tinha prontos eram em PHP, resolvi testar os dois no ActiveRecord. Fiz questão de efetuar um teste real, com os dados de nossos clientes. Então serializei os objetos ActiveRecord da seguinte forma:

File.open('produtos.yml', 'w+') << Produto::Base.find(:all).to_yaml

Com os objetos serializados era hora de exportar para o postgres então reconfirei meu projeto Rails para o postgres rodei um rake db:migrate e populei o projeto da seguinte forma

Produto::Base #essa linha e necessaria para o rais carregar a classe
YAML::load_file('produtos.yml').each{|obj| obj.send(:create) }

Nota: no rails 1.2.6 ele deixa usar o create direto, já no rails 2.0.2 não. E não posso usar save neste caso. Pronto ! Os dois bancos de dados estavam com dados idênticos e reais para testes.
Minha decepção foi rodar os testes e ver que o PostgreSQL estava perdendo em consultas grandes e ganhava do MySQL em consultas pequenas. Também notei que, sendo menor o número de COLUNAS, menor a diferença ficava.
Então refiz os testes utilizando os driver's nativos

mysql = MySQL.new(...)
20.times do
init = Time.now
mysql.query('SELECT ...')
puts "resultado do teste: #{Time.now - init}"
end


conn = PGconn.connect(...)
20.times do
init = Time.now
conn.exec('SELECT ...')
puts "resultado do teste: #{Time.now - init}"
end


Nesse caso, os testes representaram a mesma proporção do PHP (5.2.2 utilizando PDO). mas os testes foram ligeiramente mais rápidos que no PHP.
Desconfiando que o problema estava no ActiveRecord fui olhar a implementação do Adapter do Postgres e MySQL e comparei os dois. No meu caso se encontram em:
/usr/local/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.6/lib/active_record/connection_adapters/
postgresql_adapter.rb
mysql_adapter.rb
O método responsável por fazer as chamadas era select
Verifique a implementação dos dois:
Mysql:


def select(sql, name = nil)
connection.query_with_result = true
result = execute(sql, name)
rows = result.all_hashes
result.free
rows
end

def all_hashes
rows = []
each_hash { |row| rows << row }
rows
end
PostgreSql:

def select(sql, name = nil)
res = execute(sql, name)
results = res.result
rows = []
if results.length > 0
init = Time.now
results.each do |row|
fields = res.fields
hashed_row = {}
row.each_index do |cel_index|
column = row[cel_index]

case res.type(cel_index)
when BYTEA_COLUMN_TYPE_OID
column = unescape_bytea(column)
when TIMESTAMPTZOID, TIMESTAMPOID
column = cast_to_time(column)
when NUMERIC_COLUMN_TYPE_OID
column = column.to_d if column.respond_to?(:to_d)
end

hashed_row[fields[cel_index]] = column
end
rows
Primeira_offAnterior_off 1 2 3 Proxima_onUltima_on
Object Training
Rua Carlos da Costa Ramalho Junior, 201
Próximo as estações Osasco e Pres. Altino do Trem.
COPYRIGHT