事务Transaction
@[TOC]
为什么写这系列的文章
在日常的工作和开发中,接触最多的便是与数据库打交道,无论你使用什么框架进行开发都绕不开事务的管理. 在Java开发中你可能会接触很多ORM框架,无论是Hibernate、MyBatis、还是Spring Jdbc 都会遇到事务的相关操作,再到中大型项目,你还会遇到单一数据源本地事务、多数据源本地事务、分布式事务、分布式多数据源事务等各种奇葩环境,数据的一致性也会面临各种挑战,在这时如何游刃有余的处理数据一致性就考验你对事务的理解,也正是这系列文章写作的真正原因.
由于我换工作原因,先后在峰鸟科技、人人网、国家电网待过一段时间,先后接触了不同的ORM框架
峰鸟科技
在这里由于SQL比较灵活,同时考虑到性能问题,这里直接使用的Spring JDBC,为了提高开发效率,我也开发了一个简单ORM框架,用来将javabean 和 数据库表信息进行关联,提供了简单的增删改查和分页功能,这时候也是我初步接触框架的开发,接触反射、代理等功能
人人网
在这里先后使用 Python 和 Java 进行开发,用过MyBatis、Django-orm、sqlalchemy、JPA、Jade等ORM框架。其中Jade是人人网自行开发的ORM框架,使用简单,支持条件化语句、支持多数据源切换和多数据源事务功能,相对MyBatis来说,使用方式和功能更加友好,强大。
国家电网
在这里主要使用的MyBaits,第一次看见Spring事务注解可以添加到类上而不是方法上,这也说明你懂的还是不多
通过不同的经历,也先后接触到了JDBC事务、Spring事务、Hibernate事务、MyBaits事务、多数据源事务、分布式事务的使用和解决方案,这里总结一下
事务概念
事务(Transaction):一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
数据库事务(Database Transaction):是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。
ACID
- atomicity 原子性
原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
- consistency 一致性
一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
- isolation 隔离性
隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
- durability 持久性
持久性:当事务正确完成后,它对于数据的改变是永久性的。
并发事务导致的问题
在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些的问题。
脏读(Dirty Read)
当一个事务读取另一个事务尚未提交的修改时,产生脏读。
同一事务内不是脏读。 一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚,也就是说读取出的数据其实是错误的。
非重复读(Nonrepeatable Read)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生非重复读。
幻读(Phantom Reads)
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。
丢失修改(Lost Update)
第一类:当两个事务更新相同的数据源,如果第一个事务被提交,第二个却被撤销,那么连同第一个事务做的更新也被撤销。
第二类:有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。
事务隔离
为了兼顾并发效率和异常控制,在标准SQL规范中,定义了4个事务隔离级别.
读未提交(Read Uncommitted)
直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别的事务可以读到这个改变。
Read Uncommitted允许脏读。
读已提交(Read Committed)
直译就是"读提交",意思就是语句提交以后,即执行了 Commit 以后别的事务就能读到这个改变,只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别。
Read Commited 不允许脏读,但会出现非重复读。
可重复读(Repeatable Read):
直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的。
Repeatable Read 不允许脏读,不允许非重复读,但是会出现幻象读。
串行读(Serializable)
直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行。完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
Serializable 不允许不一致现象的出现。
通过不同的隔离级别,可以防止一些并发事务问题,同时级别越高则相应性能越低,这个设置需要根据实际场景进行设置.
下一篇:MySQL数据库事务以及存储引擎
系列文章:
事务Transaction
Spring Cloud 分布式事务管理
Spring Cloud 分布式事务管理(二)2pc/3pc