任何关系型数据库中,ACID 是组成数据库的重要部分,是数据库事务的一组属性,该特性目的主要确保数据库在异常情况下保证数据的有效性。

数据库ACID特性

A(Atomicity)

原子性:

事务通常由多个语句组成。原子性保证将每个事务视为单个单元,该事务要么完全成功,要么完全失败。换句话说,如果在一个事务中,任何语句都未能完成,整个事务都会失败,未完成事务中的数据条目在数据库中保持不变。一个原子系统必须保证在任何情况下都具有原子性,包括电源故障,数据库错误和实例奔溃。同时,原子性可以防止在数据库中发生部分数据更新的情况。举一个例子,假设用户USER1 和 USER2 之间进行转账。这之间会进行两个动作,第一个动作,从USER1 账户中划钱;第二个动作,将从USER1账户中划拨的钱保存到 USER2 账户中。原子性在这个过程中可以保证数据库中的状态一致,即意味着从用户 USER1 账户减去(事务失败回滚不变)转账金额,在用户 USER2 账户中加上(事务失败回滚不变)转账金额。

上图中,假设用户USER1 有1000元,USER2用户有500 元,现在用户USER1向用户USER2 进行转账交易500元,如果在系统正常情况下,转账成功,那么 USER1账户的将会减去 500,用户USER2 账户的金额将会增加500。整个过程在事务完成后,数据库中的数据状态也将发生变化,用户USER1 将会被减去500 ,用户USER2将会被加上100,,并最终保持总账户金额一致。否则,用户之间的金额在数据库中保持原有数据状态。

C(Consistency)

一致性:

一致性确保事务只能使数据库从一个有效的状态进入到另一种有效的状态,并保证数据库数据最终的状态一致。一致性主要通过定义的一些规则来保证,比如在表对象上定义约束,触发器,级联或者之间组合而成的规则。通过这些规则来防止数据库中的非法事务(异常事务)。主外键主要维护数据库对象之间的参照完整性。例如在上面原子性的示例中,用户USER1 向用户USER2转账,那么就要在事务规则的约束下验证 USER1 + USER2 = 1500的一致有效性。假设在这个事务中,用户 USER1 向用户USER2 转账 500 元,那么用户 USER1 账户减去500,而 USER2 账户却没有变化,那么就要根据验证规则验证用户 USER1 + USER2 最终的状态是否为1500,现在看来,USER1账户转账 500 元,这个事务中用户 USER1 账户已经扣除了500 元,那么用户 USER1 现在的账户上剩余 500 元,对于事务来说,实现了原子性,但是账户验证结果为用户 USER1 + USER2 = 1000 元,这与事务规则验证用户 USER1 + USER2 = 1500 的状态不一致,那么必须要取消这个事务,并将受影响进行转账的事务回滚到事务前的一个状态。如果在一个事务中,存在其它约束、触发器等行为,则在提交事务之前,以相同的验证规则去检查每个更改操作。可能还有其它的约束规则,如要求表中的两列 COL1 和 COL 2必须为整数,那么如果此时输入 COL1 的值为一个浮点数,那么事务将会被取消,如果此列上有触发器,那么将会提示用户。还有在完整性约束的规则下,无论该表是参照表(主实体)或者引用表(子实体),是不允许删除其中一个表中的行,因为表主体之间收到主外键的约束影响。

I(Isolation)

隔离性:

隔离性主要保证多个事务可以同时进行,最简单的例子就是多个事务在对一张表同时进行读取和写入。隔离性可以保证多个事务之间同时进行,并互相不影响。同时,隔离性可以确保事务在并发执行时,数据库中的数据状态与执行事务所获取的的数据的状态是相同的。隔离性是事务并发控制的主要目标,根据不同的隔离特性,未完成的交易可能对于其他事务来说是不可见的。为了保证并发事务访问数据库对象,一般在关系型数据库中,会使用不同的方法来保证事务之间相互隔离。如Oracle使用SI(Snapshot Isolation)隔离特性,PostgreSQL使用SSL(Serializable Snapshot Isolation)隔离特性。

在数据库中,因为事务隔离的等级不同,所以根据事务需要的隔离级别,将隔离级别划分为四个等级,分别是读未提交(read uncommitted),读已提交度(read committed),可重复读(repeatable read)和可串行化(serializable)。也因为不同的隔离等级而引发了不同隔离等级下的一些现象,如脏读(dirty read),不可重复读(nonrepeatable read),幻读(phantom read)和序列化异常(serialization anomaly)。而在PostgreSQL中,在读未提交的隔离级别下,脏读不可能发生,但是在ANSI-SQL标准中,是被允许的。同时在可重复的隔离级别下,幻读不可能发生,但是在 ANSI-SQL 标准中,是被允许的。正常情况下,隔离会出现事务之间的等待情况,换句话说,在两个事务之间,其中的一个事务必须要等到另一个完成才能保证隔离成功。例如,依然使用上面的例子,用户 USER1 账户有 1000 元,用户 USER2 账户有 500 元。现在有两个事务T1和T2,T1事务需要从用户 USER1 账户转账 200 到 用户 USER2 账户;T2事务需要从用户 USER2账户转账 100 到 用户 USER1账户。那么最终的操作如下:

T1事务:从用户 USER1 账户中减去 200,

T1事务:给用户 USER2 账户中加上 200

T2事务:从用户 USER2 账户中减去 100

T2事务:给用户 USER1 账户中加上 100

如下过程:

hrdb=> CREATE TABLE tab_account(id integer,name varchar,account numeric(10,2));
CREATE TABLE
hrdb=> INSERT INTO tab_account VALUES(1,'USER1',1000.00);
INSERT 0 1
hrdb=> INSERT INTO tab_account VALUES(2,'USER2',500.00);
INSERT 0 1--事务T1
BEGIN;
UPDATE tab_account SET account = account - 200 WHERE name = 'USER1';
UPDATE tab_account SET account = account + 200 WHERE name = 'USER2';--事务T2
BEGIN;
--此时更新第一条语句时将会发生等待
UPDATE tab_account SET account = account - 100 WHERE name = 'USER2';
UPDATE tab_account SET account = account + 100 WHERE name = 'USER1';

如果上面操作按照顺序执行,尽管T2事务需要等待,但是事务之间依然是隔离的。

如果上面的操作很可能不是按照顺序执行的,有可能交叉执行,如下:

T1事务:从用户 USER1 账户中减去200

T2事务:从用户 USER2 账户中减去 100

T2事务:给用户 USER1 账户中加上 100

T1事务:给用户 USER2 账户中加上 200

此时,如果最后一个T1事务失败,T2事务已经更改了用户 USER1 账户,那么如果不退出该无效的数据库,用户 USER1的账户是不能够还原T2修改的用户 USER1 账户中的值的。由于两个事务视图写入同一数据字段,发生了写写失败,实际上就是隔离失败导致发生的死锁。在一般的系统中,可以通过恢复到最后一个已知的事务的状态,取消失败的事务T1并从该状态中重启中断的事务T2来解决该问题。在PostgreSQL中的的示例:

--事务T1
hrdb=> BEGIN;
BEGIN
hrdb=> UPDATE tab_account SET account = account - 200 WHERE name = 'USER1';
UPDATE 1
--此刻在该事务中USER2账户的状态是更新成功的
hrdb=> UPDATE tab_account SET account = account + 200 WHERE name = 'USER2';
--被PostgreSQL自动捕获到死锁状态并释放该更新
ERROR:  deadlock detected
DETAIL:  Process 10788 waits for ShareLock on transaction 1186; blocked by process 10830.
Process 10830 waits for ShareLock on transaction 1185; blocked by process 10788.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (0,2) in relation 'tab_account'
--此时,事务T1 的状态全部被回滚--事务T2
BEGIN;
hrdb=> BEGIN;
BEGIN
hrdb=> UPDATE tab_account SET account = account - 100 WHERE name = 'USER2';
UPDATE 1
--此刻更新的状态会处于等待状态,数据库自动检测到死锁并释放事务T1中的更新USER2账户更改信息
hrdb=> UPDATE tab_account SET account = account + 100 WHERE name = 'USER1';
UPDATE 1

D(Durability)

持久性:

事务的持久性主要用来保证事务一旦被提交,及时在系统出现异常的情况下(如断电,实例奔溃),未完成的事务进行回滚,完成的事务将被自动提交并记录在永久性存储介质上。假设此时用户 USER1 向用户USER2账户转账,200,那么此时用户USER1账户减去200,然后给用户 USER2 账户加上 200。此时,用户看到的信息是交易成功。此时的更改的数据依然被存放到磁盘buffer中或者说内存buffer中来等待被提交写入到磁盘,然而,假设此时未提交而发生了电源故障或者示例奔溃故障,那么更改将会由于没有写入到磁盘而丢失,而用户依然认为交易已经成功,此时该现象叫做持久化失败。那么为了维护这种持久化失败的情况,有两种方案可以解决该问题,预写日志记录和影子分页来解决。预写日志中记录在对数据更新之前将原始值记录在日志中来确保持久性。影子分页技术将更新条目保存为一个副本,并在事务提交时激活新的副本。关于影子分页技术,感兴趣的同学下去自行查找相关资料。

PostgreSQL中的ACID特性介绍相关推荐

  1. Node.js 4.0 中的 ES 6 特性介绍

    Node.js 4.0.0 已经发布了.这是和 io.js 合并之后的首个稳定版本,它带来了一系列的新特性,支持 ES 6的大部分特性.已经有很多ES 6 的特性介绍了,这里我们介绍一下该怎么使用它们 ...

  2. Postgresql杂谈 20—详解Postgresql中的Checkpoint、WAL日志和热备份恢复

    本文中,我们共同学习下Postgresql的WAL日志.WAL,是Write Ahead Log的简称,翻译过来就是预写日志,或者叫做重做日志.相信大家对数据库事务的四大特性ACID(原子性.一致性. ...

  3. mysql 5.7_MySQL 5.7新特性介绍

    1. 介绍身处MySQL这个圈子,能够切身地感受到大家对MySQL 5.7的期待和热情,似乎每个人都迫不及待的想要了解.学习和使用MySQL 5.7.那么,我们不禁要问,MySQL 5.7到底做了哪些 ...

  4. 【概念原理】四种SQL事务隔离级别和事务ACID特性

    2019独角兽企业重金招聘Python工程师标准>>> 事务是一组读写操作,并且具有只有所有操作都成功才算成功的特性. 事务隔离级别 SQL事务隔离级别由弱到强分别是:READ_UN ...

  5. Entity Framework Core 2.0 特性介绍和使用指南

    前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本, ...

  6. 从Oracle到PostgreSQL:Storage Index 特性 vs BRIN 索引

    墨墨导读:本文介绍 PostgreSQL 中的BRIN索引.为什么引人注意专门单独讲述这个性能?因为这就是活脱脱的 Oracle Exadata 中的 Storage Index 和 Oracle D ...

  7. 详述MySQL事务及ACID特性的实现原理

    " 事务是 MySQL 等关系型数据库区别于 NoSQL 的重要方面,是保证数据一致性的重要手段. 本文将首先介绍 MySQL 事务相关的基础概念,然后介绍事务的 ACID 特性,并分析其实 ...

  8. ACID特性的实现原理与MySQL事务的关系

    深入学习MySQL事务:ACID特性的实现原理 事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段.本文将首先介绍MySQL事务相关的基础概念,然后介绍事务的ACID ...

  9. Postgresql杂谈 16—Postgresql中的锁机制

    今天,我们学习下Postgresql中的锁机制.锁是数据库事务的基础,通过锁才能保证数据库在并发时能够保证数据的安全和一致,才能够达到事务的一致性和隔离性.但是任何事物都有它的两面性,引入锁同样会增加 ...

最新文章

  1. 滴滴魅族手机人脸识别没有反应_魅族Note9发布,亮点不足,价格却很有诚意
  2. 生活不易,且行且珍惜
  3. new char[x]和new char(x)的差别
  4. idea2019中文版
  5. auto errored after 报错解决_css重点知识和bug解决方法
  6. C Primer Plus(第六版):C语言概述
  7. MySQL-TCL语言
  8. jni c call java_Java通过-jni调用c语言
  9. UBUNTU修改控制台语言
  10. (20)System Verilog利用clocking块产生输出信号延迟激励
  11. Messari:自2019年,DeFi领域因黑客攻击损失超2.84亿美元资产
  12. 大数据平台搭建包含哪些层级
  13. 删除Windows10系统远程桌面的连接记录
  14. 滤波器基础02——滤波器的传递函数与性能参数
  15. R语言和医学统计学(4):秩和检验
  16. office_word_如何创建目录
  17. IGBT的驱动功率计算
  18. python idle是什么_idle是什么意思
  19. YYH的王国(NOIP模拟赛Round 6)
  20. 为什么很多新型编程语言都抛弃了 C 语言风格的 for 语句?

热门文章

  1. 惠州市政企信息化(互联网)市场调研报告
  2. 检测ip和port是否可连接
  3. 【VBA研究】用Ping命令测试IP地址是否通达
  4. 如何使用Nmap扫描所有TCP和UDP端口?
  5. AVB之镜像的签名及验证签名详解
  6. 所谓的牛逼,都是用苦逼换来的
  7. polkit启动失败_CentOS Linux 7.4中polkit服务启动失败
  8. 洛谷P1125笨小猴C语言
  9. uhuntu五笔输入法fcitx安装
  10. mac系统可以进行软件测试吗,iPhone和Mac如何加入Apple Beta版软件测试计划