« レガシーアプリケーション,だと... | トップページ

2010年1月19日 (火)

PostgreSQL トランザクションの隔離性

PostgreSQL のトランザクションで使用できる隔離性 isolation level は, read committed と, serializable の 2 種類です. これらは, それぞれ, 文単位での読み取り一貫性と, トランザクション単位での読み取り一貫性を保証しようとするものです. 1

さて, 以下のようなテーブル m があります.

mydb=# select * from m;

 m_id | m_name | m_upd_time
------+--------+------------
    1 | foo    |
    2 | bar    |
    3 | baz    |
(3 rows)

ここに, 以下のようなタイミングで, 2 つのトランザクション, Trans1, Trans2 を実行します.

Time Trans1 Trans2
1 select from m -
2 - update m
3 select from m -

すると, それぞれの隔離性に対して, 以下の実行結果が得られます.

------- Read Committed Isolation Level
begin trans2
end trans2
begin trans1
begin select
trans1:bar
end select
begin trans2
end trans2
begin select
trans1:fizzbuzz
end select
end trans1
------- Serializable Isolation Level
begin trans2
end trans2
begin trans1
begin select
trans1:bar
end select
begin trans2
end trans2
begin select
trans1:bar
end select
end trans1

なお, 各隔離性の実行結果について, 先頭の Trans2 の実行は無視してください. テーブル値の初期化に使用しています.

以下は, 実験に使用したソースコード.

###
### pg-trans1.rb
###
require 'rubygems'
require 'pg'

class PgTrans
  def initialize(*conn)
    @conn = PGconn.open(*conn)
  end

  def x(sql, *rest)
    @conn.exec(sql, *rest)
  end

  private :x
end

class Trans1 < PgTrans
  def execute(is_serial = false)
    begin
      puts 'begin trans1'
      x 'begin'
      x 'set transaction isolation level serializable' if is_serial
      puts 'begin select'
      r = x 'select * from m where m_id = 2'
      r.each {|t| puts "trans1:#{t['m_name']}" }
      puts 'end select'
      sleep 0.2
      puts 'begin select'
      r = x 'select * from m where m_id = 2'
      r.each {|t| puts "trans1:#{t['m_name']}" }
      puts 'end select'
      x 'commit'
      puts 'end trans1'
    rescue
      x 'rollback'
      STDERR.puts "pg:#$!"
    end
  end
end

class Trans2 < PgTrans
  def execute(mname)
    begin
      puts 'begin trans2'
      x 'begin'
      x "update m set m_name = $1 where m_id = 2", [mname]
      x 'commit'
      puts 'end trans2'
    rescue
      x 'rollback'
      STDERR.puts "pg:#$!"
    end
  end
end

def go_trans(is_serial)
  Trans2.new(:dbname => 'mydb').execute('bar')
  threads = []
  threads.
    push(Thread.new() do
           Trans1.new(:dbname => 'mydb').execute(is_serial)
         end
         )
  threads.
    push(Thread.new() do
           Trans2.new(:dbname => 'mydb').execute('fizzbuzz')
         end
         )
  threads.each {|t| t.join }
end

puts '------- Read Committed Isolation Level'
go_trans(false)
puts '------- Serializable Isolation Level'
go_trans(true)

1. 完全に保証するわけではない. 13.2.2.1. Serializable Isolation versus True Serializability, PostgreSQL 8.4.2 Documentation

|

« レガシーアプリケーション,だと... | トップページ

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/80472/33038560

この記事へのトラックバック一覧です: PostgreSQL トランザクションの隔離性:

« レガシーアプリケーション,だと... | トップページ