互联网架构程序员

数据库事务实现原理

2018-05-12  本文已影响23人  hello_coke

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事务隔离级别

Spring事务传播级别

      <!--事务模板 -->  
    <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>  

           


上一篇 下一篇

猜你喜欢

热点阅读