读书笔记:知行合一,多在事上练。正如知道与做到,总是有一道巨大鸿沟。需要多实践。珍惜每次犯错后的内省、静坐。以及发现自己情绪不稳定时候,要及时重视起来,在事上好好打磨自己。
一、前言背景
二、事务AICD原则-酸
2.1 原子性Atomicity
2.2 隔离性Isolation
2.3 一致性Consistency
2.4 持久性Durability
三、事务并发问题与隔离级别
3.1 事务开启和结束的方式
3.2 事务并发可能引发的问题
3.2.1 脏读问题
3.2.2 不可重复读问题
3.2.3 幻读问题
3.3 事务的隔离级别
3.3.1 读未提交Read Uncommitted
3.3.2 读已提交Read Committed
3.3.3 可重复读Repeatable Read
3.3.4 串行化Serializable
四、CAP理论
4.1 一致性Consistency
4.2 可用性Availability
4.3 分区容忍性Partition tolerance
4.4 为什么CAP是不可能的三角?
五、BASE原则-碱
5.1 基本可用Basically Available
5.2 软状态Soft state
5.3 最终一致性Eventually consistent
一、前言背景
7年前,面试一家外企大厂,被面试官问到aicd、base相关领域基础,自此对数据库事务的印象极其深刻。除了因为这是全程英文面试,其中还有首次远程视频与国外技术大佬1v1面试,最重要的是高频的口头语、并不习惯的符号简称用语,让面试沟通并不顺畅。耐心的面试官,会换到具体描述来表达考察问题,甚至提出具体场景问题,让你更好展现自己。
日常我们了解最多的就是事务的四种隔离级别,但是如果面试官直接问你,了解数据库的aicd、base原则吗?分布式系统架构CAP理论是什么?看了这篇文章,我们也可以很好应对事务相关笼统、符号化、高压的面试。「子所不欲勿施于人,面试是双向的选择,如果面试官给候选人的印象高人一等、强势自负,可能会错过一些优秀的候选人」。
今天我们重点分析事物的四大特性、以及详细图解事务隔离级别,最后也整理分布式系统设计非常重要的CAP理论、BASE理论。一篇事务让大家看个满足。
二、事务的四大特性-AICD原则(酸)
数据库事务,有四大特性。即原子性(Atomicity)、隔离性(Isolation)、一致性(Consistency)、持久性(Durability)。这 4 个特性,英文首拼加起来,简称为 ACID-酸。面试官问到AICD原则,我们就可以分别介绍这四种特性的特点。
2.1 原子性Atomicity
原子性,指的是事务的操作是一个整体,不可切分。一个事务里的操作要么全部执行,要么全不执行。如果事务里的一部分失败,会导致全部回滚。最经典的事务原子性案例,就是张三给李四转账,张三账号扣款+李四账号增加金额,两个操作必须在一个事务里,一起提交成功,或者失败。
面试官可能会问,MySQL是如何保障事务原子性?
回答这个问题:可以基于原子性全部成功、全部失败回滚的特性去展开。具体就是我们在系列2里说的,当事务需要回滚的时候,undoLog日志会支持事务回滚,而redoLog在事务提交时,会写入commit内容,标识整个事务提交成功来确保bufferpool+undoLog+redoLog数据更新是一个完整操作,其中任何步骤出故障、或者系统宕机,都不会破坏事务的原子性。
2.2 一致性Consistency
在事务提交后,数据库的完整性约束没有被破坏,但事务执行过程中允许临时的破坏。
一致性,其实有弱一致性、强一致性和最终一致性三种。而数据库事务的一致性,默认说的就是强一致性。一致性的表现在于,事务提交后,该事务涉及的全部数据,都处于一致状态,也就是数据准确且可见可用。
同样拿银行转账例子举例,张三给李四转账100万完成后,张三看到自己银行卡少了100w,而且李四可以立马看到自己的卡可取现金额增加了100w。这就是事务提交后,数据的一致性。
此外,补充说一下事务的一致性的三种类型区别:
强一致性:某个数据被事务更新后,后续的访问能立马看到更新后的值。
弱一致性:某个数据被事务更新后,后续的查询可能查到更新后的值,也可能是更改前的值。
最终一致性:某个数据被更新后,需要经过一段时间后,才能查到更新的值;
2.3 隔离性Isolation
事务之间互不干扰,一个事务对数据的修改,不依赖或者影响其他事务。原则来说,一个还没提交的事务,此时对数据的修改,另一个事务看不到当前修改的脏数据。
事务的隔离性,根据四种不同隔离级别,事务呈现的隔离性也不同。在第三部分,我们重点展开。
2.4 持久性Durability
事务提交后,相关数据变更将写入数据库,不会被回滚,即使数据库宕机重启,数据也不会丢失。
那MySQL的数据持久性是如何保障的?或者是如何实现的?这里会涉及高可用集群架构、以及主备同步机制。抛开集群主备不说,我们专注剖析MySQL一条数据更新是如何存储数据来展开。
之前系列1、2、3文章说过,数据更新,最开始事务提交后,最新数据内容默认只存在缓存bufferpool。如果需要数据完成持久化,需要将redoLog落盘、bufferpool脏页落盘。而MySQL innoDB存储引擎,有专门的IO线程不定时负责将bufferpool里的脏页刷到我们的磁盘当中。刷盘过程,包括从内存刷到os page cache,以及从os page cache 到磁盘文件。假如系统还没来得及刷缓存脏数据到磁盘宕机了,MySQL重启恢复后,可以从redoLog日志里恢复脏数据。保障MySQL事务的持久性、以及一致性不被破坏。
当然,如果很不幸,按redoLog配置的某个参数策略,redoLog也还没来得及落盘,此时数据库宕机了,那没办法,MySQL事务提交了,确实有可能导致已提交事务的数据无法恢复回来。所以重要系统的数据库参数要慎重考虑。
刚性的事务,满足AICD四大特性,要求数据有强一致性。而柔性事务,遵循的是base原则,只保障最终一致性,这个在分布式系统应用会很常见。接下来第四部分我们重点说说见的少、但是也容易被问到的base理论。
三、事务并发问题与隔离级别
3.1 事务开启和结束的方式
MySQL的事务开启方式有2种,手工提交+自动提交,默认是自动提交。手工开启并提交事务比如:
begin;
update user_t set col1=‘拉丁解牛说技术,vs公号搜’ where id=1;
commit;
MySQL事务的结束方式,大部分人只知道有commit。其实还有rollback,以及断开连接,都会结束事务。
比如
begin;
update user_t set col1=‘拉丁解牛说技术,vs公号搜吧’ where id=1;
rollback;
3.2 事务并发可能引发的问题
在了解事务隔离级别之前,我们先看看并发事务可能引发哪些问题?
我们直接用案例直白分析说明。模拟场景如下,有一个非常简单的表t1(id,name),里面只有一条数据,id=1,name='拉丁解牛说技术'。
3.2.1 脏读问题
当前8:00:00整,两个客户端分别开启一个事务,TRX1和TRX2。TRX1里面的SQL如下:
begin;--8:00:00整
select * from t1 where id =1;--8:00:10查到name是拉丁解牛说技术
select * from t1 where id = 1;--10秒后,8:00:20再次查到name是拉丁解牛说技术66
commit;-- 结束事务
TRX2里面的SQL如下:
begin;--8:00:00整
update t1 set name='拉丁解牛说技术66' where id=1;-- 8:00:15
select * from t1 where id=1; -- 10秒后,8:00:25,查询查到name是拉丁解牛说技术66
commit;-- 结束事务
脏读的表现就是:事务TRX1在8点20秒读到了事务TRX2在8点15s修改id=1 的name值,但还没提交的脏数据'拉丁解牛说技术66';这就是脏读。一个事务读到了另一个事务还未提交的脏数据。
一句话总结:脏读,特指的就是一个事务里select查询到另一个事务update语句未提交的脏数据场景。
3.2.2 不可重复读问题
这个【不可重复读】是并发事务大家很容易搞混和迷糊的一个问题。不可重复读问题,就是不允许你重复读!这确实是个问题,一个事务里,我们想读多少次都可以,凭什么不允许重复读?是什么原因不让重复读?
我们具体模拟复现这个问题:
模拟场景如下,有一个非常简单的表t1(id,name),里面只有一条数据,id=1,name=’拉丁解牛说技术‘。
当前8:00:00整,两个客户端分别开启一个事务,TRX1和TRX2。TRX1里面的SQL如下:
begin;
select * from t1 where id =1;-- 8:00:10查到name是拉丁解牛说技术
select * from t1 where id=1;-- 10秒后,8:00:20,再次查询查到name是拉丁解牛说技术66
commit;-- 结束事务
TRX2里面的SQL如下:
begin;
update t1 set name='拉丁解牛说技术66' where id=1;-- 8:00:15 修改name,并提交事务
commit;
不可重复读问题的表现就是:事务TRX1分别在8点10秒,8点20秒读了id=1的数据,但是两次数据name值不一样,原因是事务TRX2在8点15s修改并提交的id=1 的name值的'拉丁解牛说技术66';这就违反了一个事务里,相同的查询,不管查询多少次,结果要求一致的原则。
两句话总结:不可重复读问题,就是一个事务读到了另一个事务已提交的数据。主要针对的是一个事务里select到另一个事务update或者delete语句的更新结果。
看到这里,不知道大家会不会有疑问:一个事务里,如果读到另一个事务未提交的数据是脏读,如果读到另一个事务已提交的数据是犯了不可重复读问题。那问题来了:一个事务里,重复读应该读到什么数据?这是个很好的问题,我们将在MySQL MVCC机制、锁原理里重点分析。
3.2.3 幻读问题
幻读,一句话总结:就是一个事务读到另一个事务新增的数据,特指一个事务里select查询查到了另一个事务insert数据。具体表现就是,一个事务里多次查询,有时候会查到另一个事务insert进来的数据。尤其是对方事务后来又回滚了,再次查询发现数据没了的幻觉。
比如还是刚才那个t1表,只有一条id=1的数据。
事务TRX1在8:00:00查询表
select * from t1;
事务TRX2在8点2秒进行了新增数据操作,并提交事务;
insert into t1(id,name)values(2,'拉丁解牛说技术22');
commit;
事务TRX1在8点10秒再次查询表select * from t1;
发现有两条id=1和id=2的数据了。
3.3 事务的隔离级别
针对上面讨论的三种事务并发问题,脏读、不可重复读、幻读,InnoDB存储引擎,支持事务的隔离级别也有四种,相应的也可以解决对应并发问题。
3.3.1 读未提交read uncommitted
当事务隔离级别为读未提交,也就是允许一个事务可以读到另一个事务update修改未提交的数据。这是并发事务最不安全的一种。会出现脏读、幻读、不可重复读问题。
3.3.2 读已提交read commmitted
顾名思义,可以读到另一个事务已提交的数据。这种事务隔离级别,就可以解决脏读问题。但是同样存在不可重复读、幻读问题。
3.3.3 可重复读repeatable read
MySQL InnoDB默认的事务隔离级别就是可重复读。也就是MySQL默认一个事务的重复查询结果是一样的,可以避免脏读、可重复读问题。而且MySQL 通过MVCC机制,在这种隔离级别下,也成功的解决了幻读问题。具体我们在MVCC机制详细探讨。
3.3.4 串行化serializable
事务串行执行。没有任何并发问题。当然这种事务隔离级别,IO读写效率肯定有所影响,一般不会选择这种事务隔离级别。
四、CAP理论
在分布式架构下,系统数据事务很难同时满足AICD特性,随后出现了CAP理论。
CAP是一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)的缩写。在一个分布式系统中,这三个特点最多只能同时实现两点,可以说是分布式架构里目前不可能实现的三角。在金融领域,也有个不可能三角:资本自由流动、货币完全独立、固定汇率,也就是通胀、利率、汇率三个指标。
在单体架构里,由于不存在分区情况,可以满足CAP理论。但是分布式环境下,由于分布式分区的特性,存在网络延迟、集群架构协同交互、故障转移等复杂特性,将让系统数据的一致性、可用性受到挑战。具体说:
4.1 一致性Consistency
在分布式系统中的所有数据样本,所有节点能否同时查到最新的数据。
4.2 可用性Availability
在分布式集群,整体是否能随时响应客户端的读写请求。
4.3 分区容错性Partition tolerance
分布式系统在分区出现故障的时候,仍然能够对外提供满足一致性和可用性的服务。
4.4 为什么CAP是不可能的三角?
在分布式架构里,举例子说:比如有abc三个节点,id=1数据在a节点更新,此时为了保证数据一致性C特性,那出现bc节点数据不可用的问题,也就是说,在a节点数据没同步更新到bc节点之前,客户端请求bc,是要返回失败的。否则返回的并不是最新的数据,违反了C特性。再通俗一点,张三给拉丁哥转账100块,数据更新在a节点了。拉丁哥去现在去b、c节点查询,发现没到账。这就是个CAP问题。
所以,分布式架构下,只能选AP,或者CP,无法同时满足CAP。
在这不可能的三角关系,出现了3种方案:
CP:强调分布式数据一致性,牺牲可用性。比如同步协议Paxos、Raft、ZAB或者2PC协议。CP类型的系统有 Zookeeper、redis、hbase、mongodb等。
AP:确保分布式数据高可用,牺牲一致性。数据同步的协议一般采用非严格协议,比如AP类型的数据库有 Couch DB、Cassandra、Amazon Dynamo等。
AC:重点保障数据高可用和强一致性,牺牲分区容错能力。典型AC型数据库系统有Oracle和MySQL,还有OceanBase。
五、事务BASE原则(碱)
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)的缩写。虽然是四个字母,但对应的是三个特性。
BASE理论,一般不直接应用到事务上来讲,通常是在分布式系统设计、数据库应用要求来说。
在这里我们之所以先说CAP理论,再说BASE原则,是因为在分布式系统中,CAP理论是指导思维,它是不可能兼顾实现的的三角关系。而BASE理论是CAP理论中AP(可用性和一致性)特性最佳权衡的结果,也就是分布式架构下最佳实践原则。
所以,BASE原则核心思想是:即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点, 采用适当的方式来使系统达到最终一致性(Eventual consistency)。
也就是BASE原则约等于CAP原则里的AP。
5.1 基本可用Basically Available
指分布式架构里在出现部分节点分区故障的时候,允许部分功能不可用或者不好用,但是保证基本核心功能可用。比如购物下单交易,可能系统正常时候是1s就响应返回给客户,但是由于某些硬件故障、服务故障,导致部分逻辑接口响应变慢,最后3s才返回给客户,我们说系此时满足基本可用特点。
基本可用性,主要是是分布式集群高可用能力,允许出现部分故障,但不影响整体功能结构完整。
5.2 软状态Soft state
相对数据状态而言,在软状态下,允许数据在多个分布式节点下存在一定的同步延时,也就是可能在某一段时间,也就是软状态下,在不同节点看到的数据是不一样的。
软状态的出现,允许同一时刻,客户端的请求,路由到不同节点可能出现数据不一致情况。
5.3 最终一致性Eventually consistent
数据或者系统经过一定时间后,最终能够达到一致的状态,而不需要实时保证系统数据的强一致性。相对软状态来说,数据状态不能一直是软状态,会有一个时间期限。期限过后,应当保证所有副本的最终一致性。这个达到最终一致性的时间阈值取决于系统负载,网络延时,数据同步复制方案等因素。
据此,AICD、CAP、BASE三大原则,以及事务隔离级别的核心原理分享到此结束。
推荐阅读拉丁解牛相关专题系列(欢迎交流讨论,搜索:拉丁解牛):