白癜风能治好了吗 http://m.39.net/baidianfeng/qzzt/bdfnzhm/来源
Java之道自年双11以来,在每年双11超大规模流量的冲击上,蚂蚁金服都会不断突破现有技术的极限。年双11的支付峰值为2万笔/分钟,到年双11时这个数字变为了25.6万笔/秒。年双11的支付峰值为8万笔/秒,年双11支付峰值为5.万笔/秒,创下新纪录,是年第一次双11的倍。在如此之大的支付TPS背后除了削峰等锦上添花的应用级优化,最解渴最实质的招数当数基于分库分表的单元化了,蚂蚁技术称之为LDC(逻辑数据中心)。本文不打算讨论具体到代码级的分析,而是尝试用最简单的描述来说明其中最大快人心的原理。我想关心分布式系统设计的人都曾被下面这些问题所困扰过:
支付宝海量支付背后最解渴的设计是啥?换句话说,实现支付宝高TPS的最关键的设计是啥?
LDC是啥?LDC怎么实现异地多活和异地灾备的?
CAP魔咒到底是啥?P到底怎么理解?
什么是脑裂?跟CAP又是啥关系?
什么是PAXOS,它解决了啥问题?
PAXOS和CAP啥关系?PAXOS可以逃脱CAP魔咒么?
Oceanbase能逃脱CAP魔咒么?
如果你对这些感兴趣,不妨看一场赤裸裸的论述,拒绝使用晦涩难懂的词汇,直面最本质的逻辑。LDC和单元化
LDC(logicdatacenter)是相对于传统的(InternetDataCenter-IDC)提出的,逻辑数据中心所表达的中心思想是无论物理结构如何的分布,整个数据中心在逻辑上是协同和统一的。这句话暗含的是强大的体系设计,分布式系统的挑战就在于整体协同工作(可用性,分区容忍性)和统一(一致性)。单元化是大型互联网系统的必然选择趋势,举个最最通俗的例子来说明单元化。我们总是说TPS很难提升,确实任何一家互联网公司(比如淘宝、携程、新浪)它的交易TPS顶多以十万计量(平均水平),很难往上串了。因为数据库存储层瓶颈的存在再多水平扩展的服务器都无法绕开,而从整个互联网的视角看,全世界电商的交易TPS可以轻松上亿。这个例子带给我们一些思考:为啥几家互联网公司的TPS之和可以那么大,服务的用户数规模也极为吓人,而单个互联网公司的TPS却很难提升?究其本质,每家互联网公司都是一个独立的大型单元,他们各自服务自己的用户互不干扰这就是单元化的基本特性,任何一家互联网公司,其想要成倍的扩大自己系统的服务能力,都必然会走向单元化之路。它的本质是分治,我们把广大的用户分为若干部分,同时把系统复制多份,每一份都独立部署,每一份系统都服务特定的一群用户。以淘宝举例,这样之后,就会有很多个淘宝系统分别为不同的用户服务,每个淘宝系统都做到十万TPS的话,N个这样的系统就可以轻松做到N*十万的TPS了。LDC实现的关键就在于单元化系统架构设计,所以在蚂蚁内部,LDC和单元化是不分家的,这也是很多同学比较困扰的地方,看似没啥关系,实则是单元化体系设计成就了LDC。小结:分库分表解决的最大痛点是数据库单点瓶颈,这个瓶颈的产生是由现代二进制数据存储体系决定的(即I/O速度)。单元化只是分库分表后系统部署的一种方式,这种部署模式在灾备方面也发挥了极大的优势。系统架构演化史
几乎任何规模的互联网公司,都有自己的系统架构迭代和更新,大致的演化路径都大同小异。最早一般为了业务快速上线,所有功能都会放到一个应用里,系统架构如下图所示:这样的架构显然是有问题的,单机有着明显的单点效应,单机的容量和性能都是很局限的,而使用中小型机会带来大量的浪费。随着业务发展,这个矛盾逐渐转变为主要矛盾,因此工程师们采用了以下架构:这是整个公司第一次触碰到分布式,也就是对某个应用进行了水平扩容,它将多个微机的计算能力团结了起来,可以完胜同等价格的中小型机器。慢慢的,大家发现,应用服务器CPU都很正常了,但是还是有很多慢请求,究其原因,是因为单点数据库带来了性能瓶颈。于是程序员们决定使用主从结构的数据库集群,如下图所示:其中大部分读操作可以直接访问从库,从而减轻主库的压力。然而这种方式还是无法解决写瓶颈,写依旧需要主库来处理,当业务量量级再次增高时,写已经变成刻不容缓的待处理瓶这时候,分库分表方案出现了:分库分表不仅可以对相同的库进行拆分,还可以对相同的表进行拆分,对表进行拆分的方式叫做水平拆分。不同功能的表放到不同的库里,一般对应的是垂直拆分(按照业务功能进行拆分),此时一般还对应了微服务化。这种方法做到极致基本能支撑TPS在万级甚至更高的访问量了。然而随着相同应用扩展的越多,每个数据库的链接数也巨量增长,这让数据库本身的资源成为了瓶颈。这个问题产生的本质是全量数据无差别的分享了所有的应用资源,比如A用户的请求在负载均衡的分配下可能分配到任意一个应用服务器上,因而所有应用全部都要链接A用户所在的分库,数据库连接数就变成笛卡尔乘积了。在本质点说,这种模式的资源隔离性还不够彻底。要解决这个问题,就需要把识别用户分库的逻辑往上层移动,从数据库层移动到路由网关层。这样一来,从应用服务器a进来的来自A客户的所有请求必然落库到DB-A,因此a也不用链接其他的数据库实例了,这样一个单元化的雏形就诞生了。思考一下,应用间其实也存在交互(比如A转账给B),也就意味着,应用不需要链接其他的数据库了,但是还需要链接其他应用。如果是常见的RPC框架如Dubbo等,使用的是TCP/IP协议,那么等同于把之前与数据库建立的链接,换成与其他应用之间的链接了。为啥这样就消除瓶颈了呢?首先由于合理的设计,应用间的数据交互并不巨量,其次应用间的交互可以共享TCP链接,比如A-B之间的Socket链接可以被A中的多个线程复用。而一般的数据库如MySQL则不行,所以MySQL才需要数据库链接池。如上图所示,但我们把整套系统打包为单元化时,每一类的数据从进单元开始就注定在这个单元被消化,由于这种彻底的隔离性,整个单元可以轻松的部署到任意机房而依然能保证逻辑上的统一。下图为一个三地五机房的部署方式:蚂蚁单元化架构实践蚂蚁支付宝应该是国内最大的支付工具,其在双11等活动日当日的支付TPS可达几十万级,未来这个数字可能会更大,这决定了蚂蚁单元化架构从容量要求上看必然从单机房走向多机房。另一方面,异地灾备也决定了这些IDC机房必须是异地部署的。整体上支付宝也采用了三地五中心(IDC机房)来保障系统的可用性。跟上文中描述的有所不同的是,支付宝将单元分成了三类(也称CRG架构):RZone(RegionZone):直译可能有点反而不好理解。实际上就是所有可以分库分表的业务系统整体部署的最小单元。每个RZone连上数据库就可以撑起一片天空,把业务跑的溜溜的。
GZone(GlobalZone):全局单元,意味着全局只有一份。部署了不可拆分的数据和服务,比如系统配置等。
实际情况下,GZone异地也会部署,不过仅是用于灾备,同一时刻,只有一地GZone进行全局服务。GZone一般被RZone依赖,提供的大部分是读取服务。
CZone(CityZone):顾名思义,这是以城市为单位部署的单元。同样部署了不可拆分的数据和服务,比如用户账号服务,客户信息服务等。理论上CZone会被RZone以比访问GZone高很多的频率进行访问。
CZone是基于特定的GZone场景进行优化的一种单元,它把GZone中有些有着”写读时间差现象”的数据和服务进行了的单独部署,这样RZone只需要访问本地的CZone即可,而不是访问异地的GZone。
“写读时间差现象”是蚂蚁架构师们根据实践统计总结的,他们发现大部分情况下,一个数据被写入后,都会过足够长的时间后才会被访问。生活中这种例子很常见,我们办完银行卡后可能很久才会存第一笔钱;我们创建微博账号后,可能想半天才会发微博;我们下载创建淘宝账号后,可能得浏览好几分钟才会下单买东西。当然了这些例子中的时间差远远超过了系统同步时间。一般来说异地的延时在ms以内,所以只要满足某地CZone写入数据后ms以后才用这个数据,这样的数据和服务就适合放到CZone中。相信大家看到这都会问:为啥分这三种单元?其实其背后对应的是不同性质的数据,而服务不过是对数据的操作集。下面我们来根据数据性质的不同来解释支付宝的CRG架构。当下几乎所有互联网公司的分库分表规则都是根据用户ID来制定的。而围绕用户来看整个系统的数据可以分为以下两类:用户流水型数据:典型的有用户的订单、用户发的评论、用户的行为记录等。这些数据都是用户行为产生的流水型数据,具备天然的用户隔离性,比如A用户的App上绝对看不到B用户的订单列表。所以此类数据非常适合分库分表后独立部署服务。用户间共享型数据:这种类型的数据又分两类。一类共享型数据是像账号、个人博客等可能会被所有用户请求访问的用户数据。比如A向B转账,A给B发消息,这时候需要确认B账号是否存在;又比如A想看B的个人博客之类的。另外一类是用户无关型数据,像商品、系统配置(汇率、优惠政策)、财务统计等这些非用户纬度的数据,很难说跟具体的某一类用户挂钩,可能涉及到所有用户。比如商品,假设按商品所在地来存放商品数据(这需要双维度分库分表),那么上海的用户仍然需要访问杭州的商品。这就又构成跨地跨Zone访问了,还是达不到单元化的理想状态,而且双维度分库分表会给整个LDC运维带来复杂度提升。注:网上和支付宝内部有另外一些分法,比如流水型和状态性,有时候还会分为三类:流水型、状态型和配置型。个人觉得这些分法虽然尝试去更高层次的抽象数据分类,但实际上边界很模糊,适得其反。直观的类比,我们可以很轻易的将上述两类数据对应的服务划分为RZone和GZone,RZone包含的就是分库分表后负责固定客户群体的服务,GZone则包含了用户间共享的公共数据对应的服务。到这里为止,一切都很完美,这也是主流的单元化话题了。对比支付宝的CRG架构,我们一眼就发现少了C(CityZone),CZone确实是蚂蚁在单元化实践领域的一个创新点。再来分析下GZone,GZone之所以只能单地部署,是因为其数据要求被所有用户共享,无法分库分表,而多地部署会带来由异地延时引起的不一致。比如实时风控系统,如果多地部署,某个RZone直接读取本地的话,很容易读取到旧的风控状态,这是很危险的。这时蚂蚁架构师们问了自己一个问题——难道所有数据受不了延时么?这个问题像是打开了新世界的大门,通过对RZone已有业务的分析,架构师们发现80%甚至更高的场景下,数据更新后都不要求立马被读取到。也就是上文提到的”写读时间差现象”,那么这就好办了,对于这类数据,我们允许每个地区的RZone服务直接访问本地,为了给这些RZone提供这些数据的本地访问能力,蚂蚁架构师设计出了CZone。在CZone的场景下,写请求一般从GZone写入公共数据所在库,然后同步到整个OB集群,然后由CZone提供读取服务。比如支付宝的会员服务就是如此。即便架构师们设计了完美的CRG,但即便在蚂蚁的实际应用中,各个系统仍然存在不合理的CRG分类,尤其是CG不分的现象很常见。支付宝单元化的异地多活和灾备流量挑拨技术探秘简介
单元化后,异地多活只是多地部署而已。比如上海的两个单元为ID范围为[00~19],[0~59]的用户服务。而杭州的两个单元为ID为[20~39]和[60,79]的用户服务,这样上海和杭州就是异地双活的。支付宝对单元化的基本要求是每个单元都具备服务所有用户的能力,即——具体的那个单元服务哪些用户是可以动态配置的。所以异地双活的这些单元还充当了彼此的备份。发现工作中冷备热备已经被用的很乱了。最早冷备是指数据库在备份数据时需要关闭后进行备份(也叫离线备份),防止数据备份过程中又修改了,不需要关闭即在运行过程中进行数据备份的方式叫做热备(也叫在线备份)。也不知道从哪一天开始,冷备在主备系统里代表了这台备用机器是关闭状态的,只有主服务器挂了之后,备服务器才会被启动。而相同的热备变成了备服务器也是启动的,只是没有流量而已,一旦主服务器挂了之后,流量自动打到备服务器上。本文不打算用第二种理解,因为感觉有点野。为了做到每个单元访问哪些用户变成可配置,支付宝要求单元化管理系统具备流量到单元的可配置以及单元到DB的可配置能力。如下图所示:其中Spanner是蚂蚁基于Nginx自研的反向代理网关,也很好理解,有些请求我们希望在反向代理层就被转发至其他IDC的Spanner而无需进入后端服务,如图箭头2所示。那么对于应该在本IDC处理的请求,就直接映射到对应的RZ即可,如图箭头1。进入后端服务后,理论上如果请求只是读取用户流水型数据,那么一般不会再进行路由了。然而,对于有些场景来说,A用户的一个请求可能关联了对B用户数据的访问,比如A转账给B,A扣完钱后要调用账务系统去增加B的余额。这时候就涉及到再次的路由,同样有两个结果:跳转到其他IDC(如图箭头3)或是跳转到本IDC的其他RZone(如图箭头)。RZone到DB数据分区的访问这是事先配置好的,上图中RZ和DB数据分区的关系为:RZ0*--aRZ1*--bRZ2*--cRZ3*--d下面我们举个例子来说明整个流量挑拨的过程,假设C用户所属的数据分区是c,而C用户在杭州访问了cashier.alipay.