数据库四大性质和隔离等级

一、四大性质:ACID

如果想要说明一个数据库或者一个框架支持事务性操作,则必须要满足下面的四大特性:

  1. 原子性(Atomicity)
  2. 一致性(Consistency)
  3. 隔离性(Isolation)
  4. 持久性(Durability)

1. 原子性(Atomicity)

  • 事务包含的所有操作要么全部成功,要么全部失败回滚
  • 失败回滚的操作事务,将不能对事物有任何影响

2. 一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

数据库状态如何变化?

  • 每一次数据变更就会导致数据库的状态迁移。
  • 如果数据库的初始状态是C0,第一次事务T1的提交就会导致系统生成一个SYSTEM CHANGE NUMBER(SCN),这是数据库状态从C0转变成C1。
  • 执行第二个事务T2的时候数据库状态从T1变成T2
  • 以此类推,执行第Tn次事务的时候数据库状态由C(n-1)变成Cn。

一致性可以从一致读和一致写两个方面来理解。

  • 一致读 事务读取数据只能从一个状态中读取,不能从2个或者2个以上状态读取。

    也就是T(n)只能从C(n-1),C(n-2)… C(1)中的一个状态读取数据,不能一部分数据读取自C(n-1),而另一部分数据读取自C(n-2)。

  • 一致写 事务执行的数据变更只能基于上一个一致的状态,且只能体现在一个状态中。

    T(n)的变更结果只能基于C(n-1),C(n-2), …C(1)状态,且只能体现在C(n)状态中。也就是说,一个状态只能有一个事务变更数据,不允许有2个或者2个以上事务在一个状态中变更数据。至于具体一致写基于哪个状态,需要判断T(n)事务是否和T(n-1),T(n-2),…T(1)有依赖关系。

3. 隔离性(Isolation)

隔离性是指当多个用户并发访问数据库时,比如同时访问一张表数据库每一个用户开启的事务,不能被其他事务所做的操作干扰,多个并发事务之间,应当相互隔离。

例如同时有T1和T2两个并发事务,从T1角度来看,T2要不在T1执行之前就已经结束,要么在T1执行完成后才开始。

将多个事务隔离开,每个事务都不能访问到其他事务操作过程中的状态。

4. 持久性(Durability)

持久性是指事务的操作,一旦提交,对于数据库中数据的改变是永久性的,即使数据库发生故障也不能丢失已提交事务所完成的改变。

二、没有隔离性并发操作遇到的问题

1. 脏读

脏读是指一个事务读取了未提交事务执行过程中的数据。

当一个事务的操作正在多次修改数据,而在事务还未提交的时候,另外一个并发事务来读取了数据,就会导致读取到的数据并非是最终持久化之后的数据,这个数据就是脏读的数据。

事务A 事务B
事务开始 总数100
事务开始
插入新数据
查询数据总数 101
提交事务
回滚

2. 不可重复读

不可重复读是指对于数据库中的某个数据,一个事务执行过程中多次查询返回不同查询结果,这就是在事务执行过程中,数据被其他事务提交修改了

事务A 事务B
查询数据总数 100
插入新数据
查询数据总数 101
插入新数据
查询数据总数 102

不可重复读同脏读的区别在于

  • 脏读是一个事务读取了另一未完成的事务执行过程中的数据
  • 而不可重复读是一个事务执行过程中,另一事务提交并修改了当前事务正在读取的数据。

3. 幻读

幻读是事务非独立执行时发生的一种现象:

如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。

事务A 事务B
查询数据总数 100
插入新数据
查询数据总数 101

幻读和不可重复读都是读取了另一条已经提交的事务(这点同脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

三、隔离级别说明

MySQL定义了四种隔离级别,包括一些具体规则,用于限定事务内外哪些改变是可见的,哪些改变是不可见的。低级别的隔离一般支持更高的并发处理,并且拥有更低的系统开销。

Repeatable Read 可重复读

MySQL数据库默认的隔离级别。该级别解决了READ UNCOMMITTED隔离级别导致的问题。它保证同一事务的多个实例在并发读取事务时,会“看到同样的”数据行。不过,这会导致另外一个棘手问题“幻读”。InnoDB和Falcon存储引擎通过多版本并发控制机制解决了幻读问题。

Read Committed 读取提交内容

大多数数据库系统的默认隔离级别(但是不是MySQL的默认隔离级别),满足了隔离的早先简单定义:一个事务开始时,只能“看见”已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的“不可重复读”。这意味着用户运行同一个语句两次,看到的结果是不同的。

Read UnCommitted 读取未提交内容

在这个隔离级别,所有事务都可以“看到”未提交事务的执行结果。在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,并有很好的理由选择这样做。本隔离级别很少用于实际应用,因为它的性能也不必其他性能好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称为“脏读”

Serializable 可串行化

该级别是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,SERIALIZABLE是在每个读的数据行上加锁。在这个级别,可能导致大量的超时Timeout和锁竞争Lock Contention现象,实际应用中很少使用到这个级别,但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级

下面的表格总结了各种隔离级别和各自的缺点

隔离级别 脏读可能性 不可重复读可能性 幻读可能性
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

四、修改隔离级别的方法

全局修改

全局修改需要修改MySql的全局文件mysql.ini,修改内容如下

1
2
3
1 #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
2 [mysqld]
3 transaction-isolation = REPEATABLE-READ

语句修改

在命令行模式下连上MySql后,可以使用下列语句查看MySql当前隔离级别

1
2
3
4
5
6
7
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

可以使用下面的命令修改当前会话Session的隔离级

1
2
3
4
5
6
7
8
9
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)

AutoCommit 事务自动提交

MySql中有AutoCommit参数,默认为on,也就是开启状态。

它的作用是每一条单独的查询都是一个事务,自动开始,自动提交(语句执行完成就提交。如果你要适用select for update,而不手动调用 start transaction,这个for update的行锁机制等于没用,因为行锁在自动提交后就释放了)。所以事务隔离级别和锁机制即使你不显式调用start transaction,这种机制在单独一条语句查询中也是适用的。

在命令行模式下可以使用下面的命令查看当前MySql的autocommit是否开启

1
2
3
4
5
6
7
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)

如果需要关闭autocommit,我们可以使用下面语句设置

1
2
mysql> set autocommit=0;
# 0就是OFF,1就是ON。

设置为OFF之后,则用户执行语句之后,将一直处于一个事务中,直到执行commit或者rollback,才会结束当前事务,重新开始新的事务。