海量存储系列之六

简介:

上次我们讲到,单机事务个我们面临的问题,下面我们来说一些我所知的解决的方法。

在我开始做淘宝数据层的时候,被问得最多的无非也就是:如何做事务,如何做join.至今仍然如此,我一般都会简单而明确的跟对方说:没有高效的实现方法。

虽然没有高效的实现,但实现还是有的。作为引子,我们先来介绍一下这种实现的方式。

我们仍然以上一次讲到的bob和smith为例子来说明好了。

开始的时候。Bob要给smith100块,那么实际上事务中要做的事情是

事务开始时查询bob有多少钱。如果有足够多的钱让bob的账户 -100 ,然后给smith 的账户+100 。最后事务结束。

如果这个事情在单机,那么事情可以使用锁的方式加以解决。

但如果bob在一台机器,smith在另外一台机器,我们应该怎么做呢?

第一种最常被人想起的方法,就是两段提交协议。

两段提交协议从原理上来说是非常简单的一套协议。

Prepare(bob-100) at 机器A->prepare (smith+100) at 机器b ->commit(bob) ->commit(smith)

事务结束。

两段提交的核心,是在prepare的阶段,会对所有该操作所影响的数据加锁,这样就可以阻止其他人(或机器)对他的访问。题外话,问个问题: )如果这时有其他节点,用相反的方向,进行更新,也就是先更新smith,然后更新bob.会有可能发生什么事情呢?

两段提交协议是被我们在大部分场景下放弃的一个模型,原因主要是因为

  1. Tm本身需要记录事务进行的过程,log要保证安全和可信,性能非常低。

  2. 锁的利用率和并行性较低。

  3. 网络开销较大

  4. 可见性要求实际上就等于让快的操作等慢的。

所以从性能角度来说,这类需求不多也不常见。

既然这样的模型不行,有没有其他模型可以使用呢?

有的。

在事务的过程中,细心的读者不难发现,实际上事务中并不需要这么强的一致可见性。

Bob是需要强一致的,因为他的操作仰赖于他有多少钱,如果他的钱不够100,那么是不能让他的账户变为负数的。但smith却不需要,smith不需要判断他的账户有多少钱,只需要把钱加到他的账户里,不少给他,到账时间尽可能短就可以。

Smith不需要chech账户的钱数,这个前提非常重要,这也是我们能使用最终一致性的关键因素。

下面,我们来看一下另外的选择吧。

Bob的账号在机器A上,smith的账号在机器b上。

首先,我们在机器A上做以下操作:

  1. 本地事务开始

  2. 读取bob的账户

  3. 判断是否有充足余额

  4. 更新bob的账号,将bob的钱减少100

  5. 将需要给smith加100块这个操作,以事务的形式插入到同机(A)的一张log表中,并自动生成一个唯一的transactionID。

  6. 事务关闭

然后,异步的发送一个通知,给一个消费者。

消费者接到通知后,从bob的机器上读取到需要给smith+100这个操作,以及该操作所对应的transactionID。

然后,按照如下方法进行运作

  1. 查看在去重表内是否有对应的transactionID.如果没有,则

  2. 开启本地事务

  3. 将smith的账户+100

  4. 将transactionID 插入去重表

  5. 事务结束

这样,我们也可以完成一个交易的核心流程了。在交易类过程中的大量事务操作,都是以这样的方式完成的。

下面,我们针对上面的这个流程的一些抉择的点进行一些探讨。

首先,是bob这个机器,这里涉及第一个抉择点。

如果bob是个消费大户,短时间内进行了大量购买,那么可能会造成的问题是,bob所在的那个机器会成为热点,如果在某个突发的情况下,某个账户突然成为热点,那么这些有状态的数据很难快速的反应并加以处理,会造成事务数在某个单节点大量堆积。造成挂掉。

可能的解决方法是:

  1. 利用两段提交协议来让原来的” 将需要给smith加100块这个操作,以事务的形式插入到同机(A)的一张log表中,并自动生成一个唯一的transactionID”这步操作放在另外的一台机器上进行。

这样做的的好处是,无论bob怎么是热点,都可以通过水平的加log机器的方式来防止这种热点的产生。

坏处则有:

1方案复杂度高

2额外的网络开销

3消息基于网络发送后,会可能得到三个可能的反馈:1. 成功 2. 失败 3. 无反馈。最麻烦的就是这个无反馈,他可能成功,也可能失败。所以是不确定的状态,需要进行事务的两边进行第二次确认,来确保这个事务的参与方是否都做了该做的事情,如果有一方做了类似commit的操作,那么另外的一方应该commit.如果两方都没做commit操作,那么应该回滚。

  1. 让bob的库余量更高,并按照访问压力进行数据的切分,按照热度进行数据划分,放弃原有的简单取mod的策略。来兼容这种不均匀特性。

其次,如果有80个系统都关注着smith加了100这个操作的log,要做对应的处理(比如一些人要针对这个加钱操作做个打款短信推送,有些要做个数据分析等等),那么这里就有另外一个问题,这些系统对bob所在的库的读取就会让该机器成为悲剧的存在。

所以,可以考虑的方式是,增加一个队列,使用,推,拉,或推拉结合的方式将smith加100这个操作加以分发。这样就可以减轻主机的压力。

坏处则是:

1方案进一步复杂

2如何保证log到数据分发服务器之间的数据同步是安全的和准确的?

3如何保证分发服务器的可靠和冗余?

4如何保证写入分发服务器的数据的安全和可靠?

再次,smith这边也有问题,为什么要使用一张去重表呢?其实是因为,在发送端,也就是队列将数据发送到目标机器后,也可能从目标机获取到三种不同的反馈,一类是成功(这个占了大多数)。一类是失败。还有一类是。。。没反馈。

当然,最麻烦的还是这个没反馈的情况,没人知道这时候到底对方是做成功了呢?还是没做成功,为了保证最大的吞吐量,又不能其他人都不做事儿了,就等对方的反馈。所以这里就有另外的权衡了。

一般的模型有两类,一类是用分布式事务来完成。

一类是使用努力送达的模型,说叫努力送达,顾名思义,就是只有得到成功的反馈,才停止投递,而其他时候则重复投递消息,直到对方反馈成功为止。

两种模型比较,显然应该追求速度而放弃方便性,于是我们主要来说说这个努力送达以后所带来的影响。

影响一 : 会有重复的投递,也就是说,这个消息可能会投多次,这对于update set version=version+1 这类的操作来说,是个比较毁灭性的打击。

影响二:如果需要重复投递的消息过多,会导致log分发的机器消耗大量资源来进行重复投递。这会影响server的稳定性

影响三:如果大量堆积消息,那么会造成消息的严重delay。smith发现自己在1个月后收到了bob的钱,你说他会不会去K咱一顿: ) .

最后,额外记的这两次log其实在某些场景下也是可以省去的。

以上,就是我在尝试还原淘宝的消息和事务系统时所能大概想到的一些非常需要权衡和注意的问题点。

小小总结一下,整个问题的核心其实是幂等,说白了就是要能够理解数据基于网络的同步过程中,无反馈是一个经常发生的现象,在这种现象中,重复投递比傻傻等待要有效率的多。所以,重复作为一个side affect也就被默认的存在于系统中,所有的工程师都需要认识到这个问题的客观存在,并采取方法去解决之。

在基于网络的数据同步过程中,一致性是第一个被放弃的。然后数据和消息不会出现重复,是第二个被放弃指标。

使用这种模型,我们可以放弃原来快得等慢的的模式,让整体的吞吐量和性能不会受制于锁的限制,所以淘宝和支付宝才能够支持如此大的交易量。完成大量交易订单。

本文来源于"阿里中间件团队播客",原文发表时间" 2011-12-19 "

相关文章
|
7月前
|
机器学习/深度学习 人工智能 算法
普通人怎么学人工智能?这些隐藏学习秘籍大揭秘,生成式人工智能认证(GAI认证)来助力
在人工智能(AI)快速发展的今天,普通人学习AI已成为必然趋势。本文从明确学习目标与路径、利用多元化资源、注重实践应用、关注GAI认证及持续自我提升五个方面,为普通人提供系统化的AI学习指南。通过设定目标、学习编程语言、参与项目实践和获取专业认证,普通人可逐步掌握AI技能,在未来职场中占据优势并开启智能时代新篇章。
|
前端开发 JavaScript 测试技术
前端测试技术中,如何提高集成测试的效率?
前端测试技术中,如何提高集成测试的效率?
|
存储 网络安全 开发工具
Git 中文参考(一)(7)
Git 中文参考(一)
191 2
|
Linux C语言 开发者
音视频-SDL的简单使用
音视频-SDL的简单使用
173 0
|
调度 开发者
priority task1课程|学习笔记
快速学习 priority task1课程
168 0
priority task1课程|学习笔记
|
存储 JavaScript API
每日一题:你是怎么理解ES6新增Set、Map两种数据结构的?
每日一题:你是怎么理解ES6新增Set、Map两种数据结构的?
204 0
|
监控 druid 关系型数据库
第六篇:SpringBoot 2.x添加Druid作为数据库连接池
整合了一大堆ORM,是时候增加一个连接池了,此处选用了druid作为连接池,druid是alibaba开源平台上的一个数据库连接池实现,对比c3p0,dbcp加入了对数据库的监控,不知道甩出几条街的距离,个人推为数据库连接池的首选(手动摊手) 这里仍然...
4310 0
|
弹性计算 Java 应用服务中间件
ECS使用体验
我是一本在读的软件工程学生,我在暑假的时候,通过网络渠道学习java项目的编程,在项目的后面阶段,需要将项目防止到云服务器上,在老师的介绍下,我使用了阿里云服务器,因为学生资金比较紧缺,我选择参加了飞天加速计划·高校学生在家实践”活动。 因为是第一次使用阿里云服务器ECS,在这过程中遇到了很多的困难,比如对Linux语句的不熟悉。在完成服务器的选择后,第一步就是配置自己项目的运行环境,我使用的是Xftp 7来实现图形界面上传文件。 通过这次的云服务器ECS的使用,我收获颇丰,第一次将项目放到了服务器上进行访问,在过程中,遇到的困难只要是tomcat老是异常报错,通过网络找各种解决方法。删除重新