ホームページ  >  記事  >  データベース  >  【MySQL 06】トランザクション処理

【MySQL 06】トランザクション処理

黄舟
黄舟オリジナル
2017-02-04 11:57:041221ブラウズ

1. トランザクションの ACID プロパティ

トランザクションには、原子性、一貫性、分離性、耐久性の 4 つの特性があります。

「銀行振込」を例に挙げます。

  • 原子性: トランザクションを構成するステートメントは論理単位を形成し、その一部のみを実行することはできません。つまり、トランザクションは分割できない最小単位です。例: 銀行振込処理中に、ある口座から振込金額を差し引いて、同時に別の口座に追加する必要があります。1 つの口座だけを変更するのは不合理です。

  • 一貫性: データベースはトランザクション実行の前後で一貫性があります。つまり、トランザクションはシステム状態を正しく変換する必要があります。例: 銀行振込プロセス中に、振込金額が 1 つの口座から別の口座に送金されるか、両方の口座が変更されず、それ以外の状況は発生しません。

  • 分離: 1 つのトランザクションは別のトランザクションに影響を与えません。つまり、どのトランザクションでもトランザクションが不完全な状態にあることを確認することは不可能です。たとえば、銀行送金中、送金トランザクションが送信されるまでは、別の送金トランザクションは待機状態になることしかできません。

  • 耐久性: トランザクション処理の効果は永久に保存できます。逆に、トランザクションは、サーバー、プロセス、通信、メディアの障害などを含むすべての障害に耐えることができる必要があります。例: 銀行振込処理中、振込後の口座ステータスを保存する必要があります。

2. トランザクションステータス

SET AUTOCOMMIT = 0 , 禁止自动提交 SET AUTOCOMMIT = 1, 开启自动提交
START TRANSACTION:开始事务,autocommit设为0,如果已经有一个事务在运行,则会触发一个隐藏的COMMIT
COMMIT:提交事务,保存更改,释放锁
ROLLBACK:回滚本事务对数据库的所有更改,然后结束事务,释放锁
SAVEPOINT savepoint_name:创建一个savepoint识别符来ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT savepoint_name:回滚到从savepoint_name开始对数据库的所有更改,这样就允许回滚事务中的一部分,保证更改的一个子集被提交
SET TRANSACTION:允许设置事务的隔离级别
LOCK TABLES:允许显式的锁住一个或多个table,会隐式的关闭当前打开的事务,建议在执行LOCK TABLES语句之前显式的commit或rollback。
我们一般所以一般在事务代码里不会使用LOCK TABLES

3. トランザクション操作

(1) まず従業員データテーブルを作成します:

mysql> create table employee(
    -> employeeID char(4),
    -> name varchar(20) not null,
    -> job varchar(20),
    -> departmentID int
    -> );
Query OK, 0 rows affected (0.10 sec)

mysql> insert into employee value ('7513' , 'Nora Edwar' , 'Programmer', 128);
mysql> insert into employee value ('9006' , 'Candy Burn' , 'Systems Ad',128 );
mysql> insert into employee value ( '9842' , 'Ben Smith' ,  'DBA' , 42);
mysql> insert into employee value ('9843',  'Pert Park'  , 'DBA' , 42 );
mysql> insert into employee value ('9845' , 'Ben Patel'  , 'DBA' , 128 );
mysql> insert into employee value ('9846' , 'Red Right' ,  null, 128 );
mysql> insert into employee value ('9847' , 'Run Wild'  ,  null , 128 );
mysql> insert into employee value ('9848' , 'Rip This J' , null , 128 );
mysql> insert into employee value ('9849' , 'Rip This J' , null  , 128 );
mysql> insert into employee value ( '9850' , 'Reader U' ,   null , 128 );
mysql> insert into employee value ('6651',  'Ajay Patel' , 'Programmer', 128 );

mysql> select * from employee;
+------------+------------+------------+--------------+
| employeeID | name       | job        | departmentID |
+------------+------------+------------+--------------+
| 6651       | Ajay Patel | Programmer |          128 |
| 7513       | Nora Edwar | Programmer |          128 |
| 9006       | Candy Burn | Systems Ad |          128 |
| 9842       | Ben Smith  | DBA        |           42 |
| 9843       | Pert Park  | DBA        |           42 |
| 9845       | Ben Patel  | DBA        |          128 |
| 9846       | Red Right  | NULL       |          128 |
| 9847       | Run Wild   | NULL       |          128 |
| 9848       | Rip This J | NULL       |          128 |
| 9849       | Rip This J | NULL       |          128 |
| 9850       | Reader U   | NULL       |          128 |
| 6651       | Ajay Patel | Programmer |          128 |
+------------+------------+------------+--------------+

(2) SET AUTOCOMMIT=0:

mysql> set autocommit = 0;//禁止自动提交

mysql> insert into employee values(null,'test1',null,128);

mysql> savepoint s1;//创建一个savepoint识别符

mysql> insert into employee values(null,"test2",null,128);

mysql> savepoint s2;//创建一个savepoint识别符

mysql> insert into employee values(null,"test3",null,128);

mysql> savepoint s3;//创建一个savepoint识别符mysql> select * from employee;
+------------+------------+------------+--------------+| employeeID | name       | job        | departmentID |
+------------+------------+------------+--------------+| 6651       | Ajay Patel | Programmer |          128 |
| 7513       | Nora Edwar | Programmer |          128 |
| 9006       | Candy Burn | Systems Ad |          128 |
| 9842       | Ben Smith  | DBA        |           42 |
| 9843       | Pert Park  | DBA        |           42 |
| 9845       | Ben Patel  | DBA        |          128 |
| 9846       | Red Right  | NULL       |          128 |
| 9847       | Run Wild   | NULL       |          128 |
| 9848       | Rip This J | NULL       |          128 |
| 9849       | Rip This J | NULL       |          128 |
| 9850       | Reader U   | NULL       |          128 |
| 6651       | Ajay Patel | Programmer |          128 |
| NULL       | test1      | NULL       |          128 |
| NULL       | test2      | NULL       |          128 || NULL       | test3      | NULL    |    128 |
+------------+------------+------------+--------------+


(3) ROLLBACK TO SAVEPOINT:

mysql> rollback to savepoint s1;//回滚到s1标签处:mysql> select * from employee;
+------------+------------+------------+--------------+| employeeID | name       | job        | departmentID |
+------------+------------+------------+--------------+| 6651       | Ajay Patel | Programmer |          128 |
| 7513       | Nora Edwar | Programmer |          128 |
| 9006       | Candy Burn | Systems Ad |          128 |
| 9842       | Ben Smith  | DBA        |           42 |
| 9843       | Pert Park  | DBA        |           42 |
| 9845       | Ben Patel  | DBA        |          128 |
| 9846       | Red Right  | NULL       |          128 |
| 9847       | Run Wild   | NULL       |          128 |
| 9848       | Rip This J | NULL       |          128 |
| 9849       | Rip This J | NULL       |          128 |
| 9850       | Reader U   | NULL       |          128 |
| 6651       | Ajay Patel | Programmer |          128 || NULL       | test1      | NULL       |      128 |
+------------+------------+------------+--------------+

(4) COMMIT:

mysql> commit;//提交事务
mysql> rollback to savepoint s2;
//一旦事务提交了,就不能再回滚ERROR 1305 (42000): SAVEPOINT s2 does not exist

(5) SET AUTOCOMMIT=1:

mysql> set autocommit = 1;//自动提交事务

mysql>  insert into employee values(null,"test4",null,128);

mysql> savepoint s4;//一旦创建,自动提交mysql> select * from employee;
+------------+------------+------------+--------------+| employeeID | name       | job        | departmentID |
+------------+------------+------------+--------------+| 6651       | Ajay Patel | Programmer |          128 |
| 7513       | Nora Edwar | Programmer |          128 |
| 9006       | Candy Burn | Systems Ad |          128 |
| 9842       | Ben Smith  | DBA        |           42 |
| 9843       | Pert Park  | DBA        |           42 |
| 9845       | Ben Patel  | DBA        |          128 |
| 9846       | Red Right  | NULL       |          128 |
| 9847       | Run Wild   | NULL       |          128 |
| 9848       | Rip This J | NULL       |          128 |
| 9849       | Rip This J | NULL       |          128 |
| 9850       | Reader U   | NULL       |          128 |
| 6651       | Ajay Patel | Programmer |          128 |
| NULL       | test1      | NULL       |          128 |
| NULL       | test2      | NULL       |          128 |
| NULL       | test3      | NULL       |          128 || NULL       | test4      | NULL       |          128 |
+------------+------------+------------+--------------+mysql> rollback to s4;//此时就无法回滚了
ERROR 1305 (42000): SAVEPOINT s4 does not exist

4、ロック

共有ロック、排他ロック、悲観的ロック、楽観的ロック、行レベルのロック、テーブルレベルのロック

  • 共有ロック: データを読み取るときに、データに共有ロックを追加します。共有は共有と直接競合しませんが、排他ロックと競合します。

  • 排他ロック: データを更新する場合、排他ロックを設置し、他のすべての操作を禁止します。

  • 悲観的ロック: 更新が多く、クエリが少ない場合に使用されます。悲観的ロックはデータベースの実際のロックではなく、トランザクションに対する人々の態度です。

  • 楽観的ロック: 更新はほとんどなく、多くのクエリに使用されます。楽観的ロックはデータベースの実際のロックではなく、物事に対する人々の態度です。

5. 同時処理

  • ダーティリード: 1 つのトランザクションが別のトランザクションからコミットされていないデータを読み取ります

    トランザクション 1: データの一部を更新します
    ————>トランザクション 2: トランザクション 1 によって更新されたレコードを読み取ります
    トランザクション1:commitを呼び出してコミットします
    このときトランザクション2で読み取られるデータは、データベースのメモリに格納されているデータであり、これをダーティリーディングと呼びます。
    読み取られたデータはダーティデータです
    詳細な説明:
    ダーティ読み取りとは、トランザクションがデータにアクセスし、データを変更したが、この時点ではこの変更がまだデータベースに送信されていないとき、
    別のトランザクションこのデータにもアクセスして使用されます。このデータはまだ送信されていないため、別のトランザクションによって読み取られたデータはダーティ データであり、ダーティ データに基づく演算が正しく行われない可能性があります。

  • Non-repeatable read: 同じトランザクション内で、同じデータが 2 回読み取られ、内容が異なります
  • トランザクション 1: レコードをクエリします

    ————->トランザクション 2: トランザクション 1 によってクエリされたレコードを更新します
    ———— ->トランザクション 2: commit を呼び出して送信します
    トランザクション 1: 最後のレコードを再度クエリします
    この時点で、トランザクション 1 は同じデータを 2 回クエリします。利用可能なコンテンツは異なります。これは非反復可能と呼ばれます。読む

  • 幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同 
    事务1:查询表中所有记录 
    ———->事务2:插入一条记录 
    ———->事务2:调用commit进行提交 
    事务1:再次查询表中所有记录 
    此时事务1两次查询到的记录是不一样的,称为幻读 
    详细解释: 
    幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改, 
    这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表 
    中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行, 
    就好象发生了幻觉一样。

6、事务隔离

事务隔离五种级别:

TRANSACTION_NONE 不使用事务。 
TRANSACTION_READ_UNCOMMITTED 允许脏读。 
TRANSACTION_READ_COMMITTED 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别 
TRANSACTION_REPEATABLE_READ 可以防止脏读和不可重复读, 
TRANSACTION_SERIALIZABLE 可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率

以上的五个事务隔离级别都是在Connection接口中定义的静态常量,

使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。 
如:con.setTransactionIsolation(Connection.REPEATABLE_READ);

注意:事务的隔离级别受到数据库的限制,不同的数据库支持的的隔离级别不一定相同

summary: 
(1)Serializable:可避免脏读、不可重复读、虚读情况的发生。 
(2)Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读,是 MySQL 默认的事务隔离级别) 
(3)Read committed:可避免脏读情况发生。(读取已提交的数据) 
(4)Read uncommitted:最低级别,以上情况均无法保证。(读取到了未提交的数据)

以上就是 【MySQL 06】事务处理的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。