天天看点

对MySQL并发问题及隔离级别的一些理解

数据库并发问题

脏读

事务A读取到了事务B未提交的数据,当事务B回滚后,事务A读取到的就是脏数据。

不可重复读

同一事务在事务内多次读取,读取到的数据不一致。例:在事务A的第一次读取和第二次读取中间,事务B更新或删除了数据,导致事务A两次读取到不同的数据,即不可重复读。

幻读

同一事务在事务内多次读取,读取到的记录数不一样。例:在事务A的第一次读取和第二次读取中间,事务B插入了新的记录(此记录符合事务A的查询条件),导致事务A读取到的记录数不一样,即幻读。

数据库隔离级别

MySQL数据库有4种隔离级别,隔离级别依次递增,相应的系统开销也依次递增。

未提交读:Read uncommitted

mysql> set [global|session] transaction isolation level read uncommitted;
           

本事务读取了别的事务未提交的数据,即脏读。下面以并发的A事务和B事务对银行账户的金额操作进行说明:

A事务 B事务

查询余额

mysql> select money from t_account where id=1234567;

+-------+

| money |

+-------+

| 10000 |

+-------+

1 row in set (0.01 sec)

开启事务

mysql>start transaction;

更新余额

mysql> update t_account set money=10001 where id=1234567;

开启事务

mysql> start transaction;

查询余额

mysql> select money from t_account where id=1234567;

+-------+

| money |

+-------+

| 10001 |

+-------+

1 row in set (0.01 sec)

回滚事务

mysql> rollback;

其它事务操作

mysql> 

提交事务

mysql> commit;

从上面表格可以看出,B事务在A事务更新了余额且未提交事务时,读取到了脏数据money=10001。

由于可以读到别的事务未提交的数据,所以同样会有不可重复读和幻读的情况发生,此隔离级别很少用于实际应用中。

已提交读:Read committed

mysql> set [global|session] transaction isolation level read committed;
           

本事务只能读取到别的事务提交后的数据。即事务未提交时,处于中间状态(不一致状态)的数据对其它事务是不可见的。下面以并发的A事务和B事务对银行账户的金额操作进行说明:

A事务 B事务

查询余额

mysql> select money from t_account where id=1234567;

+-------+

| money |

+-------+

| 10000 |

+-------+

1 row in set (0.01 sec)

开启事务

mysql>start transaction;

更新余额

mysql> update t_account set money=10001 where id=1234567;

开启事务

mysql> start transaction;

查询余额

mysql> select money from t_account where id=1234567;

+-------+

| money |

+-------+

| 10000 |

+-------+

1 row in set (0.01 sec)

提交事务

mysql> commit;

查询余额

mysql> select money from t_account where id=1234567;

+-------+

| money |

+-------+

| 10001 |

+-------+

1 row in set (0.01 sec)

提交事务

mysql> commit;

从上面表格可以看出,B事务第一次查询余额时,A事务还未提交,所以读到的数据仍是A事务开始前的值money=10000。当B事务第二次查询余额时,此时A事务已提交,B事务读到的数据亦是A事务更新后的数据,即money=10001。

已提交读避免了脏读的情况,但仍然有不可重复读和幻读的情况发生。

可重复读:Repeatable read

mysql> set [global|session] transaction isolation level repeatable read;
           

对同一数据的多次读取,结果是一致的,除非数据被自身事务所修改。可重复读可以阻止脏读和不可重复读,但仍然有幻读的情况发生。

可序列化:Serializable

mysql> set [global|session] transaction isolation level serializable;
           

最高的隔离级别,完全服从ACID的隔离级别。所有事务依次逐个执行,这样事务之间就完全不可能产生干扰。该隔离级别可阻止脏读、不可重读和幻读。

以上4种隔离级别和相应的副作用关系如下:

隔离级别 脏读 不可重复读 幻读
未提交读
已提交读
可重复读
可序列化

继续阅读