事务的隔离级别和死锁

作者: 代号027 | 来源:发表于2015-10-15 17:15 被阅读569次

    隔离事务远比想象的要复杂

    在SQL标准中订立了四种隔离级别

    READ UNCOMMITTED(未提交读)

    在READ UNCOMMITTED级别,事务的修改即使没有commit,对其他事务也是可见的,也被称为脏读(DirtyRead)。

    READ COMMITTED(提交读)

    大部分数据库默认的隔离级别为READ COMMITTED(但MySQL不是)。和UNCOMMITTED相对,在没有commit之前所有的修改对其他事务都是不可见的,也被称作不可重复读(nonrepeatable read)。

    REPEATABLE READ(可重复读)

    REPEATABLE READ解决了脏读的问题。该级别保证了同一个事务中多次读取同样的记录结果是一致的。但理论上该级别无法解决幻读问题。

    所谓幻读,当某个事物在读取某个范围的记录是,另一个失误又在该范围内插入了新的纪录,当之前的食物再次读取该范围的时候,会产生幻读(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。

    SERIALIZABLE(可串行化)

    SERIALIZABLE是最高的隔离级别。他通过强制事务串行执行,避免了前面所说的幻读的问题。也就是说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑使用。

    死锁

    死锁是两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。假设如下事务:

    事务1

    START TRANSACTION;
    UPDATE StockPrice SET colse=45.50 WHERE stock_id=4 and date='2002-05-01';
    UPDATE StockPrice SET colse=19.80 WHERE stock_id=3 and date='2002-05-02';
    COMMIT;
    

    事务2

    START TRANSACTION;
    UPDATE StockPrice SET high=20.12 WHERE stock_id=3 and date='2002-05-02';
    UPDATE StockPrice SET high=47.20 WHERE stock_id=4 and date='2002-05-02';
    COMMIT;
    

    如果凑巧,两个事物都执行了第一条UPDATE语句,更新了一行数据,同时也锁定了该行数据,借着每个事物都尝试去执行第二条UPDATE语句,却发现该行已经被对防锁死,然后都在等待对方释放锁,同时又相互持有对方需要的锁,则陷入死循环。

    为了解决这种问题,数据库系统实现了各种死锁检测和超时机制。一般会在检测到死锁后立即返回一个错误。

    死锁的出现有时候是由于存储引擎的实现方式导致的,而有的则是业务中真正的数据冲突,而且基本无法避免。死锁发生后只有回滚其中的一个事务才能打破死锁。所以程序设计的时候必须考虑如何处理死锁。

    事务日志

    事务日志可以帮助提高事务的效率,它的原理是在修改表的数据的时候只需要修改其内存拷贝,再把修改行为记录持久化到硬盘上的事务日志,事务日志持久以后,内存中被修改的数据在后台可以慢慢刷回到磁盘。如果出现断电等情况,下一次启动时,存储引擎会根据事务日志,恢复数据。

    因为事务日志是追加方式写入,所以写操作是磁盘上一小块区域的顺序I/O,不同于直接写入数据库磁盘的随机I/O,所以要快很多。

    相关文章

      网友评论

      • 592fca5e7cd9:楼主写得倒是容易懂,但是错别字略多啊:joy:

      本文标题:事务的隔离级别和死锁

      本文链接:https://www.haomeiwen.com/subject/xftacttx.html