Mysql比知必会シリーズチュートリアル(九)------mysqlトランザクションの基本操作


トランザクションの概念:トランザクション(Transaction)は、一般的にやるべきことややるべきことを指します.コンピュータ用語では、データベース内の様々なデータ項目にアクセスし、更新可能なプログラム実行ユニット(unit)を指す.トランザクションは、通常、SQL、C+、Javaなどの高度なデータベース操作言語またはプログラミング言語によって書かれたユーザプログラムの実行によって引き起こされ、begin transaction文やend transaction文(または関数呼び出し)のような形で定義されます.トランザクションは、トランザクション開始(begin transaction)とトランザクション終了(end transaction)の間で実行されるすべてのアクションから構成されます.
トランザクションには、原子性、一貫性、独立性、持続性の4つのプロパティがあります.この4つの属性は一般にACID特性と呼ばれる.
  • 原子性(atomicity).1つのトランザクションは分割できない作業単位であり、トランザクションに含まれる操作はすべて行うか、しないかのいずれかです.
  • コンシステンシ(consistency).トランザクションは、データベースを1つのコンシステンシ状態から別のコンシステンシ状態に変更する必要があります.一致性は原子性と密接に関連している.
  • 隔離性(isolation).1つのトランザクションの実行は、他のトランザクションに干渉されてはいけません.すなわち、1つのトランザクション内部の操作および使用されるデータは、同時実行される他のトランザクションから分離され、同時実行される各トランザクション間で相互に干渉することはできません.
  • 持続性(durability).永続性は永続性(permanence)とも呼ばれ、トランザクションがコミットされると、データベース内のデータの変更が永続的であるべきであることを意味します.次の他の操作や障害は、それに影響を与えるべきではありません.データベースを単一スレッドのデータベースに設計することで、すべてのスレッドのセキュリティ問題を防止できますが、実際には実行できません.複数のスレッドがそれぞれのトランザクション・オペレーション・データベース内のデータを開く場合、データベース・システムは、各スレッドがデータを取得する際の正確性を保証するために、分離操作を担当します.2つのスレッドが同時に修正されると必ず干渉するので、ロックメカニズムで同時修正を防止する必要があります.2つのスレッドがクエリーの場合、スレッドセキュリティの問題はありません.

  • トランザクションの使用:トランザクションとは、論理的なアクションのセットです.このアクションは同時に完了するか、同時に失敗するかのいずれかです.データベースはトランザクションを管理します.1つの文がトランザクションを独占するか、次の文で操作します.Connection.settAutoCommit(false)// start transaction Connection.rollback()に相当します. rollback Connection.commit();  commitはまずトランザクションを開いてからトランザクションのコミットを行い、コミットしてから今回の操作を終了することができます.
    mysql> select*from account;
    +----+------+-------+
    | id | name | money |
    +----+------+-------+
    |  1 | a    |   100 |
    |  2 | b    |   100 |
    +----+------+-------+
    2 rows in set
    mysql> start transaction;
    Query OK, 0 rows affected
    mysql> update account set money=money-100 where name='a';
    Query OK, 1 row affected
    Rows matched: 1  Changed: 1  Warnings: 0
    
    
    mysql> select*from account;
    +----+------+-------+
    | id | name | money |
    +----+------+-------+
    |  1 | a    |     0 |
    |  2 | b    |   100 |
    +----+------+-------+
    2 rows in set
    
    
    mysql> rollback;
    Query OK, 0 rows affected
    
    
    mysql> select *from account;
    +----+------+-------+
    | id | name | money |
    +----+------+-------+
    |  1 | a    |   100 |
    |  2 | b    |   100 |
    +----+------+-------+
    2 rows in set
    
    
    mysql> start transaction;
    Query OK, 0 rows affected
    
    
    mysql> update account set money=money-100 where name='a';
    Query OK, 1 row affected
    Rows matched: 1  Changed: 1  Warnings: 0
    
    
    mysql> update account set money=money+100 where name='b';
    Query OK, 1 row affected
    Rows matched: 1  Changed: 1  Warnings: 0
    
    
    mysql> select *from account;
    +----+------+-------+
    | id | name | money |
    +----+------+-------+
    |  1 | a    |     0 |
    |  2 | b    |   200 |
    +----+------+-------+
    2 rows in set
    
    mysql> commit;
    1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'comnit' at line 1
    mysql> commit;
    Query OK, 0 rows affected
    
    mysql> 

    jdbc操作トランザクションの例://トランザクションの管理例:
    public class JDBCTranDemo {
    public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    //     ;
    Savepoint sp = null;
    try{
    conn = JDBCUtils.getConn();
    //    ===start transaction
    conn.setAutoCommit(false);
    
    ps = conn.prepareStatement("update account set money=money-100 where name = ?");
    ps.setString(1, "a");
    ps.executeUpdate();
    
    
    ps = conn.prepareStatement("update account set money=money+100 where name=?");
    ps.setString(1, "b");
    ps.executeUpdate();
    
    //     
    sp = conn.setSavepoint();
    
    ps = conn.prepareStatement("update account set money=money-100 where name = ?");
    ps.setString(1, "a");
    ps.executeUpdate();
    
    String str = null;
    str.toUpperCase();
    
    ps = conn.prepareStatement("update account set money=money+100 where name=?");
    ps.setString(1, "b");
    //    
    ps.executeUpdate();
    //    
    conn.commit();
    }catch (Exception e) {
    try {
    if(sp == null){//      null                    ,          
    //    
    conn.rollback();
    }else{//  sp  null,        ,        ,     ,            ,         
    //     
    conn.rollback(sp);
    conn.commit();
    }
    
    } catch (SQLException e1) {
    e1.printStackTrace();
    }
    e.printStackTrace();
    }finally{
    JDBCUtils.close(rs, ps, conn);
    }
    }
    }

    4つの独立性レベル:セキュリティレベル:
  • Serializable:汚れた読み取り、重複しない読み取り、虚読みを避けることができます.(シリアル化)
  • Repeatable read:汚れた読み取り、重複しない読み取りを避けることができます.(繰り返し読み可能)虚読み
  • は避けられない.
  • Read committed:汚れた読み取りを回避(読み取り済み)
  • Read uncommitted:最低レベル、以上の状況は保証できません.(読み取り未送信)
  • 効率性:後ろから前へ;本当にデータを使用するときは、自分が使用しているデータベースのニーズに応じて、セキュリティと効率の要求を総合的に分析して独立性レベルを選択します.mysqlのデフォルトはRepeatable readです.汚れた読み取り、重複しない読み取りを避けることができます.oracleのデフォルトはread committed独立性レベルです.カッコを書かない場合はデフォルトでsessionは現在のクライアントを指します.
  • set [global/session]  Transaction isolation levelトランザクション独立性レベル
  • の設定
  • select @@tx_isolationクエリ現在のトランザクション独立性レベル
  •   問題1:ダーティリード:1つのデータが別のコミットされていないデータに読み出されます.
    a:1000;
    b:1000;
    a:
    start transaction;
    update account set money=money-100 where name=a;
    update account set money=money+100 where name=b;
    b:
    start transaction;
    select*from account;
    a:900;
    b=1100;
    a:
    rollback;
    b:
    start transaction;
    select*from account;
    a:1000;
    b:1000;
    
    
    

    問題2:繰り返し不可:1つのトランザクション内でテーブル内の1行のデータを複数回読み出す結果が異なる.ダーティ・リードとの違いは、ダーティ・リードは前のトランザクションがコミットしていないダーティ・データを読み出し、繰り返し不可リードは前のトランザクションがコミットしたデータを再読み込みします.
    a:1000 1000 1000;
    b:    :
    
    
    ------
    b:   a   
    b:start transaction;
    select    from account where name='a';--    1000;
    select    from account where name='a';--    1000;
    select    from account where name='a';--    1000;
    ------
    a       :
    start transaction;
    update account set   =money-1000 where name='a';---
    commit;
    ------
    b      :
    select   +  +   from account where name='a';---2000;
    commit;
    
    
           :
      (  ):
                         ,         。
    a: 1000
                b: 1000
                d:       
                
                -----------
                d:
                    start transaction;
                    select sum(money) from account; --- 2000  
                    select count(name) from account; --- 2  
                    
                    ------
                    c:
                        start transaction;
                            insert into account values(c,4000);
                         commit;
                    ------
                   
                    select sum(money)/count(name) from account; ---   :2000 / 
                    commit;
                |------------|
       |------------|

    トランザクション独立性レベルの問題:異なる独立性レベルでの同時問題のデモ
  • set   Transaction isolation levelトランザクション独立性レベル
  • の設定
  • select @@tx_isolationクエリ現在のトランザクション独立性レベル
  • 1.トランザクションの独立性レベルをread uncommittedに設定すると、ダーティリード、重複不可リード、ダミーリードが発生します.
    A  
    set transaction isolation level  read uncommitted;
    start transaction;
    select * from account;
    -----  a   1000 ,  b  
    
    B  
    start transaction;
    update account set money=money+100 where name='aaa';
    -----    ,  a    
    
    select * from account
    -----  a  100 ,   a   b      (  )

    2.トランザクションの独立性レベルをread committedに設定すると、重複しない読み取りと虚読みが発生しますが、汚れた読み取りは回避されます. 
    A  
    set transaction isolation level  read committed;
    start transaction;
    select * from account;
    -----  a   1000 ,  b  
    B  
    start transaction;
    update account set money=money+100 where name='aaa';
    commit;
    -----  a  
    
    
    select * from account;
    -----  a    100,   ,a            ,    a           (     )
    

    3.トランザクションの独立性レベルをrepeatable read(mysqlデフォルトレベル)に設定すると、虚読が発生しますが、汚れた読み取り、重複しない読み取りは回避されます.
    A  
    set transaction isolation level repeatable read;
    start transaction;
    select * from account;
    ----    4   ,  b  
    
    B  
    start transaction;
    insert into account(name,money) values('ggg',1000);
    commit;
    -----   a  
    
    select * from account;
    ----      5   ,      a              (  )
    

    4.トランザクションの独立性レベルをSerializableに設定すると、すべての問題が回避されます.
    A  
    set transaction isolation level Serializable;
    start transaction;
    select * from account;
    -----  b  
    
    B  
    start transaction;
    insert into account(name,money) values('ggg',1000);
    -----      ,    a        
    

     データベース内のロックのメカニズム:
  • 共有ロック:非serializable独立性レベルでクエリーを行い、ロックを追加せずにserializable独立性の下でクエリーを行い、共有ロックを追加します.特徴:共有ロックと排他ロックは共存できない;
  • 排他ロック:すべての独立性レベルで削除変更を行う操作には、そのロックの特徴が追加されます.任意のロックと共存しません.
  •  失われた問題の更新:
  •    2つのスレッドは同じクエリ結果に基づいて修正する、その後修正する人は先に修正した人の修正を上書きする.  2つのソリューション:
  •             悲観ロック:悲観ロック悲観的には、操作のたびに更新が失われるという問題が発生し、クエリーのたびに排他ロックが追加されます.      select*fromテーブル名for update;このとき排他ロックに参加した後、他のスレッドは待つしかありません.クエリが多いと効率が低下します
  •             楽観ロック:楽観ロックは、クエリーのたびに更新が失われないと楽観的に考えられます.バージョンフィールドを使用して制御します. テーブルにバージョンフィールドを追加します.updateテーブル名set stat=1 and version=version+1 where id=1 and version=0; クエリーが非常に多く、変更が非常に少なく、楽観的なロックを使用しています.  変更が非常に多く、クエリーが非常に少なく、悲観的なロック
  • を使用しています.
    OKはここまでにしましょう.