MySQL的事务处理及隔离级别

事务是DBMS得执行单位。它由有限得数据库操作序列组成得。但不是任意得数据库操作序列都能成为事务。一般来说,事务是必须满足4个条件(ACID)
          原子性(Autmic):事务在执行性,要做到“要么不做,要么全做!”,就是说不允许事务部分得执行。即使因为故障而使事务不能完成,在rollback时也要消除对数据库得影响!
          一致性(Consistency)事务操作之后,数据库所处的状态和业务规则是一致的;比如a,b账户相互转账之后,总金额不变!
      隔离性(Isolation):如果多个事务并发执行,应像各个事务独立执行一样!
      持久性(Durability)事务提交后被持久化到数据库.
MYSQL的事务处理主要有两种方法。
     1、用 BEGIN, ROLLBACK, COMMIT来实现
       开始:START TRANSACTION或BEGIN语句可以开始一项新的事务
       提交:COMMIT可以提交当前事务,是变更成为永久变更
       回滚:ROLLBACK可以回滚当前事务,取消其变更
     2、直接用set来改变mysql的自动提交模式
           MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!
   我们可以通过set autocommit=0 禁止自动提交
                     set autocommit=1 开启自动提交
           来实现事务的处理。
           但注意当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束,并且 只用于当前连接
    ※ MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持!

自己的理解(关于脏读,不可重复读,幻读)
       ※脏读:一个事务读取了另一个未提交的并行事务写的数据。
       (事务T1更新了一行记录的内容,但是并没有提交所做的修改。事务T2读取更新后的行,然后T1执行回滚操作,取消了刚才所做的修改。现在T2所读取的行就无效了。)
      
       exp:
       小明的分数为89,事务A中把他的分数改为98,但事务A尚未提交。
       与此同时,
       事务B正在读取小明的分数,读取到 小明的分数为98。
       随后,
       事务A发生异常,而回滚了事务。 小明的分数又回滚为89。
       最后,
       事务B读取到的 小明的分数为98的数据即为脏数据,事务B做了一次脏读。
       (大部分数据库缺省的事物隔离级别都不会出现这种状况)
      
       ※不可重复读:一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。
       (事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录。然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化。)
       exp:
       在事务A中,读取到 小明的分数为89,操作没有完成,事务还没提交。
       与此同时,
       事务B把 小明的分数改为98,并提交了事务。
       随后,
       在事务A中,再次读取 小明的分数,此时工资变为98。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
       ※幻读:一个事务重新执行一个查询,返回一套符合查询条件的行,发现这些行因为其他最近提交的事务而发生了改变。
       (事务T1读取一条指定的WHERE子句所返回的结果集。然后事务T2新插入 一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE 子句的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突 然出现的一样。)
       exp:
       目前分数为90分以上的的学生有15人,事务A读取所有 分数为90分以上的的学生人数有15人
       此时, 事务B插入一条分数为99的学生记录。
       这是,事务A再次读取 90分以上的的学生,记录为16人。此时产生了幻读。
       (大部分数据库缺省的事物隔离级别都会出现这种状况,此种事物隔离级别将带来表级锁)

事务隔离级别描述:
       READ UNCOMMITTED:幻读,不可重复读和脏读均允许;
       READ COMMITTED:允许幻读和不可重复读,但不允许脏读;
       REPEATABLE READ:允许幻读,但不允许不可重复读和脏读;
       SERIALIZABLE:幻读,不可重复读和脏读都不允许; 
       ORACLE默认的是 READ COMMITTED。
       MYSQL默认的是 REPEATABLE READ。
      
       如果数据库的隔离级别为REAE_UNCOMMITTED, 则其他线程可以看到未提交的数据, 因此就出现脏读;
       如果数据库隔离级别设为READ_COMMITTED,即没提交的数据别人是看不见的,就避免了脏读;但是,正在读取的数据只获得了读取锁,读完之后就解锁,不管当前事务有没有结束,这样就容许其他事务修改本事务正在读取的数据。导致不可重复读。
       REPEATABLE READ因为对正在操作的数据加锁,并且只有等到事务结束才放开锁, 则可以避免不可重复读;
       REPEATABLE READ只能保证正在被本事务操作的数据不被其他事务修改,却无法保证有其他事务提交新的数据。 则有可能线程1在操作表T1的时候(特别是统计性的事务),其他线程仍然可以提交新数据到表T1,这样会导致线程1两次统计的结果不一致,就像发生幻觉一样。
       SERIALIZABLE因为获得范围锁,且事务是一个接着一个串行执行,则保证了不会发生幻读。
       由此可见,隔离级别越高,受其他事物干扰越少,并发性能越差。

       二个或以上事务在操作同一个共享记录集时,可能会出现的问题:
       (A)脏读 (B)不可重复读 (C)幻读
       隔离级别:
       (1)read-uncommit, (2)read-commit, (3)read-repeatable, (4)read-serializable
       都是用来阻止上面的问题的,其中:
       (1)什么都阻止不了。
       (2)阻止(A)
       (3)阻止(A)(B)
       (4)阻止(A)(B)(C)
       (1)->(4)隔离级别越高,性能损失越大。

修改事务的隔离级别:
       在MySQL中默认事务隔离级别是可重复读(Repeatable read).可通过SQL语句查询:
       查看InnoDB系统级别的事务隔离级别:
       mysql> SELECT @@global.tx_isolation;

       结果:
       +-----------------------+
       | @@global.tx_isolation |
       +-----------------------+
       | REPEATABLE-READ          |
       +-----------------------+

       查看InnoDB会话级别的事务隔离级别:
       mysql> SELECT @@tx_isolation;

       结果:
       +-----------------+
       | @@tx_isolation   |
       +-----------------+
       | REPEATABLE-READ |
       +-----------------+

       修改事务隔离级别:
       mysql> set global transaction isolation level read committed;
       mysql> set session transaction isolation level read committed;


第1级别:Read Uncommitted(读取未提交内容)

(1)所有事务都可以看到其他未提交事务的执行结果
(2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
(3)该级别引发的问题是——脏读(Dirty Read)读取到了未提交的数据

#首先,修改隔离级别
set tx_isolation='READ-UNCOMMITTED'; select @@tx_isolation;
+------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:也启动一个事务(那么两个事务交叉了)
       在事务B中执行更新语句,且不提交 start transaction; update tx set num
=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:那么这时候事务A能看到这个更新了的数据吗? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 |   --->可以看到!说明我们读到了事务B还没有提交的数据 | 2 | 2 | | 3 | 3 | +------+------+ #事务B:事务B回滚,仍然未提交 rollback; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:在事务A里面看到的也是B没有提交的数据 select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 |      --->脏读意味着我在这个事务中(A中),事务B虽然没有提交,但它任何一条数据变化,我都可以看到! | 2 | 2 | | 3 | 3 | +------+------+

 


第2级别:Read Committed(读取提交内容)

(1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
(2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
(3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
     |——>导致这种情况的原因可能有:(1)有一个交叉的事务有新的commit,导致了数据的改变;(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit

#首先修改隔离级别
set tx_isolation='read-committed'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | READ-COMMITTED | +----------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:也启动一个事务(那么两个事务交叉了)
       在这事务中更新数据,且未提交 start transaction; update tx set num
=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:这个时候我们在事务A中能看到数据的变化吗? select * from tx; ---------------> +------+------+                | | id | num |                | +------+------+                | | 1 | 1 |--->并不能看到!  | | 2 | 2 |                | | 3 | 3 |                | +------+------+                |——>相同的select语句,结果却不一样                                   | #事务B:如果提交了事务B呢?            | commit;                           |                                   | #事务A:                            | select * from tx; ---------------> +------+------+ | id | num | +------+------+ | 1 | 10 |--->因为事务B已经提交了,所以在A中我们看到了数据变化 | 2 | 2 | | 3 | 3 | +------+------+

 


第3级别:Repeatable Read(可重读)

(1)这是MySQL的默认事务隔离级别
(2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
(3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
(4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题

#首先,更改隔离级别
set tx_isolation='repeatable-read'; select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:开启一个新事务(那么这两个事务交叉了)
       在事务B中更新数据,并提交 start transaction; update tx set num
=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ commit; #事务A:这时候即使事务B已经提交了,但A能不能看到数据变化? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->还是看不到的!(这个级别2不一样,也说明级别3解决了不可重复读问题) | 2 | 2 | | 3 | 3 | +------+------+ #事务A:只有当事务A也提交了,它才能够看到数据变化 commit; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+

 


第4级别:Serializable(可串行化)

(1)这是最高的隔离级别
(2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
(3)在这个级别,可能导致大量的超时现象和锁竞争

#首先修改隔离界别
set tx_isolation='serializable'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | SERIALIZABLE | +----------------+ #事务A:开启一个新事务 start transaction; #事务B:在A没有commit之前,这个交叉事务是不能更改数据的 start transaction; insert tx values('4','4'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction update tx set num=10 where id=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 


           


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值