0%

数据库基础

主流的数据库

SqlServer数据库
是微软,.net程序员最爱,中型和大型项目,性能高

Oracle数据库
是甲骨文的,java程序员(必学),大型项目,特点是适合处理复杂业务逻辑。

Mysql数据库
是sun公司,属于甲骨文公司,中型大型项目,特点:并发性好。对简单的sql处理效率高。

db2数据库
是ibm公司,处理海量数据,大型项目。很强悍。

Informix数据库
是ibm公司。在银行系统,安全性高

Sybase数据库

阅读全文 »

CAP理论

CAP理论介绍

CAP 理论是一个已经经过证实的理论,指出在一个分布式系统中最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

阅读全文 »

前言

事务想必大家并不陌生,至于什么是 ACID,也是老生常谈了。不过暖男为了保证文章的完整性确保所有人都听得懂,我还是得先说说 ACID,然后再来介绍下什么是分布式事务和常见的分布式事务包括 2PC、3PC、TCC、本地消息表、消息事务、最大努力通知。

事务的基本概念

什么是事务?

什么是事务?举个生活中的例子:你去小卖铺买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个步骤失败,事务将撤销所有已成功的步骤。

明白上述例子,再来看事务的定义:事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。

ACID 理论

严格意义上的事务实现应该是具备原子性、一致性、隔离性和持久性,简称 ACID。

ACID理论:

  • 原子性(A):所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。
  • 一致性(C):事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。
  • 隔离性(I):所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。
  • 持久性(D):所谓的持久性,就是说一旦事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。

总结:通俗意义上事务就是为了使得一些更新操作要么都成功,要么都失败。

Redis事务吐槽

看到 ACID 理论,可能有人会说,不对啊 Redis 的事务不能保证所有操作要么都执行要么都不执行,为什么它也叫事务啊?

我们来看看 Redis 怎么说的:
步骤1:redis 官网解释了为什么不支持回滚,他们说首先如果命令出错那都是语法使用错误,是你们自己编程出错,而且这种情况应该在开发的时候就被检测出来,不应在生产环境中出现。
redis 官网解释事务

步骤2:然后 Redis 就是为了快!不需要提供回滚。
下面还有一段话我就不截图了,就是说就算提供回滚也没用,你这代码都写错了,回滚并不能使你免于编程错误。而且一般这种错也不可能进入到生产环境,所以选择更加简单、快速的方法,我们不支持回滚。

步骤3:你看看这说的好像很有道理,我们不提供回滚,因为我们不需要为你的编程错误买单!但好像哪里不对劲?角度、立场不同,大家自己品。

分布式事务基本概念

分布式事务产生的背景

分布式事物在什么时候会产生?

  • 传统项目(单体应用,多数据源情况下)
  • 分布式项目(soa,微服务,也就是跨服务完成事务情况下)

分布式事务产生的场景

单体项目多数据源

单体项目多数据源

单体系统访问多个数据库实例 当单体系统需要访问多个数据库(实例)时就会产生分布式事务。 比如:用户信息和订单信息分别在两个MySQL实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信 息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。 简言之:跨数据库实例产生分布式事务。

微服务架构

微服务架构

典型的场景就是微服务架构 微服务之间通过远程调用完成事务操作。比如:订单微服务和库存微服务,下单的 同时订单微服务请求库存微服务减库存。简言之:跨JVM进程产生分布式事务。

分布式理论(CAP理论&Base理论)

这里不讲解了,自己去了解即可。

2PC(两阶段提交)

2PC简介

2PC(Two-phase commit protocol),中文叫两阶段提交协议。两阶段提交协议是一种强一致性设计。
2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,2PC将事务分成两阶段,两阶段分别是指:准备阶段(Prepare phase)、提交阶段(commit phase)。

2PC处理流程

2PC处理流程

  1. 准备阶段(Prepare phase):事务协调者给每个事务参与者发送Prepare消息,每个参与者在本地执行事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向协调者反馈事务能否处理成功。
  2. 提交阶段(commit phase):事务协调者根据反馈消息,选择回滚事物或提交事物。如果事务协调者接受到事务参与者执行失败或超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Conmmit)消息

2PC存在的问题

  • 同步阻塞:所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  • 单点问题:协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。列如:在第一阶段已经完成,在阶段二协调者发生故障,所有事务参与者会一直处于阻塞状态,无法完成其它操作。
  • 数据不一致:在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  • 太过保守:任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

2PC实现方案

2PC实现方案有:

  • XA方案:传统数据库实现
  • Seata方案:阿里开源分布式事务框架

3PC(三阶段提交)

简介

3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。

3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。

3PC处理流程

3PC处理流程

  • 准备阶段(CanCommit):事务协调者询问事务参与者是否可以完成事务,在此阶段只是询问不做真正操作,防止某些事务参与者不可用情况下导致所有事务参与者都阻塞(不执行事务操作)。
  • 预提交阶段(PreCommit):在准备阶段所有的事务参与者都返回可以执行操作时,事务协调者要求每个事务参与者预提交此操作,并反馈是否可以提交。(执行事务操作,但不提交,只是反馈是否可以成功提交)
  • 提交阶段(DoCommit):事务协调者根据反馈消息,选择回滚事物或提交事物。如果每个事务参与者在预提交阶段返回准备成功,那么进行提交事。否则,进行回滚事物,释放资源。

超时机制:如果在预提交阶段超时,则中断事务;如果提交阶段超时,则事务参与者提交事物。

3PC的优缺点

优点:

  • 降低了阻塞范围,在等待超时后协调者或参与者会中断事务
  • 避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。

缺点:
数据不一致问题依然存在,即在参与者收到PreCommit请求后等待最终指令,如果此时协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

3PC与2PC的区别

增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为,但是它并不能发现所有的这种行为,只会减少这种情况的发生在准备阶段以后,协调者和参与者执行的任务中都增加了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据概率统计上超时后默认成功的正确性最大。

三阶段提交协议与两阶段提交协议相比,具有如上的优点,但是一旦发生超时,系统仍然会发生不一致,只不过这种情况很少见罢了,好处就是至少不会阻塞导致永远锁定资源。

TCC(补偿机制)

简介

TCC是Try、Confirm、Cancel三个词语的缩写。
TCC 要求每个分支事务实现三个操作:预处理(Try),确认(Confirm),撤销(Cancel)。
2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务。

TCC处理流程

TCC处理流程图

分为三个阶段:

  • Try阶段:是做业务检查以及资源预留(锁定)。此阶段仅是一个初步操作,需要和后续的Confirm阶段一起才能真正构成一个完整的业务逻辑。
  • Confirm阶段:是做确认提交。Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则 认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。Confirm阶段需要保证幂等性。
  • Cancel阶段:是做资源释放。Try阶段执行错误,取消执行Confirm阶段,调用Cancel阶段把Try阶段的操作撤销(回滚)了。通常情况下,Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。Cancel阶段需要保证幂等性。
  • TM事务管理器
    • TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公 用组件,是为了考虑系统结构和软件复用。
    • TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文, 追踪和记录状态,由于Confirm 和cancel失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求 多少次,其结果都相同。

TCC和2PC/3PC很像,不过TCC的事务控制都是业务代码层面的,而2PC/3PC则是资源层面的。

TCC又可以被称为两阶段补偿事务,第一阶段try只是预留资源,第二阶段要明确的告诉服务提供者,这个资源你到底要不要,对应第二阶段的confirm/cancel,用来清除第一阶段的影响,所以叫补偿型事务。

举例模拟TCC流程

前提:假设库存数量本来是50,那么可销售库存也是50。账户余额为50,可用余额也为50。

用户下单,买了1个单价为1元的商品。流程如下:

  • Try阶段:
    • 订单服务:修改订单的状态为支付中
    • 账户服务:账户余额不变,可用余额减1,然后将1这个数字冻结在一个单独的字段里
    • 库存服务:库存数量不变,可销售库存减1,然后将1这个数字冻结在一个单独的字段里
  • Confirm阶段:
    • 订单服务:修改订单的状态为支付完成
    • 账户服务:账户余额变为(当前值减冻结字段的值),可用余额不变(Try阶段减过了),冻结字段清0。
    • 库存服务:库存变为(当前值减冻结字段的值),可销售库存不变(Try阶段减过了),冻结字段清0。
  • cancel阶段
    • 订单服务:修改订单的状态为未支付
    • 账户服务:账户余额不变,可用余额变为(当前值加冻结字段的值),冻结字段清0。
    • 库存服务:库存不变,可销售库存变为(当前值加冻结字段的值),冻结字段清0。

TCC优缺点

优点

因为Try阶段检查并预留了资源,所以confirm阶段一般都可以执行成功。
资源锁定都是在业务代码中完成,不会block住DB,可以做到对db性能无影响。
TCC的实时性较高,所有的DB写操作都集中在confirm中,写操作的结果实时返回(失败时因为定时程序执行时间的关系,略有延迟)。

缺点

从流程分析中可以看到,因为事务状态管理,将产生多次DB操作,这将损耗一定的性能,并使得整个TCC事务时间拉长。

事务涉及方越多,Try、Confirm、Cancel中的代码就越复杂,可复用性就越底(这一点主要是相对最终一致性方案而言的)。另外涉及方越多,这几个阶段的处理时间越长,失败的可能性也越高。

TCC实现方案

TCC实现方案有:

  • Seata
  • tcc-transaction
  • ByteTCC
  • Himly

本地消息表(异步确保)

参考博客:
https://www.cnblogs.com/zhangliwei/p/9984129.html
https://blog.csdn.net/weixin_40533111/article/details/85069536

本地消息表这个方案最初是 eBay 提出的。
eBay 的完整方案:https://queue.acm.org/detail.cfm?id=1394128。

本地消息表处理流程

本地消息表

消息生产方:
需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说它们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。(会有定时任务)

消息消费方:
需要处理这个消息,并完成自己的业务逻辑。
如果本地事务处理成功,则发送消息到消息生产方删除对应消息数据,表明已经处理成功了。
如果处理失败(运行期异常,非业务失败),那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

本地消息表优缺点

优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

消息事务

RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。

第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。

再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。

并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。

如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。

如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。

可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。

消息事务

最大努力通知型

其实我觉得本地消息表也可以算最大努力,事务消息也可以算最大努力。

就本地消息表来说会有后台任务定时去查看未完成的消息,然后去调用对应的服务,当一个消息多次调用都失败的时候可以记录下然后引入人工,或者直接舍弃。这其实算是最大努力了。

事务消息也是一样,当半消息被commit了之后确实就是普通消息了,如果订阅者一直不消费或者消费不了则会一直重试,到最后进入死信队列。其实这也算最大努力。

所以最大努力通知其实只是表明了一种柔性事务的思想:我已经尽力我最大的努力想达成事务的最终一致了。

适用于对时间不敏感的业务,例如短信通知。

总结

可以看出 2PC 和 3PC 是一种强一致性事务,不过还是有数据不一致,阻塞等风险,而且只能用在数据库层面。

而 TCC 是一种补偿性事务思想,适用的范围更广,在业务层面实现,因此对业务的侵入性较大,每一个操作都需要实现对应的三个方法。

本地消息、事务消息和最大努力通知其实都是最终一致性事务,因此适用于一些对时间不敏感的业务。

什么是分布式锁?

在单jvm中,我们可以使用语言类库提供的锁保证线程安全。
在多jvm中,我们可以使用分布式锁来保证线程安全。

阅读全文 »

Zookeeper 基础

Zookeeper是什么?

zookeeper是一个分布式开源框架,是一个分布式协调服务,是给其他分布式程序提供一致性服务的软件。
zookeeper在CAP理论中的选择的是CP(一致性和分区容错性),牺牲了可用性。。
注意:zk保证的一致性是最终一致性,而不是强一致性。

阅读全文 »

Redis基础

Redis是什么?

Redis是c语言编写的,是开源免费的。是一个高性能的分布式内存数据库(存储结构为key/value键值对)。
Redis是基于内存运行的,并支持持久化的NoSQL数据库。是单线程的。

Redis优缺点

优点

  • 读写性能优异,Redis能读的速度是110000次/s,写的速度是81000次/s。
  • 支持数据持久化,支持AOF和RDB两种持久化方式。
  • 支持事务 // TODO
  • 数据结构丰富,支持String,hash,set,zset,list等数据结构
  • 支持主从复制,哨兵机制,分布式集群部署

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
阅读全文 »

Redis缓存穿透

什么是缓存穿透

缓存穿透:是指查询一个数据库不存在的数据。

Redis正常使用流程:
数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存中,直接向外返回空。

问题引出:
想象一下这个情况,如果传入的参数为-1,会是怎么样?这个-1,就是不存在的对象。就会每次都去查询数据库,而每次查询都是空,每次又都不会进行缓存。假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。即便是采用UUID,也是很容易找到一个不存在的KEY,进行对数据库的攻击。

解决方案

1)redis缓存空值(推荐方案)
对上诉所讲传入一个不存在的key,对数据库进行攻击,我们可以将这个不存在的key缓存一个null值放入到redis中,给null值得缓存设置一个比平常缓存短的过期时间即可。当这个不存在的key存在时,即可将null值删除,将数据存入key中即可。

2)判断key的数据格式(有局限,列如key没有任何规则)
网关判断客户端传入对应的key规则,如果不符合数据库的查询规则直接返回null值。

Redis缓存雪崩

什么是缓存雪崩

缓存雪崩:指在某一个时间段,缓存集中过期失效。

举例:
马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。

解决方案

1)使用二级缓存(推荐)
一级缓存使用ehcache,二级缓存使用redis。
一级缓存为原始缓存,二级缓存为拷贝缓存,一级缓存失效时,可以访问二级缓存。一级缓存失效时间设置为短期,二级缓存设置为长期。

2)均摊分配redis key的失效时间(推荐)
不同的key,设置不同的过期时间,让缓存失效的时间点不要过于集中。这种方式需要根据业务需求和正式场景看情况设置失效时间。

Redis缓存击穿

什么是缓存击穿

缓存击穿:热点数据突然过期。

举例:一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案

1)设置热点数据永远不过期。(推荐)

2)使用锁(不推荐,效率低)
单点项目使用本地锁,分布式项目使用分布式锁。
对于某种规则key中,在缓存失效后,通过加锁来控制读数据库写缓存的线程数量。也就是同一时间只允许一个线程查询数据和写缓存,其他线程等待。

CAP理论

CAP理论

  • 一致性:在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性,等同于所有节点访问同一份最新的数据副本。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
  • 可用性:每次请求都能获取到正确的响应,但是不保证获取的数据为最新数据。
  • 分区容错性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。

CAP 理论是一个已经经过证实的理论,指出在一个分布式系统中最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

BASE理论

Base理论

  • 基本可用:在分布式系统出现故障,允许损失部分可用性(服务降级、页面降级)。
  • 软状态:允许分布式系统出现中间状态。而且中间状态不影响系统的可用性。这里的中间状态是指不同的 data replication(数据备份节点)之间的数据更新可以出现延时的最终一致性。
  • 最终一致性:data replications 经过一段时间达到一致性。

BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致性(CAP的一致性就是强一致性),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

分布式事务什么场景会产生?

  • 单体项目多数据源情况下(跨数据库实例产生分布式事务)
  • soa架构和微服务架构下(跨服务产生分布式事务)

分布式事务相关协议有哪些?

  • 2PC:分为两个阶段,准备阶段、提交阶段
  • 3PC:分为三个阶段,准备阶段、预提交阶段、提交阶段

描述下2PC

2PC处理流程

2PC,两阶段性提交协议:

  • 准备阶段(Prepare phase):事务协调者给每个事务参与者发送Prepare消息,每个参与者在本地执行事务操作,但在执行完成后不会提交本地事务,而是先向协调者反馈事务能否处理成功。
  • 提交阶段(commit phase):事务协调者根据反馈消息,选择回滚事物或提交事物。如果事务协调者接受到事务参与者执行失败或超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Conmmit)消息

2PC存在的问题:

  • 同步阻塞:所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  • 单点问题:协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。列如:在第一阶段已经完成,在阶段二协调者发生故障,所有事务参与者会一直处于阻塞状态,无法完成其它操作。
  • 数据不一致:在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  • 太过保守:任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

描述下3PC

3PC处理流程

3PC,三阶段性提交协议,是基于2PC的,添加了一个阶段,并添加超时机制和:

  • 准备阶段(CanCommit):事务协调者询问事务参与者是否可以完成事务,在此阶段只是询问,防止某些事务参与者不可用情况下导致所有事务参与者都阻塞(不执行事务操作)。
  • 预提交阶段(PreCommit):在准备阶段所有的事务参与者都返回可以执行操作时,事务协调者要求每个事务参与者预提交此操作,并反馈是否可以提交。(执行事务操作,但不提交,只是反馈是否可以成功提交)
  • 提交阶段(DoCommit):事务协调者根据反馈消息,选择回滚事物或提交事物。如果每个事务参与者在预提交阶段返回准备成功,那么进行提交事。否则,进行回滚事物,释放资源。
  • 超时机制:如果在预提交阶段超时,则中断事务,事务回滚;如果提交阶段超时,则事务参与者提交事物。

3PC优点:

  • 降低了阻塞范围,在等待超时后协调者或参与者会中断事务
  • 避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。

3PC缺点:数据不一致问题依然存在,即在参与者收到PreCommit请求后等待最终指令,如果此时协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

描述下2PC与3PC,它们的区别有哪些?

增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为,但是它并不能发现所有的这种行为,只会减少这种情况的发生在准备阶段以后,协调者和参与者执行的任务中都增加了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据概率统计上超时后默认成功的正确性最大。

三阶段提交协议与两阶段提交协议相比,具有如上的优点,但是一旦发生超时,系统仍然会发生不一致,只不过这种情况很少见罢了,好处就是至少不会阻塞导致永远锁定资源。

描述下TCC

TCC处理流程图

TCC 是业务层面的分布式事务,分为三个阶段:

  • Try阶段:是做业务检查以及资源预留(锁定)。此阶段仅是一个初步操作,需要和后续的Confirm阶段一起才能真正构成一个完整的业务逻辑。
  • Confirm阶段:是做确认提交。Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则 认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。Confirm阶段需要保证幂等性。
  • Cancel阶段:是做资源释放。Try阶段执行错误,取消执行Confirm阶段,调用Cancel阶段把Try阶段的操作撤销(回滚)了。通常情况下,Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。Cancel阶段需要保证幂等性。

TCC和2PC/3PC很像,不过TCC的事务控制都是业务代码层面的,而2PC/3PC则是资源层面的。

TCC又可以被称为两阶段补偿事务,第一阶段try只是预留资源,第二阶段要明确的告诉服务提供者,这个资源你到底要不要,对应第二阶段的confirm/cancel,用来清除第一阶段的影响,所以叫补偿型事务。

分布式事务解决方案有哪些?

  • XA:传统数据库实现的2PC
  • 补偿机制TCC:补偿机制,是业务层面的分布式事务,可以认为是个2阶段性补偿事务
  • 本地消息表:异步确保最终一致性
  • 消息事务:基于MQ实现最终一致性
  • 最大努力通知性:也是基于MQ实现最终一致性

Seata

Seata模块组件

Seata模块组件
Seata 中有三大模块,分别是 TM、RM 和 TC。

  • TC (Transaction Coordinator):事务协调者;它是独立的中间件,需要独立部署运行。它维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager):事务管理器;定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager):资源管理器;管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

Seata执行流程
在 Seata 中,分布式事务的执行流程:

  • TM 开启分布式事务(TM 向 TC 注册全局事务记录);
  • 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 );
  • TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
  • TC 汇总事务信息,决定分布式事务是提交还是回滚;
  • TC 通知所有 RM 提交/回滚 资源,事务二阶段结束。

AT模式

AT模式是两阶段提交协议(2PC)的演变:

  • 阶段1:将业务数据和回滚日志记录在同一个本地事务提交,释放本地锁和连接资源(不阻塞)
  • 阶段2:
    • 分支事务全部完成,触发全局提交,不需要同步协调处理(只需要异步清除回滚日志)
    • 分支事务发来回滚请求,触发全局回滚,RM通过回滚日志完成分支事务的回滚。

AT模式主要流程:

  • TM请求TC,开启一个全局事务,全局事务创建成功并生成唯一的XID
  • RM向TC注册事务分支,并且执行本地事务逻辑,TC将事务分支纳入XID对应全局事务的管辖
  • XID通过服务调用链,传递到其他服务中(其他RM)
  • 当RM分支事务执行全部完成或有RM分支事务执行异常,TM向TC发起针对XID的全局事务提交或回滚
  • TC调度XID下管辖的全部RM分支事务完成提交或回滚请求。

核心概念:

  • 每个RM使用DataSourceProxy连接数据库,其目的是使用ConnectionProxy,使用数据源和数据连接代理的目 的就是在第一阶段将undo_log和业务数据放在一个本地事务提交,这样就保存了只要有业务操作就一定有 undo_log。
  • 在第一阶段undo_log中存放了数据修改前和修改后的值,为事务回滚作好准备,所以第一阶段完成就已经将分 支事务提交,也就释放了锁资源。
  • TM开启全局事务开始,将XID全局事务id放在事务上下文中,通过feign调用也将XID传入下游分支事务,每个 分支事务将自己的Branch ID分支事务ID与XID关联
  • 第二阶段全局事务提交,TC会通知各各分支参与者提交分支事务,在第一阶段就已经提交了分支事务,这里各 各参与者只需要删除undo_log即可,并且可以异步执行,第二阶段很快可以完成。
  • 第二阶段全局事务回滚,TC会通知各各分支参与者回滚分支事务,通过 XID 和 Branch ID 找到相应的回滚日 志,通过回滚日志生成反向的 SQL 并执行,以完成分支事务回滚到之前的状态,如果回滚失败则会重试回滚操作

简介

主从复制是什么?

主从复制:主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

Redis主从复制能做什么?

  1. 读写分离:master写,slave读,提高服务器的读写负载能力
  2. 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  3. 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
  4. 高可用基石:基于主从复制,构建哨兵模式域集群,实现Redis的高可用方案

Redis主从复制实现

主从连接(slave连接master)

从节点开启主从复制,有三种方式。
分别是:配置文件,启动服务命令,客户端命令。

方式1:配置文件

在从服务器的配置文件中加入:

1
slaveof <masterip> <masterport>

方式1:启动服务命令

redis-server启动命令后加入:

1
redis-server -slaveof <masterip> <masterport>

方式3:客户端命令

Redis服务器启动后,直接通过客户端执行命令

1
slaveof <masterip> <masterport>

主从断开连接

客户端执行命令:

1
slaveof no one

实例

注意:主从复制的开启,完全是在从节点发起的,不需要我们在主节点做任何事情。

// TODO

Redis主从复制原理

  • 主从复制流程大体可以分为3个阶段
    • 建立连接阶段(即准备阶段)
    • 数据同步阶段
    • 命令传播阶段

Redis主从复制工作流程

建立连接

简介

该阶段的主要作用是在主从节点之间建立连接,为数据同步做好准备。

建立连接工作流程

建立连接工作流程

步骤1:设置master的地址和端口,保存master信息
步骤2:建立socket连接
步骤3:发送ping命令(定时器任务)
步骤4:身份验证
步骤5:发送slave端口信息
至此,主从连接成功!

数据同步

简介

主从节点之间的连接建立以后,便可以开始进行数据同步,该阶段可以理解为从节点数据的初始化。具体执行的方式是:从节点向主节点发送psync命令(Redis2.8以前是sync命令),开始同步。
数据同步阶段是主从复制最核心的阶段,根据主从节点当前状态的不同,可以分为全量复制和部分复制,下面会有一章专门讲解这两种复制方式以及psync命令的执行过程,这里不再详述。

需要注意的是:在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端;而到了这一阶段及以后,主从节点互为客户端。原因在于:在此之前,主节点只需要响应从节点的请求即可,不需要主动发请求,而在数据同步阶段和后面的命令传播阶段,主节点需要主动向从节点发送请求(如推送缓冲区中的写命令),才能完成复制。

数据同步工作流程

数据同步工作流程

步骤1:请求同步数据
步骤2:创建RDB同步数据
步骤3:恢复RDB同步数据
步骤4:请求部分同步数据
步骤5:恢复部分同步数据
至此,数据同步工作完成!

数据同步阶段master说明

  1. 如果master数据量巨大,数据同步阶段应避开流量高峰期,避免造成master阻塞,影响业务正常执行
    • repl-backlog-size 1mb
  2. 复制缓冲区大小设定不合理,会导致数据溢出。如进行全量复制周期太长,进行部分复制时发现数据已
    经存在丢失的情况,必须进行第二次全量复制,致使slave陷入死循环状态。
  3. master单机内存占用主机内存的比例不应过大,建议使用50%-70%的内存,留下30%-50%的内存用于执
    行bgsave命令和创建复制缓冲区

数据同步阶段slave说明

  1. 为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,建议关闭此期间的对外服务
    • slave-serve-stale-data yes|no
  2. 数据同步阶段,master发送给slave信息可以理解master是slave的一个客户端,主动向slave发送
    命令
  3. 多个slave同时对master请求数据同步,master发送的RDB文件增多,会对带宽造成巨大冲击,如果
    master带宽不足,因此数据同步需要根据业务需求,适量错峰
  4. slave过多时,建议调整拓扑结构,由一主多从结构变为树状结构,中间的节点既是master,也是
    slave。注意使用树状结构时,由于层级深度,导致深度越高的slave与最顶层master间数据同步延迟
    较大,数据一致性变差,应谨慎选择

命令传播

注意:先看[全量复制和部分复制] + [心跳机制],再看这节内容。

简介

数据同步阶段完成后,主从节点进入命令传播阶段;在这个阶段主节点将自己执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。

在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。由于心跳机制的原理涉及部分复制,因此将在介绍了部分复制的相关内容后单独介绍该心跳机制。

延迟与数据不一致

  • 异步:需要注意的是,命令传播是异步的过程,即主节点发送写命令后并不会等待从节点的回复;
  • 数据不一致:因为是异步的,因此实际上主从节点之间很难保持实时的一致性,延迟在所难免。数据不一致的程度,与主从节点之间的网络状况、主节点写命令的执行频率、以及主节点中的repl-disable-tcp-nodelay配置等有关。

repl-disable-tcp-nodelay no

  • 该配置作用于命令传播阶段,控制主节点是否禁止与从节点的TCP_NODELAY;默认no,即不禁止TCP_NODELAY。当设置为yes时,TCP会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送频率与Linux内核的配置有关,默认配置为40ms。当设置为no时,TCP会立马将主节点的数据发送给从节点,带宽增加但延迟变小。
  • 一般来说,只有当应用对Redis数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为yes;多数情况使用默认值no。

命令传播工作流程

命令传播工作流程

主节点心跳流程:
主节点发送命令ping,判断从节点是否超时。

从节点心跳流程:
步骤1:从节点发送命令replconf ack offset
步骤2:主节点接受命令,判断从节点复制偏移量(offset)是否在复制缓冲区
步骤3:如果在,主节点复制偏移量(offset)和从节点复制偏移量(offset)相同,则忽略
步骤3:如果在,但主节点复制偏移量(offset)和从节点复制偏移量(offset)不同,则执行部分复制
步骤3:如果不在,则执行全量复制

全量复制和部分复制(数据同步阶段)

在Redis2.8以前,从节点向主节点发送sync命令请求同步数据,此时的同步方式是全量复制;在Redis2.8及以后,从节点可以发送psync命令请求同步数据,此时根据主从节点当前状态的不同,同步方式可能是全量复制或部分复制。

  • 全量复制:用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作。
  • 部分复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。需要注意的是,如果网络中断时间过长,导致主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制。

注意:后文介绍以Redis2.8及以后版本为例。

全量复制

全量复制触发条件

  1. 第一次进行主从连接
  2. 主节点无法进行部分复制(runid或offset不满足)

全量复制流程

Redis通过psync命令进行全量复制的过程如下:

  1. 从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行部分复制;
  2. 主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
  3. 主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
  4. 主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态
  5. 如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态

通过全量复制的过程可以看出,全量复制是非常重型的操作:

  1. 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的;
  2. 主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗
  3. 从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof,也会带来额外的消耗

部分复制

由于全量复制在主节点数据量较大时效率太低,因此Redis2.8开始提供部分复制,用于处理网络中断时的数据同步。
部分复制的实现,依赖于三个重要的概念:服务器运行ID,复制积压缓存区,复制偏移量。

服务器运行ID(runid)

简介

概念:每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成。
作用:运行id被用于在服务器间进行传输,识别身份。

详解

主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来,当断线重连时,从节点会将这个runid发送给主节点;
主节点根据runid判断能否进行部分复制:

  • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);
  • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

复制积压缓存区

简介

概念:复制积压缓冲区,又名复制缓冲区,是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;
作用:当主节点开始有从节点时创建,是备份主节点最近发送给从节点的数据。(用于存储服务器执行过的命令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区)
组成:偏移量 + 字节值

注意:无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区。

详解

复制积压缓存区详解1
复制积压缓存区详解2

在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且是先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

由于该缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size);例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。

从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:

  • 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
  • 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。

复制偏移量(offset)

简介

概念:主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;
作用:主节点每次向从节点传播N个字节数据时,主节点的offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。

详解

offset用于判断主从节点的数据库状态是否一致:
如果二者offset相同,则一致;如果offset不同,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据。例如,如果主节点的offset是1000,而从节点的offset是500,那么部分复制就需要将offset为501-1000的数据传递给从节点。

部分复制流程

在了解了节点运行id、复制积压缓冲区、复制偏移量之后,总结一下部分复制的流程:

  1. 主节点维护了一个复制缓冲区和节点id(runid),主从节点都维护了一个复制偏移量(offset)
  2. 从节点发送命令psync2 runid offset
  3. 主节点判断runid是否匹配,判断offset是否存在复制缓冲区中;如果runid或offset有一个不满足,则执行全量复制
  4. 主节点判断runid或offset校验通过,返回 +CONTINUE offset,进行部分复制,通过stocket发送复制缓冲区[从节点中复制偏移量]到[主节点复制偏移量]的数据
  5. 从节点接收到+CONTINUE,保存master的复制偏移量,并执行命令,完成同步

psync命令执行详解

在了解了节点运行id、复制积压缓冲区、复制偏移量之后,本节将介绍psync命令的参数和返回值,从而说明psync命令执行过程中,主从节点是如何确定使用全量复制还是部分复制的。

全量复制+部分复制
psync命令执行详解

  1. 首先,从节点根据当前状态,决定如何调用psync命令
    • 如果从节点之前未执行过slaveof,或最近执行了slaveof no one,则从节点发送命令为psync ? -1,向主节点请求全量复制;
    • 如果从节点之前执行了slaveof,则发送命令为psync <runid> <offset>,其中runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量。
  2. 主节点根据收到的psync命令,及当前服务器状态,决定执行全量复制还是部分复制
    • 如果主节点版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送sync命令执行全量复制;
    • 如果主节点版本够新,且runid与从节点发送的runid相同,且从节点发送的offset之后的数据在复制积压缓冲区中都存在,则回复+CONTINUE,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可;
    • 如果主节点版本够新,但是runid与从节点发送的runid不同,或从节点发送的offset之后的数据已不在复制积压缓冲区中(在队列中被挤出了),则回复+FULLRESYNC ,表示要进行全量复制,其中runid表示主节点当前的runid,offset表示主节点当前的offset,从节点保存这两个值,以备使用。

心跳机制(命令传播阶段)

简介

在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用。

PING(主->从)

每隔指定的时间,主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断。。

master心跳:

  • 命令:每隔指定的时间,主节点会向从节点发送PING命令
  • 周期:PING发送的频率由repl-ping-slave-period参数控制,单位是秒,默认值是10s。
  • 作用:判断从节点是否在线

REPLCONF ACK(从->主)

slave心跳:
指令:在命令传播阶段,从节点会向主节点发送REPLCONF ACK命令,命令格式为REPLCONF ACK {offset},其中offset指从节点保存的复制偏移量
周期:频率是每秒1次
作用:

  1. 实时监测主从节点网络状态:该命令会被主节点用于复制超时的判断。此外,在主节点中使用info Replication,可以看到其从节点的状态中的lag值,代表的是主节点上次收到该REPLCONF ACK命令的时间间隔,在正常情况下,该值应该是0或1
  2. 检测命令丢失:从节点发送了自身的offset,主节点会与自己的offset对比,如果从节点数据缺失(如网络丢包),主节点会推送缺失的数据(这里也会利用复制积压缓冲区)。注意,offset和复制积压缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情形;区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的。
  3. 辅助保证从节点的数量和延迟

注意事项

  • 当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作
    • min-slaves-to-write 2
    • min-slaves-max-lag 8
    • slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
  • slave数量由slave发送REPLCONF ACK命令做确认
  • slave延迟由slave发送REPLCONF ACK命令做确认

Redis主从复制常见问题

https://www.cnblogs.com/kismetv/p/9236731.html

参考

黑马Redis视频
https://www.cnblogs.com/kismetv/p/9236731.html

MQ

MQ的应用场景

异步通信,系统解耦,流量削峰

  • 异步通信:MQ提供了异步处理机制。可以减少主流程的响应时间,列如:异步发送短信。
  • 系统解耦:降低系统之间的耦合度。列如:在数据中心中,让各个系统自己去监听需要的消息数据,达到系统解耦的目的。
  • 流量削峰:削弱瞬时的请求高峰,让系统吞吐量在高峰请求下保持可控。
阅读全文 »