2012-04-14 11 views
4

私は、操作が必要な行のリストを持つデータベースを持っています。私はルビーとDataMapperのを使用していますが、実際に私はこれは私が使用している正確な実装に固有ではない一般的なプログラミングの問題だと思う...複数のワーカースレッドが同じデータベースで動作している - 正しく動作させるには?

id  remaining  delivered locked 
============================================ 
1  10    24   f 
2  6    0   f 
3  0    14   f 

:それは次のようになります私は、この(擬似ルビー・コード)のような何かを行うワーカースレッドの束作成しています:もちろん

while true do 
    t = any_row_in_database_where_remaining_greater_than_zero_and_unlocked 
    t.lock # update database to set locked = true 
    t.do_some_stuff 
    t.delivered += 1 
    t.remaining -= 1 
    t.unlock 
end 

、問題があるが、これらのスレッドは互いに競合し、全体のことは本当にスレッドではありません安全。 whileループの最初の行は、ロックされる機会を得る前に、同じ行を複数のスレッドで簡単に引き出すことができます。

1つのスレッドが同時に1つの行で作業していることを確認する必要があります。

これを行うにはどのような方法が最適ですか?

+0

Rubyにはグローバルインタプリタロックがあるので、一度に1つのスレッドしか動作しないことはすでに保証されています。 – robbrit

+1

これは正しいとは思わない...スレッドの最初の行は複数のスレッドで実行できる.. – MikeC8

+0

ああ、そうだね、Ruby 1.9では競合条件がある可能性がある。なぜ、一度に1つの行を引っ張るのではなく、N個の行をプルして、Nをスレッドの数とし、各スレッドを返された行の1つで操作させますか?それから、すべてのスレッドが完了したら、完了するまで別のNを引きます。 – robbrit

答えて

4

重要なステップは、データベースからロックされていない行を選択し、ロックされているとマークすることです。もしあなたがそれを安全に行うことができれば、他のすべてはうまくいくでしょう。

私が知っているこの2つの方法は、この安全を悲観的で楽観的なものにすることができます。同時実行性に関しては、どちらもデータベースに頼っています。

ペシミスティック・ロックをロックペシミスティックはあなたが誰もそれらを読むことができないように、操作したい行を選択すると、ロックの先行取得を意味します。 MySQLとpostgresの(そしておそらく他の人)と行がロックされ、粒状が使用されるエンジン、インデックスなどにどのように依存するか(あなたに戻って読んでからデータベースへの他の接続を防止しますと

SELECT * from some_table WHERE ... FOR UPDATE 

作品のような 何か - データベースのドキュメントを確認してください)。並行処理の問題が発生し、ロックを予防的に取得することを前提としているため、これは悲観的です。これは、必要でない場合でもロックするコストを負担し、ロックの細分性に応じて並行性を低下させる可能性があることを意味します。

ロック楽観

オプティミスティック・ロックを使用すると、ロックされたフラグを設定する行を更新する場合、ほとんどの時間は、(同時更新ができなくなりますので、あなたが悲観的ロックの負担をしたくない技術を指しますあなたが行を読むとすぐに真になり、窓は比較的小さい)。 AFAIKこれは、一度に1行を更新する場合にのみ機能します

最初に整数列lock_versionをテーブルに追加します。テーブルを更新するたびに、作成している他の更新とともに、lock_versionを1だけ増分します。あなたは、更新

update some_table set ... where id=12345 and lock_version = 3 

に更新クエリを変更し、更新された行の数を(DBドライバがこれを返す)チェックすると、現在のlock_versionが3であると仮定します。この行が1行更新されると、すべてが正常だったことがわかります。これにより0行が更新された場合、必要な行が削除されたか、ロックバージョンが変更されたため、プロセスのステップ1に戻って作業する新しい行を検索します。

私はdatamapperユーザーではありません。そのため、/ pluginsがこれらのアプローチをサポートしているかどうかわかりません。アクティブレコードは両方をサポートしているため、データマッパーが認識できない場合は、インスピレーションのためにそこを見ることができます。

1

私はMutexを使用します。

# outside your threads 
worker_updater = Mutex.new 

# inside each thread's updater 
while true 
    worker_updater.synchronize do 
    # your code here 
    end 
    sleep 0.1 # Slow down there, mister! 
end 

これは、一度に1つのスレッドのみsynchronizeにコードを入力できることを保証します。最適なパフォーマンスを得るには、コードのどの部分をスレッドセーフ(最初の2行?)にする必要があり、その部分をMutexでラップするだけです。

+0

1つのスレッド、はい..しかしデータベースは、異なるシステム上で動作する異なるアプリケーションによってアクセスされることがよくあります。これはうまくいきません。 – baash05

+0

@daveatflowこの質問の主題とその中に "スレッド"という言葉を繰り返し使用してみましょう。 – Phrogz

関連する問題