mysql采用默认事务隔离级别REPEATABLE-READ;
然后我分别执行以下内容:
1、
2、使用jdbc访问数据库:
3、这时候打印结果,发现线程进入阻塞,一直卡在这里
也就是说我开启的另一个事务仍然能读取到数据,只是在最后执行executeUpdate的时候才被锁定不能执行
4、命令行执行commit
5、结果命令行操作被jdbc操作替换掉,数据改为4。
这不就是说REPEATABLE-READ仍然出现了丢失更新,而且没有行锁定吗?
事务与并发到底是什么区别,我在测试多线程执行以上jdbc操作时,发现最后得到的结果总是不对。例我开十个线程,每个都对数据+1,结果可能只加了5.所以在并发情况下怎么做才能保证数据的安全?
1 だけ増加した場合、数値の値は選択によって取得されず、選択によって取得された値が古い値である可能性があるため、更新テスト セット番号 = 数値 + 1 (id = ?) に置き換えます。 。ここでの番号は最新のものになります。この方法により、更新用選択の完全なシリアル パフォーマンスの損失を回避できます。
REPEATABLE-READ はファントム読み取りを引き起こす可能性があります
絶対的なセキュリティを確保したい場合は、分離レベルを SERIALIZABLE にのみ設定できます
この方法では、すべてのトランザクションは順次にのみ実行でき、当然のことながら、同時実行による問題はありませんが、影響を受けますが、パフォーマンスは大幅に低下します。
パフォーマンスを低下させたくないが、問題が起こらないように制御したい場合は、現在、より一般的なアプローチは、新しいバージョン管理を使用することです。
フィールドを UpdateVersion として維持します。変更する場合は、たとえば
where id=? and update_version = ?
をセットに追加します。これは、一度に 1 人のユーザーだけが 1 つのバージョンを更新できるように制御できます。
実験を通じて、私は自分の問題をある程度理解していることがわかりました。
私は以前、トランザクション分離レベルに関する誤った情報に惑わされ、REPEATABLE-READ レベルと SERIALIZABLE レベルで更新の消失の問題を解決できると考えていましたが、実際には解決できません。
リーリーmysql の select ... from table; ステートメントは分離レベルに関係なくブロックされないため、
分離レベルでは、データ更新時に書き込みロック (排他ロック) が解放されるのを待つだけなので、複数のスレッドが同時に number=3 を読み取ることができます。これに基づいて変更すると、必然的に更新が失われます。
したがって、解決策は、プログラムに悲観的ロックまたは楽観的ロックのメカニズムを追加することです。
リーリー悲観的ロックは更新に select ... を使用します。
このとき、トランザクション B の読み取り操作は実行されません。トランザクション A のコミットが完了した場合にのみ、トランザクション B は実行を継続できます。これは、1 つずつシリアルに実行することと同じです。
楽観的ロックは、バージョン管理またはタイムスタンプ管理に一般的に使用されます。
リーリーこの時点で、トランザクション A が最初に実行を完了してデータベースを更新し、バージョン フィールドを 2 に設定したため、トランザクション B は version=1 が存在しないことがわかります。これにより、トランザクションの送信が失敗します。プログラムで異常状況を判断する方法は次のとおりです。
上記は私の現時点での理解です。MySQL 自体がサポートしている MVCC (Multi-version Concurrency Control) については、オプティミスティックの例外処理をどのように実装するかがまだわかりません。特定のビジネス シナリオに基づいて、別のアプローチを使用する場合があります。
単純な
Select
の場合、単純なクエリ操作とみなされ、一時停止されません。特定の行に対してトランザクションを実行したい場合は、
Select
のときに最初にSelect ... For Update
形式を使用する必要があります。For Update
を使用すると、選択した行がロックされ、別のトランザクションが実行されると、そのトランザクションのSelect ... For Update
で一時停止され、現在のトランザクションの完了を待ってから続行されます。これにより、クエリと書き込みの間に仲介者が存在しないことが保証されます。ロックによって制御されます。