数据库ACID特性

目录

  • ACID介绍
    • 原子性(atomicity)
    • 一致性(consistency)
    • 隔离性(isolation)
    • 持久性(durability)
  • ACID实现
    • 原子性
    • 持久性
    • 隔离性
      • MVCC

ACID介绍

ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。

原子性(atomicity)

一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。

一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。 回滚实际上是一个比较高层抽象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比如,MVCC),并不修改实际的数据,如果有错并不会提交,所以很自然的支持回滚。 而在其他支持简单事务的系统中,不会在快照上更新,而直接操作实际数据。可以先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。

一致性(consistency)

数据库总是从一个一致性的状态转换到另一个一致性的状态。事务的一致性决定了一个系统设计和实现的复杂度,也导致了事务的不同隔离级别。

事务可以不同程度的一致性:

  • 强一致性:读操作可以立即读到提交的更新操作。
  • 弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间
  • 最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
  • 其他一致性变体还有:

单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

隔离性(isolation)

并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有:

  • 脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
  • 不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
  • 幻读:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

隔离级别从低到高有:

  • Read Uncommitted:最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生
  • Read Committed:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
  • Repeated Read:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
  • Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
    通常,在工程实践中,为了性能的考虑会对隔离性进行折中。

持久性(durability)

一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有能做到100%的持久性保证策略。



ACID实现

数据库的架构
数据库ACID特性_第1张图片
主要是存储引擎这部分为重点,负责MySQL中数据的存储和提取。MySQL中服务器层不管理事务,事务是由存储引擎实现的。MySQL支持事务的存储引擎有InnoDB、NDB Cluster等,其中InnoDB的使用最为广泛;其他存储引擎不支持事务,如MyIsam、Memory等。一般是InnoDB。

原子性

通过undo log来保证回滚,如果修改DB后没有提交,事物失败后会调用回滚,就会通过undo log来将DB回滚到修改之前的样子。

undo log属于逻辑日志,即根据修改操作“取反”来记录,如insert则为delete,update则为update。

持久性

通过redo log来实现。
InnoDB是有缓存(Buffer Pool)的,可以极大的提高读写数据的效率,如写入DB时是先写缓存,然后定时任务刷脏。但是如果MySQL也挂了,那缓存就丢失了,没来得及刷脏的数据也不见了。因此引入了redo log,先写日志后写缓存。

刷脏是随机IO,改哪里写哪里,且以页为单位,比较慢
redo log顺序IO,追加写入,比较快

redo log与binlog的区别
1、作用不同,前者仅为保证持久性,后者可以基于时间点恢复数据,还能用于主从复制
2、前者为InnoDB的存储引擎实现,后者为服务器层实现,对于其他引擎均可适用
3、前者是物理日志,基于磁盘的页,后者是二进制内容
4、写入时机不同,binlog在每次提交事务时写入,后者看情况

隔离性

表锁:锁定整张表,不会出现死锁,发生锁冲突几率高,并发低(MyISAM)

  • 表共享读锁:阻塞写,不阻塞读
  • 表独占写锁:阻塞读、写

行锁:仅锁定某一行,会出现死锁,发生锁冲突几率低,并发高(InnoDB)

MySQL的行锁是通过索引加载的,即行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,行锁则无法实现,取而代之的是表锁,此时其它事务无法对当前表进行更新或插入操作

间隙锁:当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内并不存在的记录,叫做【间隙】,InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁

MVCC

MVCC全称Multi-Version Concurrency Control,即多版本的并发控制协议

RR解决脏读、不可重复读、幻读等问题,使用的是MVCC:MVCC全称Multi-Version Concurrency Control,即多版本的并发控制协议。下面的例子很好的体现了MVCC的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)——在T5时刻,事务A和事务C可以读取到不同版本的数据。

你可能感兴趣的:(数据库,数据库)