LDAPの時も使ったCSV形式で配布されている郵便番号データをCouchDBに入力してみました。
今回の使い方は初期に大量のデータを入力して、使用フェーズではもっぱら参照だけになる、という使い方になるので、CouchDBらしくないとは思ったのですが、Viewを定義してListやShowをテストするのに使おうと思います。
準備したデータの量やハードウェアなどについて
- Alix 2c3
- Debian lenny
- Transcend CompactFlash 8GB x300(TS8GCF300)
- 郵便番号データ
- Erlang r14B
- Apache CouchDB 1.0.1
- Ruby 1.9.2-p0
郵便番号データは12万2千件余りで、CouchDBに入力した後のデータサイズはおよそ100MBです。
まずは結論、困った事や思った事
_bulk_docsを使って大量のデータを入力しようとしたのですが、30件程度、サイズで4KB以内程度でないと失敗しました。サイズに上限があるのかどうかは、はっきりしていません。
Alixは256MBしかメモリがないですし、少し特殊なハードウェアですからVMWare上のUbuntu Server 10.04 LTSでメモリを増やして確認しましたが、やはり似たような挙動になりました。
その他にはApacheをSSLで接続するReverse Proxyにした場合のデータスループットは、Stunnelを利用した場合と比較すると、およそ2倍ちょっと遅いという結果になりました。(15分→35分)
これは単純にシングルコア、かつ256MBのメモリで動くAlixにSSLとReverse Proxyが負荷を与えたのかなぁと考えています。 ただ手元のデータでは、ここら辺の考えを証明できてはいません。
最後に全文書を対象に郵便番号をキー(Key)に、文書を値(Value)にするシンプルなViewを追加した場合に、100MBのデータに対して80MB程度のファイルが作成されています。
ここら辺の動きはLDAPと比べても、あまり変わりのないところかなと思いました。
データの加工とCouchDBへの入力
以前LDAPを相手にした時のようなスキーマは必要ないので適当なデータ構造をでっちあげて、次のようなスクリプトを組みました。
カレントディレクトリに置いたcouchdb.rbには前回も使ったCouchDB Wikiにあるサンプルを改造したものを使っています。
あらかじめ /postalにDBを作成しておき、次のようにスクリプトを実行しています。
$ nkf -w ken_all.csv > ken_all.utf8.csv $ ./initdb.rb ken_all.utf8.csv
#!/usr/bin/env ruby1.9
# -*- coding: utf-8 -*-
##
## CouchDB Wiki : Transactional Semantics with Bulk Updates
## http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
##
$:.unshift File.dirname($0)
require 'csv'
require 'couchdb'
require 'json'
couch = Couch::Server.new("couchdb.example.org","5984",
{'user' => 'admin','password' => "xxxxxxxxxxxx"})
uri = '/postal/_bulk_docs'
num = 0
bulk_docs = Hash.new
bulk_docs['non_atomic'] = true
bulk_docs['docs'] = Array.new
entry = Hash.new
CSV.open(ARGV[0], 'r').each do |row|
## prepare output format
entry = Hash.new
entry['_id'] = "entry." + num.to_s
entry['num'] = num
entry['id'] = row[0]
entry['pref'] = row[6]
entry['pref_kana'] = row[3]
entry['city'] = row[7]
entry['city_kana'] = row[4]
entry['street'] = row[8]
entry['street_kana'] = row[5]
entry['code'] = row[2]
entry['code_prefix'] = row[1]
entry['other'] = [row[9],row[10],row[11],row[12],row[13],row[14]]
bulk_docs['docs'] << entry
num += 1
if num % 10 == 9
res = ""
while not res.kind_of?(Net::HTTPSuccess)
begin
res = couch.post(uri, bulk_docs.to_json)
if res.kind_of?(Net::HTTPUnauthorized)
print "num: #{num}, #{res.to_s}\n"
end
rescue
p $!
p bulk_docs.to_json
res = ""
end
end
bulk_docs['docs'] = Array.new
end
end
res = ""
while not res.kind_of?(Net::HTTPSuccess)
begin
res = couch.post(uri, bulk_docs.to_json)
print "num: #{num}, #{res.to_s}\n"
rescue
p $!
res = ""
end
end
先頭部分は#!/usr/bin/ruby
やら/usr/local/bin/ruby
やら適当に変更してください。
それとcouch = Couch::Server.new("couchdb.example.org","5984",
の行は、それぞれの環境に合せて修正してください。
ここまで終って適当にデータが入っているか確認だけしておきます。
$ curl -u admin:xxxxxxxxxxx http://couchdb.example.org:5984/postal/_all_docs?limit=10 {"total_rows":122971,"offset":0,"rows":[ {"id":"_design/all","key":"_design/all","value":{"rev":"1-5dfb6015c7dde046e055d54f02a2b8d3"}}, {"id":"entry.0","key":"entry.0","value":{"rev":"1-f53d3fc4f2e20aa9a26a642248d442d6"}}, ...
出力1行目の total_rowsが12万件を越えているところを確認します。
Viewの追加
テストのために、いくつかの値をキーにする /postal/_design/all文書を加えました。
#!/usr/bin/env ruby1.9 # -*- coding: utf-8 -*- $:.unshift File.dirname($0) require 'csv' require 'couchdb' require 'json' require 'uri' couch = Couch::Server.new("couchdb.example.org","5984", {'user' => 'admin','password' => "xxxxxxxxxxxx"}) uri = '/postal/_design/all' json = Hash.new ## check existing document begin res = couch.get(URI.escape(uri)) json = JSON.parse(res.body) rescue end json = Hash.new if json.has_key?("error") p json ## override existing document or write new document json['language'] = 'javascript' json['views'] = Hash.new if not json['views'].kind_of?(Hash) json['views']['by-code'] = Hash.new if not json['views']['by-code'].kind_of?(Hash) json['views']['by-code']['map'] = <<-MAP function(doc) { if(doc._id.indexOf('entry.') == 0) { emit(doc.code, null) } } MAP json['views']['by-pref'] = Hash.new if not json['views']['by-pref'].kind_of?(Hash) json['views']['by-pref']['map'] = <<-MAP function(doc) { if(doc._id.indexOf('entry.') == 0) { emit(doc.pref, null) } } MAP json['views']['by-street'] = Hash.new if not json['views']['by-street'].kind_of?(Hash) json['views']['by-street']['map'] = <<-MAP function(doc) { if(doc._id.indexOf('entry.') == 0) { emit(doc.street, null) } } MAP json['views']['by-code_prefix'] = Hash.new if not json['views']['by-code_prefix'].kind_of?(Hash) json['views']['by-code_prefix']['map'] = <<-MAP function(doc) { if(doc._id.indexOf('entry.') == 0) { emit(doc.code_prefix, null) } } MAP res = couch.put(uri, json.to_json) puts res
ここでも、検索が成功するか確認しておきます。
$ curl -u admin:xxxxxxxxxxx http://couchdb.example.org:5984/postal/_design/all/_view/by-code?key="9650000"&include_docs=true' {"total_rows":122970,"offset":115550,"rows":[ {"id":"entry.20279","key":"9650000","value":null,"doc":{"_id":"entry.20279","_rev":"1-efbdc6e3516306c14e784eacf30412b8","num":20279,"id":"07202","pref":"\u798f\u5cf6\u770c","pref_kana":"\u30d5\u30af\u30b7\u30de\u30b1\u30f3","city":"\u4f1a\u6d25\u82e5\u677e\u5e02","city_kana":"\u30a2\u30a4\u30c5\u30ef\u30ab\u30de\u30c4\u30b7","street":"\u4ee5\u4e0b\u306b\u63b2\u8f09\u304c\u306a\u3044\u5834\u5408","street_kana":"\u30a4\u30ab\u30cb\u30b1\u30a4\u30b5\u30a4\u30ac\u30ca\u30a4\u30d0\u30a2\u30a4","code":"9650000","code_prefix":"965 ","other":["0","0","0","0","0","0"]}} ]}
このviewを登録すると、だいたい160MBほどの中間ファイルが/usr/local/var/lib/couchdb/.postal_design/に生成されます。
問題なのは時間で、これで1時間半ぐらいでしょうか。 もっともファイルが出来てしまえば動きには問題なくて、検索結果を素早く得る事ができています。
これを元ネタに簡単なWebページを作ってみようと思います。
この記事で取り上げた品々
0 件のコメント:
コメントを投稿