1

私は次の状況にあります:

一意の番号を格納するために使用するInnoDBテーブルを持つMySQLデータベースがある場合。トランザクションを開始し、値(1000471など)を読み取り、この値を別のテーブルに格納して、増分値(100472)を更新します。ここで、トランザクションの実行中に他の誰かが値を読み取ることさえ避けたいと思います。

プレーンなMySQLを使用する場合は、次のようにします。

Exceute( "LOCK tbl1 READ");
Execute( "SELECT ... from tbl1");
Execute( "INSERT into tbl2");
Execute( "UNLOCK TABLES");

ただし、SubSonicをDALとして使用しており、コードはmysqlから独立している必要があるため、TransactionScopeを使用する必要があります。

私のコード:

        TransactionOptions TransOpt = new TransactionOptions();
        TransOpt.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
        TransOpt.Timeout = new TimeSpan(0, 2, 0);

        using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, TransOpt))
        {

             // Select Row from tbl1

             // Do something

             ts.Complete();
        }

TransactionOptionsの助けによると

system.transactions.isolationlevel

到達したい効果はIsolationLevel.ReadCommittedで実装できますが、トランザクションの外部から行を読み取ることはできます(変更しようとすると、ロックが発生するため、トランザクションは機能します)

誰か提案がありますか?TransactionScopeでも読み取りロックは可能ですか

4

5 に答える 5

2

誰かが興味を持っているなら、これはTransactionOptionsがMySqlにどのように影響するかです:

2つの方法があるとしましょう。

Method1はトランザクションを開始し、テーブルから行を選択し、値をインクリメントしてテーブルを更新します。

方法2は同じですが、選択と更新の間に1000ミリ秒のスリープを追加しました。

今、私が次のコードを持っていると想像してください:

    Private Sub Button1_Click(sender as Object, e as System.EventArgs) Handles Button1.Click

        Dim thread1 As New Threading.Thread(AddressOf Method1)
        Dim thread2 As New Threading.Thread(AddressOf Method2)

        thread2.Start() // I start thread 2 first, because this one sleeps
        thread1.Start()

    End Sub

トランザクションがないと、これが発生します
。thread2が開始し、値5を読み取り、次にスリープし、
thread1が開始し、値5を読み取り、値を6に
更新し、thread2も値を6に更新します。

効果:私は2回一意の番号を持っています。

私が欲しいもの:
thread2が開始し、値5を読み取り、次にスリープし、
thread1が開始し、値を読み取ろうとしますが、ロックを取得してスリープし
ます。thread2は値を6に更新し、
thread1は続行し、値6を読み取り、値を次のように更新します。 7

これが、TransactionScopeを使用してトランザクションを開始する方法です。

        TransactionOptions Opts = new TransactionOptions();
        Opts.IsolationLevel = IsolationLevel.ReadUncommitted;

        // start Transaction
        using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, Opts))
        {
            // Do your work and call complete
            ts.Complete();
        }

分散トランザクションを管理することもできます。例外がスローされた場合、ts.Completeは呼び出されず、スコープのDispose()部分がトランザクションをロールバックします。

さまざまなIsolationLevelがトランザクションにどのように影響するかについての概要は次のとおりです。

  • IsolationLevel.Chaos
    はNotSupportedExceptionをスローします-カオス分離レベルはサポートされていません

  • IsolationLevel.ReadCommited
    トランザクションは相互に干渉しません(2つの同一の読み取り、不良)

  • IsolationLevel.ReadUncommitted
    トランザクションは相互に干渉しません(2つの同一の読み取り、不良)

  • IsolationLevel.RepeatableRead
    トランザクションは相互に干渉しません(2つの同一の読み取り、不良)

  • IsolationLevel.Serializable
    はMySqlExceptionをスローします-ロックを取得しようとしたときにデッドロックが見つかりました。更新中にトランザクションを再開してみてください

  • IsolationLevel.Snapshot
    がMySqlExceptionをスローします-SQL構文にエラーがあります。Connection.Open()中に1行目の''の近くで使用する正しい構文については、MySQLサーバーのバージョンに対応するマニュアルを確認してください。

  • IsolationLevel.Unspecific
    はMySqlExceptionをスローします-ロックを取得しようとしたときにデッドロックが見つかりました。更新中にトランザクションを再開してみてください

  • TransactionOptionsが設定されていません
    MySqlExceptionをスローします-ロックを取得しようとしたときにデッドロックが見つかりました。更新中にトランザクションを再開してみてください

于 2009-06-03T09:05:09.467 に答える
1

私の最初の推測は使用することでしたがSELECT FOR UPDATE、簡単に検索した後、MySQL5リファレンスで読み取りのロックに関するページを見つけました。

私が正しく理解していれば、これは使用される分離レベルとは無関係です。そして注意してください-分離レベルは、現在のトランザクションが他のトランザクションの変更によってどのように影響を受けるかを示すだけです。他のトランザクションで何ができるかはわかりません。ただし、分離レベルを高くするには、より制限の厳しいロックが必要です。

于 2009-06-02T11:20:15.533 に答える
1

InnoDBとTransactionScopeを使用して読み取り用の行をロックする方法が見つからなかったため(間違っている可能性があります)、これは機能するはずです。

2つのトランザクションを同時に(TransactionOptionsなしで)実行し、一方が終了した場合、「デッドロック」例外のためにもう一方は完了できません。

MySQLのドキュメントによると、この例外を回避する代わりに、デッドロックを予期してトランザクションを再開することがベストプラクティスのようです。

設定した場合:

    TransactionOptions TransOpt = new TransactionOptions();
    TransOpt.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

トランザクションの場合、デッドロック例外は発生しませんが、私の場合、これにより一意の番号が重複することになり、さらに悪い結果になります。

于 2009-06-02T13:27:21.907 に答える
0

SubSonicはサポートしていないようで、SELECT ... FOR UPDATE分離レベルを使用することは私の意見では誤用になると思います。新しいIDを返すユーザー定義関数を使用するのはどうでしょうか。この関数はSELECT ... FOR UPDATE、tbl1から現在の値をaで読み取り、行を更新して値を返します。

新しい値を挿入するアプリケーションコードでは、次のように使用します。

insert into tbl2 (id, ....) values (next_id(), ...)
于 2009-06-04T06:51:07.367 に答える
0

わかりました。「バックドア」を使用して、インラインクエリを使用することにしました。

        // start transaction
        using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
        {
            using (SharedDbConnectionScope scope = new SharedDbConnectionScope(DB._provider))
            {

                try
                {

                    Record r = new InlineQuery().ExecuteAsCollection<RecordCollection>(
                        String.Format("SELECT * FROM {0} WHERE {1} = ?param FOR UPDATE",
                                        Record.Schema.TableName,
                                        Record.Columns.Column1), "VALUE")[0];

                    // do something

                    r.Save();
                 }
             }
         }

10スレッドを同時に開始しましたが、期待どおりに機能します。「SELECTFORUPDATE」のヒントについては、rudolfsonに感謝します。

于 2009-06-04T07:27:56.747 に答える