数据库事务

说明:本文章属于个人学习归纳总结,其中内容有摘自他人博客内容,严禁转载。

数据库事务:控制事务的隔离级别,保证数据的完整性,安全性,一致性,在此基础上实现高性能访问。

##msyql事务
1.mysql:传统理解 mysql 中的一次操作过程(sql 执行)是一次事务。
2.mysql:那么多个线程 同时操作 mysql 中的数据(同一条数据,一个范围内数据)就叫并发事务。
3.mysql:数据库层面使用不同的事务+   隔离级别来进行并发事务的控制,不同的隔离级别是因为数据库中内部锁机制的使用方式不同,例如有的是在select完成之后立马释放锁,有的是在整个事务commit 之后释放锁
。
--------------------------------------------------------------------------------------------------------------
##应用层事务
1.应用:其实每一个线程调用服务本质上也是事务。
2.应用:多个线程同时调用服务,叫并发调用服务,也可以叫并发事务。
3.应用:应用层应对并发事务(访问)解决方案有同步(悲观锁)、乐观锁(无锁CAS)。
​
作者:mark_rock
链接:https://www.imooc.com/article/17291?block_id=tuijian_wz

 

5.1 事务的四个属性

1.原子性:(Undo log)

事务是由一个或一组相互关联的SQL语句组成的,要么全部成功,要么全部失败。

2. 一致性:(Undo log)

​ 对于数据库的修改是一致的,即多个用户查询的数据是一致的。一致性主要由mysql日志机制处理,它记录数据的变化,为事务恢复提供追踪记录。

3.隔离性:(锁机制)

​ 每个事务都有自己的空间,和其他发生在系统的事务隔离开来,而且事务的结果只在他完全被执行时才能看到。

4.持久性:(Redo log)

​ 提交了这个事务之后对数据的修改是永久的。在Mysql中,如果系统崩溃,可以通过日志,恢复到系统崩溃前最后一次提交事务的数据。

InnoDB实现原理:

事务的ACID是通过InnoDB日志和锁来保证。事务的隔离性是通过数据库锁的机制实现的,持久性是通过redo log(重做日志)来实现,原子性和一致性是通过Undo log(撤销日志)来实现的。Undo log的原理很简单,为了满足原子性,在操作任何数据之前,首先将数据备份到Undo log。然后对数据进行修改。如果执行错误或者用户执行了rollback语句,系统可以利用Undo log中的额备份数据恢复到事务开始之前的状态。

Redo log 记录的是新数据的备份。在事务提交之前,只要将Redo log持久化,不需要将数据备份。系统崩溃时可以根据Redo log持久化的数据进行恢复即可。

 

 

5.2 事务隔离

不同隔离级别带来的数据操作问题:
 1.脏读:两个事务,t1事务可以读取到t2事务正在做更改的数据的中间状态(t2事务执行过程中),而这个数据的更改有可能不会被持久化(commit),而是rollback,导致t1在同一事务内的两次读取同一行数据得到结果不同。
 2.不可重复读:t1事务在整个事务执行过程中读取某一条记录多次,发现读取的此条记录不是每次都一样。
 3.幻读:t1事务在整个事务执行过程中读取某一范围内的数据,在第二次读取时发现多了几行或者少了几行。
-----------------------------------------------------------------------------------------------
数据库中的几种隔离级别
read uncommited--读未提交
    该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变,几种异常情况都可能出现。极易出错,没有安全性可言,基本不会使用。
read committed --读已提交
    该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,如Oracle,Sqlserver。
repeatable read --可重复读
    该隔离级别指一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,只要存在读改行数据就禁止写,消除了不可重复读和第二类更新丢失,这是Mysql数据库的默认隔离级别。
serializable --序列化读
     意思是说这个事务执行的时候不允许别的事务并发写操作的执行.完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,但是效率太低,一般不会用。
------------------------------------------------------------------------------------------------
数据库中的锁:
1.共享锁(Share locks简记为S锁):也称读锁,事务A对对象T加s锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。
​
2.排它锁(Exclusivelocks简记为X锁):也称写锁,事务A对对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象T直到A释放X锁。
​
3.更新锁(简记为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。
​
​
作者:mark_rock
链接:https://www.imooc.com/article/17291?block_id=tuijian_wz

 

5.2.1多线程操作数据库出现的问题

Mysql存储引擎是在表级别,存储引擎主要包括1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务、行级锁、并发 。Mysql 事务的默认级别是可重复读。

mysql> show variables like'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec)

创建一张表,设置引擎

CREATE TABLE `test`.`user`( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(20) NOT NULL , `account` DECIMAL , PRIMARY KEY (`id`)  )ENGINE=INNODB;

 

设置事务级别:

set tx_isolation='Read-uncommitted';

脏读:

​ 一个事务读取另一个事务中读取到一个未提交的数据。(事务设置的隔离级别为读未提交(Read-uncommittred会出现脏读))。

Mysql默认事务隔离级别为可重复读,避免了脏读、不可重复读。
数据A读到了数据B的未提交车数据

 

不可重复读:

​ 由于数据隔离,某个数据在一个事务范围内多次查询却返回不同的数据值。

幻读:

​ 第一个事务对一定范围内的数据进行批量修改,第二个事务在这个范围增加条数据,这时第一个事务会丢失对新增数据的修改。(由于数据隔离,第二个事务不可见第一个事务里修改的数据。例如:name唯一的user表中,事务A中新增一条name=jack的数据,提交之后,在事务B中查不到该条记录,但是新增却是失败的。这就是幻读)。

幻读

5.2.2 事务隔离级别

读未提交(READ-UNCOMMITTED ):最低级别,任何情况都可以发生。

读已提交(READ-COMMITTED ):可避免脏读发生,事务A对数据做的修改,提交之后会对事务B可见。

可重复读(REPEATABLE-READ ):可避免脏读,不可重复读。事务A对数据做的修改,提交之后,对于先于事务A开启的事务是不可见的。

串行化(SERIALIZABLE ):最高的隔离级别,可避免脏读,幻读,不可重复读,在这种隔离级别下,读取的每行数据都会加锁,会导致大量的锁争用,效率低下,性能差。

5.3 JDK事务

​ Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JPA等持久化机制所提供的相关平台框架的事务来实现。  Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器

5.3.1 事务几种实现方式

(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

(2)基于 TransactionProxyFactoryBean的声明式事务管理

(3)基于 @Transactional 的声明式事务管理

(4)基于Aspectj AOP配置事务

5.3.2 事务的传播特性

事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播。Spring定义了7中传播行为:

(1)propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。

(2)propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。

(3)propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。

(4)propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。

(5)propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

(6)propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。

(7)propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

传播行为 含义
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法总是需要独立的新事务,如果当前已存在事务,就会把当前事务挂起,直到新的事务提交或者回滚才恢复执行
PROPAGATION_NOT_SUPPORTED 表示当前方法不需要事务,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。简单点说, 一旦发现有事务存在,往下连进行都不进行,直接抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行,如果嵌套事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章