DataMapperのcountが遅い
約4万件ほどデータが格納されているデータベースに対してcountを呼ぶと数秒もかかるので、調べてみました。
require 'dm-core' require 'dm-migrations' require 'benchmark' class Test include DataMapper::Resource property :id, Serial property :number, Integer end DataMapper.setup(:default, 'sqlite:test.db') DataMapper.finalize DataMapper.auto_upgrade! # Testには4万件のデータが格納されている Benchmark.bm do |x| x.report{ Test.count } end
実行結果:
user system total real 5.975000 0.000000 5.975000 ( 6.030345)
速度を測ると約6秒ほどかかっています。これだと使い物にならないのでどんなSQLが吐き出されているのか調べてみました。
... DataMapper::Logger.new($stdout, :debug) # SQLを調べるためにLoggerを加える DataMapper.setup(:default, 'sqlite:test.db') DataMapper.finalize DataMapper.auto_upgrade! ...
実行結果:
SELECT "id", "number" FROM "tests" ORDER BY "id"
COUNT関数が発行されてると思ったら、すべてのデータを取得してからカウントしているので遅いことがわかります。内部的には配列の数を返しています。
ここでなぜCOUNT関数を使わないのかを調べてみたら、「dm-aggregates」をrequireする必要がありました。
require 'dm-core' require 'dm-aggregates' # 追加 require 'dm-migrations' require 'benchmark' ...
実行結果:
user system total real 0.016000 0.000000 0.016000 ( 0.004000)
SELECT COUNT(*) FROM "tests"
これでCOUNT関数が発行され高速に処理するようになりました。
DataMapperをrequireするときはこのようなミスをしないために、「data_mapper」をrequireするようにしたほうが無難です。data_mapperをrequireすると一度に必要なモジュールをrequireしてくれます。
require 'rubygems' require 'data_mapper'
- dm-core
- dm-aggregates
- dm-constraints
- dm-migrations
- dm-transactions
- dm-serializer
- dm-timestamps
- dm-validations
- dm-types