首先我们要知道数据库如果支持事务的话,则一定要满足ACID四个特性:
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,不能有部分成功部分失败。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。下文我们会详细分析事务的隔离性。
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
如果不考虑事务的隔离性,我们来看看在多线程同时操作数据库时会发生什么问题:
1. 脏读
事务T2读取了已经被事务T1修改但还未提交的字段,由于某种原因,T1事物回滚,则T2读取的的数据就是无效的脏数据。
2. 不可重复读
不可重复读发生在一个事务内多次读取同一行数据时。当事务T2读取一行数据后,事务T1也读取了同一行数据,并更新了一些自动,之后提交了事务T1,此后事务T2再次读该行数据时会发现和之前读到的数据不一致了。
3. 幻读
幻读发生在 insert 或 delete 数据的时候,当事务T2第一次条件查询得到 N 条数据后,事务T1向该表插入或删除了符合查询条件的X条数据,导致事务T2再次执行查询语句,得到的数据条数和之前的不一致。
注:不可重复读和幻读都是读取到其他已提交的事务(这是和脏读不同的地方),二则的区别是,不可重复读针对的是 update 或 delete 操作影响了同一数据行,幻读针对的是 insert 操作影响了数据行数。
基于上面的问题,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
READ UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
READ COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。
REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。
然而不同数据库厂商对这些事务隔离级别的支持程度是不一样的,比如:
Oracle 支持两种事务隔离级别:READ COMMITTED(默认事务隔离级别),SERIALIZABLE;
MySQL 支持四种事务隔离级别,其中REPEATABLE READ为默认事务隔离级别;
SQL Server 支持 5 种隔离级别(多一种),READ COMMITTED 为默认隔离级别;
DB2、PostgreSQL 支持四种隔离级别,READ COMMITTED为默认隔离级别;
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
在MySQL数据库中查看当前会话的事务隔离级别:
select@@tx_isolation;
通过下面的语句设置事务的隔离级别
set[glogal | session]transactionisolationlevel隔离级别名称;
设置数据库的隔离级别一定要是在开启事务之前!
如果是在 JAVA 中设置隔离级别的话,要在调用 Connection 对象的 setAutoCommit(false) 方法之前。调用 Connection 对象的 setTransactionIsolation(level) 即可设置当前 Connection 的隔离级别,可选参数为:
领取专属 10元无门槛券
私享最新 技术干货