ここにいくつか質問があります。
1)他のコマンドで中断できないトランザクションで増分を実行できないのはなぜですか?
まず、Redisの「トランザクション」は、ほとんどの人が従来のDBMSでのトランザクションと考えるものとは完全に異なることに注意してください。
# Does not work
redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()
サーバー側(Redis)で何が実行され、クライアント側(スクリプト内)で何が実行されるかを理解する必要があります。上記のコードでは、GETコマンドとSETコマンドはRedis側で実行されますが、currentへの割り当てとcurrent+1の計算はクライアント側で実行されることになっています。
アトミック性を保証するために、MULTI/EXECブロックは実行までRedisコマンドの実行を遅らせます。したがって、クライアントはGETコマンドとSETコマンドをメモリに積み上げ、それらを1回のショットで実行し、最後にアトミックに実行します。もちろん、GETとインクリメントの結果に電流を割り当てようとする試みはかなり前に行われます。実際、redis.getメソッドは、コマンドが遅延していることを示す文字列「QUEUED」のみを返し、インクリメントは機能しません。
MULTI / EXECブロックでは、ブロックの開始前にパラメーターを完全に知ることができるコマンドのみを使用できます。詳細については、ドキュメントをお読みください。
2)代わりに反復処理を行い、トランザクションを開始する前に誰も値を変更しないまで待つ必要があるのはなぜですか?
これは同時楽観的パターンの例です。
WATCH / MULTI / EXECを使用しなかった場合、競合状態が発生する可能性があります。
# Initial arbitrary value
powerlevel = 10
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: SET powerlevel 11
session B: SET powerlevel 11
# In the end we have 11 instead of 12 -> wrong
次に、WATCH / MULTI/EXECブロックを追加しましょう。 WATCH句を使用すると、MULTIとEXECの間のコマンドは、値が変更されていない場合にのみ実行されます。
# Initial arbitrary value
powerlevel = 10
session A: WATCH powerlevel
session B: WATCH powerlevel
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: MULTI
session B: MULTI
session A: SET powerlevel 11 -> QUEUED
session B: SET powerlevel 11 -> QUEUED
session A: EXEC -> success! powerlevel is now 11
session B: EXEC -> failure, because powerlevel has changed and was watched
# In the end, we have 11, and session B knows it has to attempt the transaction again
# Hopefully, it will work fine this time.
したがって、誰も値を変更しないまで待つ必要はありませんが、Redisが値に一貫性があることを確認し、成功したことを通知するまで、操作を何度も試行します。
ほとんどの場合、「トランザクション」が十分に高速で、競合が発生する可能性が低い場合、更新は非常に効率的です。ここで、競合がある場合は、いくつかの「トランザクション」に対していくつかの追加の操作を実行する必要があります(反復と再試行のため)。ただし、データは常に一貫しており、ロックは必要ありません。