前言:
本篇文章主要针对网上对数据库事务的解释比较绕人而写,同时文章主要用以本人阶段性理解的记录。
正文
通俗理解
我是这么理解数据库事务:就像打游戏时候,给数据库(英雄)加了一些属性(技能)。
当数据库(英雄)具备了事务(技能)属性,那么他将会具有以下属性加成:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),也就是我们常说的事务ACID,这样才能保证事务(Transaction)中数据的正确性。这里还可以这么理解:原子性(Atomicity)、一致性(Consistency)、持久性(Durability)是被动属性。隔离性(Isolation)需要不断的切换让数据库(英雄)满足不同的要求(应付不同的攻击)。这就意味着了解数据库事务,我们主要需要了解数据库的主动技能如何切换。
进入主题:
操作数据库将会出现一些现象:
1. 脏读(dirty read):读到了未提交的数据
2. 不可重复读(unrepeatable read):两次读同一条数据却得到一条该数据被改变的的结果
3. 幻读(phantom problem):两次读取,得到该数据数量不一致。
导致原因及解决方法
- 导致原因
- 脏读:这种现象是由于update时先更新的是cache,而select操作正好是先在cache里找,如果事务a先更新到cache里,而还没有更新到库这时事务b正好select这条数据;
- 不可重复读:另一个事务对该条数据进行了update操作,导致相同查询条件下数据发生变化;
- 幻读:另一事务进行了insert操作,导致相同查询条件下数据数量改变。
- 解决办法
这就是所谓的英雄切换技能状态了。数据库需要切换自己的隔离级别来解决以上问题:
- Read Uncommitted(读取未提交内容):(未commit允许select!!!)该隔离级别,所有事务都可以看到其他未提交事务的执行结果(实际项目中基本不会使用)。
- Read Committed(读取提交内容):(未commit不允许select,但select时允许update!!!)。大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变(解决脏读问题)。
- Repeatable Read(可重读):(select时不允许update,但允许insert!!!)这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)
- Serializable(可串行化):(select时不允许insert!!!)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。项目中一般会通过程序中加锁,而不会在数据库事务上设置该等级。
注意:低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销,反之,高等级的隔离级别带来更高的系统开销。
spring项目中使用
选择加载事务的数据库
以上图片展示了整个配置过程的大致思路:mainDataSourceCrowdsourced为建立连接的数据库别名,通过对数据库设置懒加载模式后,将数据库对象命名dataSource。对dataSource进行事务之后,命名为transactionManagerCrowdsourced。
最后使用spring注解开启事务。
上述代码为spring对事务提供的默认设置。当然在代码使用过程可以重新设置,如:
以上是我在阅读事务以及对自身基于spring 事务的项目的理解。主要用作自身记录和理解。如果有读者发现错误的地方,希望指出。谢谢!