郵便番号データ突っ込んでみたよ

2007/11/11 00:01

※ 商品のリンクをクリックして何かを購入すると私に少額の報酬が入ることがあります【広告表示】

ちかぢか大量のデータを扱うことになりそうなのと、ちょいと郵便番号データを使ったものをプライベートで作る必要があるのとで、データベースにレコードをインサートするのにかかる時間をはかってみた。

郵便番号データはたかだか12万件程度しかないので、実際にはどうなるかわからないけど楽しいからやってみただけ。

実際は位置データを使いたいからPostgreSQLになると思うんだけど、速度を考えるとメモリという選択肢もあるので、SQLiteってどうなのよという気もあってSQLiteも試してみた。

MySQL?キライ:)

郵便番号をしまうためのモデルはこんな感じ。郵便番号はユニークにした方がいいんだけど…。

  from django.db import models

  class ZipCode(models.Model):
      zipcode = models.CharField('Zip Code', max_length=7,
      db_index=True)
      prefecture_kana = models.CharField('Prefecture Kana',
      max_length=20)
      city_kana = models.CharField('City Kana', max_length=100)
      town_kana = models.CharField('Town Kana', max_length=100)
      prefecture = models.CharField('Prefecture', max_length=20)
      city = models.CharField('City', max_length=100)
      town = models.CharField('Town', max_length=100)

      def __unicode__(self):
          return '%s: %s %s %s' % (self.zipcode,
          self.prefecture, self.city, self.town)

郵便番号データは、例によって 日本郵便 の全件なので、半角カナが入ってます。

なので、 setomitsさんの全角半角コンバートモジュール を使ってます。

  import sys, os, csv, time
  from django.conf import settings
  from django.db import IntegrityError
  from greetings.zipcode.models import ZipCode

  try:
      import zenhan
  except ImportError:
      print '''You need zenhan.py .
  Download zenhan.py and install it.
  http://matatabi.homeip.net/blog/setomits/877
  '''
      sys.exit()

  #
  #from django.db import connection
  #cursor = connection.cursor()
  #cursor.execute('PRAGMA temp_store = MEMORY;')
  #cursor.execute('PRAGMA synchronous=OFF')

  from django.core.management import call_command
  call_command('syncdb')

  reader = csv.reader(open(os.path.join(settings.DATA_DIR,
  'KEN_ALL.CSV'), 'rb'))

  counter = 0

  start = time.clock()
  for data in reader:
      try:
          zipcode = ZipCode(zipcode=data[2],
                      prefecture_kana=zenhan.h2z(unicode(data[3], 'shift_jis'), zenhan.KANA),
                      city_kana=zenhan.h2z(unicode(data[4], 'shift_jis'), zenhan.KANA),
                      town_kana=zenhan.h2z(unicode(data[5], 'shift_jis'), zenhan.KANA),
                      prefecture=unicode(data[6], 'shift_jis'),
                      city=unicode(data[7], 'shift_jis'),
                      town=unicode(data[8], 'shift_jis'))
          zipcode.save()
      except IntegrityError: pass
      counter += 1
      if counter%1000 == 0:
          print '%d: %.2f' % (counter, time.clock() - start)
  print 'end: %.2f' % (time.clock() - start)

試したのは、 PostgreSQL (PostgreX8.2.5)と SQLite (3.4.0)。それからデータベースに入れなかった場合とかも試してる。

実行環境はCoreDuo1.83Ghz/2GB上のleopard+Python2.5.1。

結果は、以下の通り。

SQLiteは書き込みが遅いらしく、ファイルシステムを使った場合にはPostgreSQLの2.5倍遅い。とりあえず非同期書き込みにしてみたら1.7倍遅いところまで改善。メモリにしたら2倍速くなった :)

ちなみに、データベースにインサートを行わずに「Djangoのモデルを作るだけ」と「単に変数に入れるだけ」の場合では、12万件で2秒程度しか違いがないようなので、生成コストは無視してよさそう。O/Rを使うこと自体はメリット の方が大きい模様。

Shift_jisのデータを読み込んでunicodeに変換して半角カナを全角にするってのを省いて、「本当に単に変数に入れるだけ」の場合は12万件で1.09秒だった。マルチバイトうぜー。

2007/11/16 追記

コメントいただいたので、追試を行いました。

ワントランザクションにしたところ、SQLiteはほぼメモリと同等のパフォーマンス(+1秒)になり、PostgreSQLについてもSQLiteの1.2倍遅い(+10秒)だけの状態になりました。

2007/11/11 15:26 by anon
SQLiteの書き込みはトランザクションを使わないと非常に遅いので、その影響もあるんじゃないかと思います。
参考: 

Prev Entry

Next Entry