什么是事务?
讲mysql的时候,提出了事务。
事务是指一组sql语句的集合, 集合中有多条sql语句
可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,
或者都失败, 这些sql语句的执行是一致的,作为一个整体执行
在什么时候想到使用事务
当我们的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成的功能,或者都失败,保证操作是符合要求的。
在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。<br /> ➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。<br /> ➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。<br /> ➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读<br /> ➢ SERIALIZABLE:串行化。不存在并发问题。<br />脏读:一个事物读取到了另外一个事物未提交的数据<br />幻读:增加或者删除 两个事物相同的语句,相同的条件读取的记录数不一样<br />可重复读:(mysql默认)事物A新增了一条数据,事物B读到的还是开始的数据,想要读到新的数据,事物A的语句+for update 或者 commit<br />不可重复读:重点是修改, 两次读取的数据不一样
解决方案
加锁
读的时候加共享锁,写的时候加排它锁,但是效率低,一般不这么干
MVCC
多版本并发控制
解决mysql默认级别不可重复读的问题 加间隙锁解决幻读
MVCC 如何工作?
SELECT:InnoDB 会根据以下条件检查每一行记录:
第一,InnoDB 只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行要么是在开始事务之前已经存在要么是事务自身插入或者修改过的。
第二,行的删除版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除。
INSERT:InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。
DELETE:InnoDB 为删除的每一行保存当前系统版本号作为行删除标识。
UPDATE:InnoDB 为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识保存这两个版本号,使大多数操作都不用加锁。
它不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
MySQL 事务实现原理是什么?
事务的实现是基于数据库的存储引擎,不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有InnoDB 和 NDB。
InnoDB 是高版本 MySQL 的默认的存储引擎,因此就以 InnoDB 的事务实现为例,InnoDB 是通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。
因此 InnoDB 的 RR 隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。
事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。
如何设置 MySQL 的事务隔离级别?
配置文件添加 transaction-isolation = repeatable-read
innoDB如何开启手动提交事物
set autocommit = 0;
由于是手动提交事物,其他客户端是查不到数据的
事物的传播行为
默认情况下,只有一个事务,所有的修改操作都在一个事务里面,要么一起提交,要么一起回滚,这没什么问题。但要是有2个或者2个事务以上该如何解决呢?
既然是传播,那么至少要有2个东西,才可以传播,我传给你或者你传给我,才叫传播,单体不存在传播这个行为;
定义
事务传播行为,指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。比如说,A事务方法调用了B事务方法,B是继续在调用者A的事务中运行呢?还是为自己另开一个新事物运行? 这就是由B的事务传播行为决定的。
种类
spring事务传播行为一共有7种:(前言: 当前事务指的是调用者自带的事务,A调用B,那么A就是当前事务)
propagation 传播
propagation_REQUIRED (默认传播行为),当前存在事物就加入到事物中,如果当前没有事务,就新建一个事务,这个当前事务指的是上一个方法的事务,是别人传递过去的,类似于重入锁,A方法和B方法都有事务,A方法调用B方法,A的事务会传递给B,使它们共用同一个事务,我起了个名字叫做重入事务
REQUIRES_NEW 开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起,直到新事物挂起
SUPPORTS 如果存在一个事务,支持当前事务,如果没有事务,则非事务执行 (查询操作)
MANDATORY(强制的) 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务
NEVER 总是非事务地执行,不加入任何事务;
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按 REQUIRED 属性执行。
ACID
如何保证ACID
原子性 undolog 事务的属性
原子性(Atomicity):
对数据的操作都视为一个原子一样不可分割,要么全部都执行,要么全部都不执行。当在执行中间过程出现错误的时候,会回滚到事务开始前的状态。
实现:通过undo log,undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
一致性(Consistent):
事务的执行结果必须是从一个一致性状态向另一个一致性状态的变更。比如说,A和B两个人一共有5000元,那么不管A转钱给B,或者B转钱给A,A+B的金额永远是5000。C:一致性(Consistency),在一个事务中,事务前后数据的完整性必须保持一致。
例:假设用户A和用户B两者的钱加起来一共是200,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是200,这就是事务的一致性。
如何保证:
通过undo,回滚机制来保证
隔离性(Isolation):
允许多个并发事务同时对数据进行读写和修改的能力,并发执行的多个事务之间相互隔离,不受到其他事务的干扰。
如何保证:
通过给操作的对象加悲观锁或者乐观锁,MVCC(undo log)来保证,RC不满足隔离性,RR满足隔离性
持久性(Durable):
事务完成后,对数据的修改是永久性的。任何事务或故障都不应导致数据丢失。
一旦事务提交,其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。这个duration指的是mysql服务器可以重启的情况下,crash掉之后(比如说只写到pool buffer中,还没有fsync到磁盘文件中),保证duration。而不是物理损坏,物理损坏由备份来负责的
如何保证:
是通过redo来保证的。