事务
mysql
1、四大特性(ACID)
一般来说,事务具有四大特性(ACID):
在MySQL中,事务的原子性、一致性、持久性是通过数据的redo log(重做日志)和undo log(回滚日志)来完成的,而隔离性则是数据库另一个重要机制锁来实现的。
- 原子性(Atomicity):事务是一个不可分割的单元,要么都执行要么都不执行。
- undo log是MySQL实现事务原子性的关键,InnoDB引擎的事务回滚就是靠undo log来撤销所有已经成功执行的sql语句。当事务对数据库进行修改时,InnoDB会生成对应的undo log,undo log属于逻辑日志,它记录了sql执行相关的信息,如果事务执行失败或调用了rollback,便会利用undo log中的信息将数据回滚到修改之前的样子。回滚时根据undo log的内容做与之前相反的工作:对于insert,回滚时会执行delete;对于delete,回滚时会执行insert;对于update,回滚时会执行一个相反的update,把数据还原到之前的状态。
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
- MySQL用redo log来保证数据持久性。由于数据存放在磁盘上,如果每次读写数据都从磁盘读取,效率会很低,所以InnoDB引擎提供了缓存池作为访问数据库的缓冲。当数据库读取数据时,会首先从缓存池中读取,往数据库写入数据时,会先写入缓存池,缓存池中更新的数据会定期刷新到磁盘中。这种方式虽然提升了数据读写效率,但是会破坏数据的持久性:如果MySQL宕机,缓存池中更新的数据还没有刷回到磁盘中,就会导致数据丢失。于是,redo log被引入进来解决这个问题:当数据修改时,除了修改缓存池中的数据,还会在在redo log中记录修改后的值,事务提交的时候,会对redo log刷盘持久化。这样即使MySQL宕机,重启时也可以通过读取redo log中的数据,来对数据库进行恢复。
- 隔离性(Isolation):数据库允许多个并发事务同时对数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
- 隔离性的实现是最复杂的,事务在并发情形下会互相干扰到的操作大体可以分为两类,与之相对应地,MySQL采用了两种方式来实现它们的隔离:
- 一个事务的写操作对另一个事务的写操作的影响:锁机制保证隔离性
- 一个事务的写操作对另一个事务的读操作的影响:MVCC保证隔离性
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2、事务隔离级别
2.1 问题
多个事务并发对数据库操作时,如果没有采取有效的隔离机制,会出现以下问题:
- 脏读:一个事务读取了另外一个事务未提交的数据。
- 不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。
- 幻读:在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
事务的隔离级别规定了在一个事务内的修改哪些在事务内和事务间可见,哪些不可见。SQL标准定义了四个隔离级别,一般而言,隔离级别越高,安全性越高,但是系统开销更大,并发性能也越差。下表从低到高列出隔离级别,以及它们存在的问题
2.2 第一种隔离级别:Read uncommitted(读未提交)
如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”,但是不排斥读线程实现。这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据
解决了更新丢失,但还是可能会出现脏读
2.3 第二种隔离级别:Read committed(读提交)
如果是一个读事务(线程),则允许其他事务读写,如果是写事务将会禁止其他事务访问该行数据,该隔离级别避免了脏读,但是可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
解决了更新丢失和脏读问题
2.4 第三种隔离级别:Repeatable read(可重复读取)
可重复读取是指在一个事务内,多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务(包括了读写),这样避免了不可重复读和脏读,但是有时可能会出现幻读。(读取数据的事务)可以通过“共享读镜”和“排他写锁”实现。
解决了更新丢失、脏读、不可重复读、但是还会出现幻读
2.5 第四种隔离级别:Serializable(可序化)
提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。