如何理解数据库事务中的一致性的概念?

如何理解数据库事务中的一致性的概念?

数据库事务,4个属性,ACID,C就是Consistency,一致性,但是教科书式的定义说明,让人有点不清楚。谁能摆事实,讲道理,解释一下何为事务一致…
关注者
119
被浏览
19279

10 个回答

要想真正弄清楚这个问题,那是必须要把数据库理论中的事务机制从头开始看起,牵扯的内容比较多。
当然,如果只是想粗略的了解下,我就来举个例子吧——当然不可能太严谨。
假设我们10个人,每人有一个账号,里面有钱,可以转来转去,这组成了一个小型的数据系统,那么什么叫数据一致性?这是由你自己来定义的,比较通用的就是:这10个人的账号金额总数不变——满足这一条件,就叫数据一致,不满足,就叫数据不一致,或者在分布式的环境下,有一个数据在几个地方都保存了,那么任何时候,这几个地方的数据都必须相同,这也叫一致性。
现在我们就这个简单的一致性规则:10个人的账号金额总数不变。假设初始的时候每个人账号里有一万,A账号往B账号里转5000,这时候数据库要执行两行代码:
A:减去5000
B:加上5000
在执行完第一行代码的时候,这时候数据是不满足一致性条件的!必须要执行完第二行代码,数据才恢复到一致性的状态!换而言之,数据库中的数据是经常处于不一致的状态,这是不可避免的,因此我们提出了事务的概念,用于检测数据库中的数据是否处于一致性状态——如果数据库中有没有执行完的事务,那就是不一致的,否则,就是一致的。
上面的例子只是最简单的情况,实际的运用中要复杂得多,比如前面提到的分布式系统:某个数据存在了三个服务器上,现在要更新,就必须保证三个服务器上全都更新好,如果有一个没有成功,那么其他两个也应该维持不变,这又涉及到网络通信等问题,非常的折腾。
——————————————————————————————————————
评论中有提到了原子性,其实原子性与一致性是两个完全不同的概念,当然他们的联系也很紧密。
还是用上面的这个例子:
为了保证一致性(即10个人 的账号金额总数不变),那在我写代码的时候,如果写了代码:
A=A-5000;
那就必须要写上
B=B+5000,或者是C=C+5000,这样的代码才能保证了数据库的一致性状态。
那什么是原子性?
就是将上面的两行代码合成为一个事务,要么全做,要么全不做。
比如我写了两行代码:
A=A+2000;
B=B+3000;
如果这两行代码看成是一个事务,并且在某一时刻全执行完了,那么这个事务的原子性满足了,但却没有满足数据库的一致性。

12 条评论

sharpshooter
sharpshooter1 年前
回答的可以。
sharpshooter
sharpshooter1 年前
如果数据库中有没有执行完的事务,那就是不一致的,否则,就是一致的。
王慧Rs
王慧Rs1 年前
那这和原子性不就是一个意思了么?
几米憧憬
几米憧憬11 个月前
谢谢,这个回答我给101分
wlooong
wlooong11 个月前
不赞同这个答案 这分明就是原子性
明月清风
明月清风8 个月前
数据库的一致性原来是自己定义的,终于理解了!谢谢答主!
种冠鹏
种冠鹏4 个月前

我认为基本概念弄错了。如果按照答主的举的事务操作(不太恰当的例子)为:A=A+2000;B=B+3000;执行成功以后,从一个一致性状态到另一个一致性状态,就说明保证了数据一致性,如果在执行的过程中,发生了进程中断,导致A与B变成其他不可知的数据(此时没回滚),才说明数据不一致。数据一致性,指的是必须保证从一个一致性状态到另一个一致性状态。另外,事务中数据一致性与多副本(文中说的分布式系统那段)一致性,就是ACID和CAP中的C,不能划等号。另外,3副本的问题,答主说的是强同步复制问题,其实超过一半的数据更新成功,是可以返回成功的,看Paxos或者raft。喷完了,学生猿,请随意拍砖。

纳兰
纳兰 (作者) 回复种冠鹏4 个月前

能否再详细定义一下,什么是一致性状态?否则的话是循环定义。

种冠鹏
种冠鹏回复纳兰 (作者)4 个月前

不严格的说,从起始状态(空)开始,同一份数据的不同副本(所有或者超过半数)均相同,说明处于一致性状态。严格的讨论(非定义)关于一致性状态,请移步:ACID - Wikipedia 中 Consistency: The consistency property ensures that any transaction will bring the database from one valid state to another. 还有CAP theorem - Wikipedia中 Consistency: Every read receives the most recent write or an error 。关于状态之间的迭代或者变迁,请移步: 知乎 - 知乎 其中回答者

LynnCui是phxpaxos的核心开发者,回答的很详细。还有工程实现部分我看的是phxpaxos。原理介绍:微信自研生产级paxos类库PhxPaxos实现原理介绍 ,源码:tencent-wechat/phxpaxos 。这次不喷了,但是作为学生猿,还是请随意拍砖。因为填坑才是进步…

纳兰
纳兰 (作者)4 个月前

你要区分两个概念,分布式情况下的数据一致性,与集中式情况下的数据一致性。

分布式情况下的数据一致性应该分歧不大,只不过是定义严格或者不严格而已。

而对于集中式的情况,因为数据就只存储在服务器,没有数据副本的概念,所以这个时候,数据一致性就需要你自己来定义,你所说的acid链接里的valid状态,其实由你自己来定义。

更一般的说,数据一致性通常指关联数据之间的逻辑关系是否正确和完整。而数据存储的一致性模型则可以认为是存储系统和数据使用者之间的一种约定。如果使用者遵循这种约定,则可以得到系统所承诺的访问结果。

纳兰
纳兰 (作者)4 个月前

非常高兴你的讨论,因为有这样的疑惑是很正常的,在我上课的时候,这其实也一直是一个难点。

先简单说下,“一个事务中A的余额和B的余额同时增长10%”这样的情况,是否保持了一致性,要看你的定义,如果我对一致性的定义是“A与B的余额总和不变”,那么一致性就被破坏,系统处于不一致和状态,而如果你对一致性的定义是“A与B增加的幅度相同”,那么一致性就没有被破坏,系统还是处于一致性的状态。或者说,数据库管理系统根本就不知道一致性是个啥东西,这都是用户通过代码来进行定义的。

其实深入的讨论下有关事务的概念,就会明白这个一致性的定义了——如果不使用事务,那么在数据库执行多条语句的时候,有可能中间会出现问题而导致只执行了部分语句,因此,我们在这多条语句前面加上begin transaction,最后加上end transaction,这就是很清楚的标明了,在这些语句执行前,系统是处于一致性状态的,而在这些语句执行后,系统也是处于一致性状态的,至于这个一致性状态的定义,则是由用户通过这些语句来表达的。那么如果语句执行完成了,事务提交,这时候数据库系统就认为又到了一个一致性状态,而如果出现问题,系统则将操作回滚,返回初始状态,因为这也是一个一致性状态。而如果出现故障重启等事件时,数据库管理系统在重启后就会分析日志,将所有begin/end全都有的事务跳过或重做,而只有begin没有end的事务则undo,从而保证数据库在可用的时候都是一致性状态。

种冠鹏
种冠鹏回复纳兰 (作者)4 个月前

OK,结题!

原子性:记录之前的版本,允许回滚

一致性:事务开始和结束之间的中间状态不会被其他事务看到

隔离性:适当的破坏一致性来提升性能与并行度 例如:最终一致~=读未提交。

持久性:每一次的事务提交后就会保证不会丢失

讲数据库事务一致性怎么能不提数据库的ACID特性。
首先介绍事务,什么是事务,事务就是DBMS当中用户程序的任何一次执行,事务是DBMS能看到的基本修改单元。

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

2. 一致性(Consistency)
一致性是指事务使得系统从一个一致的状态转换到另一个一致状态。事务的一致性决定了一个系统设计和实现的复杂度。事务可以不同程度的一致性:
强一致性:读操作可以立即读到提交的更新操作。
弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
其他一致性变体还有:
单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

3. 隔离性(Isolation)
并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有:
脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
幻读:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
事务的隔离级别从低到高有:
Read Uncommitted:最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
Read Committed:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
Repeated Read:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
通常,在工程实践中,为了性能的考虑会对隔离性进行折中。

4. 持久性(Durability)
事务提交后,对系统的影响是永久的。

记得在做MySQL培训时,也碰到过这个问题,我的理解如下:
定义:一致性:C,Consistency,事务必须是使数据库从一个一致性状态变到另一个一致性状态。(教科书式的定义说明)
理解by我:在事务T开始时,此时数据库有一种状态,这个状态是所有的MySQL对象处于一致的状态,例如数据库完整性约束正确,日志状态一致等,当事务T提交后,这时数据库又有了一个新的状态,不同的数据,不同的索引,不同的日志等,但此时,约束,数据,索引,日志等MySQL各种对象还是要保持一致性(正确性)。 这就是 从一个一致性的状态,变到另一个一致性的状态。也就是事务执行后,并没有破坏数据库的完整性约束(一切都是对的)。

MySQL数据库innodb的事务,是通过redo log(innodb log),undo log,锁机制,来维护这个一致性的。

一家之言,欢迎拍砖!

1 条评论

知乎用户知乎用户1 年前
在事务T开始时,此时数据库有一种状态,这个状态是所有的MySQL对象处于一致的状态,例如数据库完整性约束正确,日志状态一致等,当事务T提交后,这时数据库又有了一个新的状态,不同的数据,不同的索引,不同的日志等,但此时,约束,数据,索引,日志等MySQL各种对象还是要保持一致性(正确性)。

作者:韩忠康
链接:如何理解数据库事务中的一致性的概念? - 韩忠康的回答
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
答主说的这个“一致性”还是没理解,,数据,索引,日志等MySQL各种对象“抑制”指的是什么
一致性就是如果你做个银行数据库的话,无论怎么转账,钱的总数都不变。

讲道理

定义:数据库一致性(Database Consistency)是指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。

数据库状态如何变化?每一次数据变更就会导致数据库的状态迁移。如果数据库的初始状态是C0,第一次事务T1的提交就会导致系统生成一个SYSTEM CHANGE NUMBER(SCN),这是数据库状态从C0转变成C1。执行第二个事务T2的时候数据库状态从T1变成T2,以此类推,执行第Tn次事务的时候数据库状态由C(n-1)变成Cn。

定义一致性主要有2个方面,一致读和一致写。

一致写:事务执行的数据变更只能基于上一个一致的状态,且只能体现在一个状态中。T(n)的变更结果只能基于C(n-1),C(n-2), ...C(1)状态,且只能体现在C(n)状态中。也就是说,一个状态只能有一个事务变更数据,不允许有2个或者2个以上事务在一个状态中变更数据。至于具体一致写基于哪个状态,需要判断T(n)事务是否和T(n-1),T(n-2),...T(1)有依赖关系。

一致读:事务读取数据只能从一个状态中读取,不能从2个或者2个以上状态读取。也就是T(n)只能从C(n-1),C(n-2)... C(1)中的一个状态读取数据,不能一部分数据读取自C(n-1),而另一部分数据读取自C(n-2)。

摆事实

一致写:
定义100个事务T(1)...T(100)实现相同的逻辑 update table set i=i+1,i的初始值是0,那么并发执行这100个事务之后i的值是多少?可能很容易想到是100。那么怎么从一致性角度去理解呢?

数据库随机调度到T(50)执行,此时数据库状态是C(0),而其它事务都和T(50)有依赖关系,根据写一致性原理,其它事务必须等到T(50)执行完毕后数据库状态变为C(1)才可以执行。因此数据库利用锁机制阻塞其它事务的执行。直到T(50)执行完毕,数据库状态从C(0)迁移到C(1)。数据库唤醒其它事务后随机调度到T(89)执行,以此类推直到所有事务调度执行完毕,数据库状态最终变为C(100)。

一致读:
还是上面的例子,假设T(1)...T(100)顺序执行,在不同的时机执行select i from table,我们看到i的值是什么?
1. T(1)的执行过程中。数据库状态尚未迁移,读到的i=0
2. T(1)执行完毕,T(2)的执行过程中,数据库状态迁移至C(1),读到的i=1

不知道有没有回到LZ的问题。

原子性:无论一个事务里有多少执行步骤,这所有的步骤合起来是一个最小的执行单元,要么不做,要么全做,不存在只做到一半情况。比如银行转账,转出跟转入这两个包含在一个事务里的动作就是原子的。要么不转出也不转入,转出了就要转入。

一致性:事务执行前与执行后数据内在的逻辑始终是成立的。比如转账前与转账后两人存款的总和始终不变。

隔离性:虽说事务是原子的,要么不做,要么全做,不存在做一半的情况。但是从代码实现上来说,事务里的步骤还是一步一步执行的,还是存在事务做到一半的情况。比如转账,代码怎么写?就两行代码,是先转出扣钱,再转入加钱。两行代码中间,也就是转出之后,转入之前,此时数据是不一致的。那怎样始终保证数据一致?那就用一个类似自欺欺人的办法,让转账这个事务在完成之前对别人都不可见,事务完成之前别人看到的都是转账前的状态,看不到转账步骤中间不一致的状态,所谓”隔离”。

持久性:事务做完了就是做完了,就生效了。就像钱转给别人后当前这比转账交易就结束了,不可能再倒回来。

举个反例就清楚了:A向B转账100元,操作1:A账号减去100;操作2:B账号加上100,执行操作1后,进程中断,操作2没有执行,但是A的账户少了100元,B又没加上100元。从一个一致性状态到了一个不一致的状态。如果不能保证数据的一致性,也就是说操作结果不准确,那该操作就无意义了。为了保证一个事务不受其他事务的影响,一般通过设置事务的隔离级别和回滚机制保驾护航。关于事务的隔离级别:数据库中隔离性的四种级别详解与例子-争取一文全懂 - 知乎专栏

自己的理解:

一致性:同生共死。

展开阅读全文

没有更多推荐了,返回首页