美文网首页互联网架构程序员
数据库事务实现原理

数据库事务实现原理

作者: hello_coke | 来源:发表于2018-05-12 12:54 被阅读23次

    ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

    原子性:是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

    一致性:是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    隔离性:是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

    持久性:是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

    个人理解据库事务主要做了两件事情:
    一是保证一致性结果,能在发生异常的时候快速恢复,也就是回滚
    二是并发访问的时候提供隔离

    隔离级别 脏读 不可重复读 幻读
    未提交读Read Uncommitted
    已提交读Read Committed
    可重复读Repeatable Read
    可串行化Serializable

    读未提交:就是一个事务可以读取另一个未提交事务的数据。
    读提交:就是一个事务要等另一个事务提交后才能读取数据。若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。
    重复读:就是在开始读取数据(事务开启)时,不再允许修改操作。重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
    Serializable:是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
    mysql 对应的InnoDB默认隔离级别是 重复读
    明细可以参考:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

    隔离怎么实现?
    隔离的实现也是依赖加锁机制, InnoDB存在两种锁:
    共享锁(S锁) :(插入/修改/删除)资源获取S锁之后,能加S锁,不能加X锁
    排它锁(X锁) : 资源加上X锁之后,不能加S锁,也不能加X锁

    S/X锁兼容表

    锁定类型 读锁 写锁
    读锁 Compatible Conflict
    写锁 Conflict Conflict

    具体的四种隔离级别锁实现:

    隔离级别 操作 生命周期
    读未提交
    读未提交 行级排它锁 立即释放
    读已提交 行级共享锁 立即释放
    读已提交 行级排它锁 事务结束
    可重复读 行级共享锁 事务结束
    可重复读 行级排它锁 事务结束
    可串行化 范围锁/表级别锁 事务结束
    可串行化 行级排它锁 事务结束

    读未提交
    为了解决丢失更新问题,需要对写操作加 X 锁

    读已提交
    为了保证读已提交,读操作加上 S 锁,这样如果其他事务有正在写的操作,必须等待写操作提交之后才能读,因为 S 和 X 互斥,如果在读的过程中其他事务想写,也必须等事务读完之后才可以。这里的 S 锁是一个临时 S 锁,表示事务读完之后立即释放该锁,可以让其他事务继续写,如果事务再读的话,就可能读到不一样的记录,这就是 不可重复读 了。

    可重复读
    为了让事务可以重复读,加在读操作的 S 锁变成了持续 S 锁,也就是直到事务结束时才释放该锁,这可以保证整个事务过程中,其他事务无法进行写操作,所以每次读出来的记录是一样的。

    可串行化
    序列化隔离级别下单纯的使用行锁已经实现不了,因为行锁不能阻止其他事务的插入操作,这就会导致幻读问题,这种情况下,我们可以把锁加到表上或者使用范围锁(间隙锁)。

    通过对锁的类型(读锁还是写锁),锁的粒度(行锁还是表锁),持有锁的时间(临时锁还是持续锁)合理的进行组合,就可以实现四种不同的隔离级别.这四种不同的加锁策略实际上又称为 封锁协议(Locking Protocol)

    Spring事务隔离级别

    • ISOLATION_DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应 。默认采用的是重复读,除了insert幻读外基本的场景都能cover
    • ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读
    • ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
    • ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
    • ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

    Spring事务传播级别

    • PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
    • PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
    • PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
    • PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。当数据库分库分表存在跨库的时候需要启用
    • PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
    • PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常。
    • PROPAGATION_NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
      demo如下
          <!--事务模板 -->  
        <bean id="transactionTemplate"  
            class="org.springframework.transaction.support.TransactionTemplate">  
            <property name="transactionManager">  
                <ref local="transactionManager" />  
            </property>  
            <!--ISOLATION_DEFAULT 表示由使用的数据库决定  -->  
            <property name="isolationLevelName" value="ISOLATION_DEFAULT"/>  
            <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>  
        </bean>  
    
               
    
    
    

    相关文章

      网友评论

        本文标题:数据库事务实现原理

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