数据库事务备忘

以前对数据库事务的隔离级别概念不是很清楚,今天看到一篇文章,将这个事情讲得比较清楚,这里记录一下。

数据库事务的特性

  • 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):事务前后数据的完整性必须保持一致。在事务执行之前数据库是符合数据完整性约束的,无论事务是否执行成功,事务结束后的数据库中的数据也应该是符合完整性约束的。在某一时间点,如果数据库中的所有记录都能保证满足当前数据库中的所有约束,则可以说当前的数据库是符合数据完整性约束的。比如删部门表前应该删掉关联员工(已经建立外键),如果数据库服务器发生错误,有一个员工没删掉,那么此时员工的部门表已经删除,那么就不符合完整性约束了,所以这样的数据库也就性能太差啦!
  • 隔离性(Isolation):事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
  • 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务隔离性问题

如果不考虑事务的隔离性,会出现以下问题:

  • 脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据。

  • 不可重复读(虚读):指一个线程中的事务读取到了另外一个线程中提交的update的数据。

  • 幻读:指一个线程中的事务读取到了另外一个线程中提交的insert的数据。

将数据库设计为串行化程的数据库,让一张表在同一时间内只能有一个线程来操作。如果将数据库设计为这样,那数据库的效率太低了。所以数据库的设计并没有直接将数据库设计为串行化,而是为数据库提供多个隔离级别选项,使数据库的使用者可以根据使用情况自己定义到底需要什么样的隔离级别,不同隔离级别可以解决一些问题:

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能

级别越高,数据越安全,但性能越低。

不可重复读与幻读的区别

不可重复读与幻读比较相似,都是在一个事务中多次读取到不同的数据,但两者还是有一些区别。

不可重复读

所谓的虚读,也就是大家经常说的不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。 一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另 一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内 两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。

幻读

所谓幻读,是指事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也 修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一 样,一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。简单来说,幻读是由插入或者删除引起的。

两者大致的区别在于不可重复读是由于另一个事务对数据的更改所造成的,而幻读是由于另一个事务插入或删除引起的。

从总的结果来看, 似乎两者都表现为两次读取的结果不一致,但如果你从控制的角度来看, 两者的区别就比较大:

  • 对于前者, 只需要锁住满足条件的记录

  • 对于后者, 要锁住满足条件及其相近的记录

mysql事务相关操作

show engines; # 查看数据库支持的存储引擎
show variables like '%storage_engine%';       # 当前的存储引擎
select @@tx_isolation; # 查看当前的事务隔离级别,InnoDB的默认隔离级别是REPEATABLE-READ
select @@global.tx_isolation; # 查看系统当前隔离级别
set [global/session] transaction isolation level xxxx; # 设置隔离级别

另外理论上在MySQL中InnoDB的默认隔离级别是REPEATABLE-READ,还是会有幻读的问题,但MySQL5.5以上版本InnoDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。