MySQL中的事务初探

来源:互联网 发布:wlan办理软件下载 编辑:程序博客网 时间:2024/06/06 02:34
事务

        事务就是一组原子性的SQL查询,或者说一个独立地工作单元。事务内的语句,要么全部执行成功,要么全部执行失败。

事务的ACID特性

        A(atomicity):原子性。一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。
       C(consistency):一致性。数据库总是从一个一致性的状态转换到另外一个一致性的状态。
       I(isolation):隔离性。通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。
       D(durability):持久性。一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

        实现了ACID的数据库,相比没有实现ACID的数据库,通常会需要更强的CPU处理能力、更大的内存和更多的磁盘空间。对于一些不需要事务的查询类应用,选择一个非事务型的存储引擎,可以获得更高的性能。

隔离级别

        在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

        READ UNCOMMITTED(未提交读)
        在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
        事务1:更新一条数据                             
                        ------------->事务2:读取事务1更新的记录            
        事务1:调用commit进行提交

        READ COMMITTED(提交读)
         大多数数据库系统的默认隔离级别都是READ COMMITTED(但MySQL不是)。READ COMMITTED满足隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。即,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。
        事务1:查询一条记录                            
                       -------------->事务2:更新事务1查询的记录                            
                       -------------->事务2:调用commit进行提交            
        事务1:再次查询上次的记录

       我们首先设置我们的数据库事务级别为提交读,开启事务1,对student表做更新,但是不对其提交,再开启事务2对数据进行读取。 
       START TRANSACTION;
       SELECT * FROM student;
       UPDATE student SET name = 'pjc' WHERE id = 1;
       SELECT * FROM student;
       在该事务中,更新数据后,我们查看student表,其中的数据已经发生了变化。
       我们开启事务2对数据查询。
      
       SELECT * FROM student;
       在该事务中查询到的student还是旧数据,
       对事务1进行提交,再在事务2中查询,数据则获取到的为我们最新的数据。 
       在此期间事务2和事务1中读取数据,事务1未提交时两个读取的数据不一致,可能发生不可重复读。
       解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。

       REPEATABLE READ(可重复读)
       REPEATABLE READ 解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别无法解决幻读问题。幻读的意思是,当某个事务读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。可重复读是MySQL的默认事务隔离级别。
       事务1:查询表中所有记录                              
                      -------------->事务2:插入一条记录                              
                      -------------->事务2:调用commit进行提交            
       事务1:再次查询表中所有记录

       例如,如果我们的数据库的隔离级别是可重复读。我们的student表里只有10条数据。开启事务1,对student表进行查询。
       START TRANSACTION;
       SELECT * FROM student;
       在该事务中,查询结果数为10。
       我们开启事务2为student增加一条记录。
       INSERT INTO student(name)  VALUES('ppp');
       事务1再次查询student表时,发现记录数变为了11,两次的结果不一样,就像出现了幻觉一样。
       SELECT * FROM student;
       解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。
  
       不可重复读的重点是修改 : 
               同样的条件, 你读取过的数据,再次读取出来发现值不一样了
       幻读的重点在于新增或者删除:  
               同样的条件, 第 1 次和第 2 次读出来的记录数不一样

        SERIALIZABLE(可串行化)
        SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁征用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。


隔离级别脏读可能性不可重复读可能性幻读可能性加锁读READ UNCOMMITTEDYesYesYesNoREAD COMMITTEDNoYesYesNoREPEATABLE READNoNoYesNoSERIALIZABLENoNoNoYes

    
       
死锁

        死锁是指两个或者多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
        如:
        事务1:
        START TRANSACTION;
        UPDATE student SET name='aaa' WHERE id=1;
        UPDATE student SET name='bbb' WHERE id=2;
        COMMIT;

     
        事务2:
        START TRANSACTION;
        UPDATE student SET name='ccc' WHERE id=2;
        UPDATE student SET name='ddd' WHERE id=1;
        COMMIT;

        如果凑巧,两个事务都执行了第一条UPDATE语句,更新了一行数据,同时锁定了该行数据,接着每个事务都尝试去执行第二条UPDATE语句,却发现该行已经被对方锁定,然后两个事务都等待对方释放锁,同时又持有对方需要的锁,则陷入死循环。除非有外部因素介入才可能解除死锁。
     为了解决这种问题,数据库系统实现了各种死锁检测和死锁超时机制。锁的行为和顺序是和存储引擎相关的。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。死锁的产生有双重原因:有些是因为真正的数据冲突,这种情况通常很难避免,但有些则完全是由于存储引擎的实现方式导致的。死锁发生后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务型的系统,这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下只需要重新执行因死锁回滚的事务即可。
1 0