Created
May 22, 2011 21:47
-
-
Save ahoward/985933 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# following is a little benchmark looking a real world example of inserting a | |
# bunch of records into pg and mongo using a few methods available in ruby. | |
# | |
# the code inserst a value with a simple uniq contraint and then looks it up. | |
# | |
# this is not meant to be an apples to apples benchmark, rather it's trying to | |
# see how the various dbs might perform in a real world situation. i know | |
# mongo does not fsync to disk. i know it's not fair using a transaction with | |
# the rdbms, but these conditions will exist in real world usage and it's that | |
# which i'm interested in. | |
# | |
# here are a few runs | |
# | |
# n=1_000 | |
# user system total real | |
# pg - with transactions 0.020000 0.030000 0.050000 ( 0.255017 ) | |
# pg - without transactions 0.030000 0.030000 0.060000 ( 0.594669 ) | |
# sequel - with transactions 0.580000 0.060000 0.640000 ( 0.890069 ) | |
# sequel - without transactions 0.580000 0.060000 0.640000 ( 1.232356 ) | |
# active_record model - with transactions 1.010000 0.040000 1.050000 ( 1.258300 ) | |
# active_record model - without transactions 1.150000 0.080000 1.230000 ( 1.841997 ) | |
# mongo_mapper model 1.200000 0.070000 1.270000 ( 1.520423 ) | |
# mongoid model 0.860000 0.060000 0.920000 ( 1.941832 ) | |
# mongo 0.410000 0.040000 0.450000 ( 0.586048 ) | |
# | |
# n=10_000 | |
# user system total real | |
# pg - with transactions 0.210000 0.280000 0.490000 ( 2.587327 ) | |
# pg - without transactions 0.280000 0.310000 0.590000 ( 6.444020 ) | |
# sequel - with transactions 6.060000 0.510000 6.570000 ( 9.315459 ) | |
# sequel - without transactions 6.030000 0.540000 6.570000 ( 13.320014 ) | |
# active_record model - with transactions 11.560000 0.360000 11.920000 ( 14.245436 ) | |
# active_record model - without transactions 11.240000 0.730000 11.970000 ( 18.405799 ) | |
# mongo_mapper model 12.240000 0.480000 12.720000 ( 15.185164 ) | |
# mongoid model 10.760000 0.590000 11.350000 ( 85.778884 ) | |
# mongo 4.060000 0.400000 4.460000 ( 5.847425 ) | |
# | |
# n=100_000 | |
# user system total real | |
# pg - with transactions 2.550000 2.700000 5.250000 ( 27.542305 ) | |
# pg - without transactions 3.470000 3.770000 7.240000 ( 87.406853 ) | |
# sequel - with transactions 69.130000 4.860000 73.990000 ( 102.175770 ) | |
# sequel - without transactions 69.320000 5.030000 74.350000 ( 140.852722 ) | |
# active_record model - with transactions 234.690000 3.750000 238.440000 ( 265.734381 ) | |
# active_record model - without transactions 130.730000 7.130000 137.860000 ( 213.607269 ) | |
# mongo_mapper model 141.110000 4.230000 145.340000 ( 170.478233 ) | |
# mongoid model 155.750000 5.790000 161.540000 ( 6950.227399 ) | |
# mongo 47.980000 3.750000 51.730000 ( 68.493900 ) | |
# | |
# | |
# | |
# conclusions: | |
# | |
# - raw pg is almost as fast(er) as raw mongo and a *lot* more durable. | |
# | |
# - ar has gotten much, much better than it used to be. | |
# | |
# - the speed of mongo isn't that compelling for simple queries (massive joins may be different). | |
# | |
# - optimizing orm actions via sql is still sexy and effective. | |
# | |
# | |
## setup | |
# | |
require 'rubygems' | |
require 'ostruct' | |
require 'benchmark' | |
require 'uuidtools' | |
require 'active_record' | |
require 'mongoid' | |
require 'mongo_mapper' | |
## cli options | |
# | |
STDOUT.sync = true | |
$n = (ARGV.shift || 1000).to_i | |
## config | |
# | |
C = | |
OpenStruct.new( | |
'db' => (ENV['USER'] || 'bench'), | |
'table' => 'bench' | |
) | |
## setup a sequel connection | |
# | |
require 'sequel' | |
S = Sequel.connect("postgres://localhost/#{ C.db }") | |
require 'sequel_pg' | |
## setup an ar class | |
# | |
ActiveRecord::Base.establish_connection(:adapter => 'postgresql', :database => C.db) | |
class A < ActiveRecord::Base | |
set_table_name C.table | |
end | |
## setup mongoid class | |
# | |
Mongoid.configure do |config| | |
config.master = Mongo::Connection.new.db(C.db) | |
end | |
class M | |
include Mongoid::Document | |
self.collection_name = C.table | |
field(:value) | |
index(:value, :unique => true) | |
end | |
## setup mongo mapper class | |
# | |
MongoMapper.connection = Mongo::Connection.new('localhost') | |
MongoMapper.database = C.db | |
class MM | |
include MongoMapper::Document | |
set_collection_name C.table | |
key(:value) | |
end | |
MM.ensure_index(:value, :unique => true) | |
## make sure the rdbms table is setup and indexed | |
# | |
S.drop_table(C.table) if S.table_exists?(C.table) | |
S.create_table(C.table) do | |
primary_key(:id) | |
String(:value, :unique => true, :null => false) | |
end | |
## make sure the mongo collection is setup and indexed | |
# | |
M.delete_all | |
M.create_indexes | |
## helper methods | |
# | |
def new_values() Array.new($n).map{ UUIDTools::UUID.timestamp_create.to_s } end | |
def transaction(which, obj, &block) | |
case which.to_s | |
when /without/ | |
block.call() | |
when /with/ | |
if obj.respond_to?(:exec) | |
obj.exec('begin transaction') | |
block.call() | |
obj.exec('commit') | |
else | |
obj.transaction(&block) | |
end | |
end | |
end | |
## bench | |
# | |
Benchmark.bm do |bm| | |
## grab some handles on drivers | |
# | |
sequel = S[:bench] | |
mongo = M.collection.db.collection(C.table) | |
pg = A.connection.raw_connection | |
## multiplex transactions on/off for rdbms | |
# | |
## pg | |
# | |
%w( with without ).each do |which| | |
GC.start | |
values = new_values | |
bm.report "pg - #{ which } transactions" do | |
transaction(which, pg) do | |
values.each do |value| | |
res = pg.exec('insert into bench values(default, $1) returning id', [value]) | |
id = res.getvalue(0,0) | |
res = pg.exec('select * from bench where id=$1', [id]) | |
end | |
end | |
end | |
end | |
## sequel | |
# | |
%w( with without ).each do |which| | |
GC.start | |
values = new_values | |
bm.report "sequel - #{ which } transactions" do | |
transaction(which, S) do | |
values.each do |value| | |
id = sequel.insert(:value => value) | |
sequel.where(:id => id).first | |
end | |
end | |
end | |
end | |
## active_record | |
# | |
%w( with without ).each do |which| | |
GC.start | |
values = new_values | |
bm.report "active_record model - #{ which } transactions" do | |
transaction(which, A) do | |
values.each do |value| | |
r = A.create!(:value => value) | |
A.where(:id => r.id).first | |
end | |
end | |
end | |
end | |
## mongo tests don't have transactions so... | |
# | |
## mongo_mapper | |
# | |
GC.start | |
values = new_values | |
bm.report 'mongo_mapper model' do | |
values.each do |value| | |
r = MM.new(:value => value) | |
r.save(:safe => true) | |
MM.find(r.id) | |
end | |
end | |
## mongoid | |
# | |
GC.start | |
values = new_values | |
bm.report 'mongoid model' do | |
values.each do |value| | |
r = M.safely.create!(:value => value) | |
M.where(:id => r.id).first | |
end | |
end | |
## mongo driver | |
# | |
GC.start | |
values = new_values | |
bm.report 'mongo' do | |
values.each do |value| | |
id = mongo.insert({:value => value}, {:safe => true}) | |
mongo.find(:_id => id).first | |
end | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
interesting. any server setup advice for mongo based on that fact then?