目录
吞吐(Throughput)vs 并发(Concurrency)
概述
在技术学习的过程中,许多人都缺乏实际中大型系统的经验,这导致他们难以全面理解一些概念。因此,本文以一个电子商务应用为例,介绍了从百个并发到千万级并发情况下服务端架构的演进过程。同时,列举了每个演进阶段可能遇到的相关技术,旨在让大家对架构的演进有一个整体的认知,为后续的深入学习提供一个全局视野。
电子商务应用是现代互联网中常见的一种应用场景,其服务端架构的演进过程可以很好地展现出系统的成长轨迹和技术发展路径。通过从少量并发到高并发的不同阶段,大家可以逐步了解服务端架构在面对不同规模和需求时的优化和改进策略,以及所涉及到的各种技术选型和解决方案。
通过本文的介绍,相信大家可以更清晰地了解到如何根据业务规模和需求来设计和优化服务端架构,以及如何选择合适的技术和工具来支撑系统的发展。这将有助于我们在后续的学习和实践中更加深入地理解和应用相关的技术和概念,从而更好地应对实际的开发和运维工作。
常见概念
在正式引入架构演进之前,为了避免大家对架构中的概念完全不了解而导致低效沟通,我们首先对其中一些比较重要的概念进行前置介绍:
基本概念
应用(Application)/ 系统(System):
应用是指为了完成一整套服务而编写的一个程序或者一组相互配合的程序群。在软件开发中,一个应用通常由多个模块或组件组成,它们共同协作来实现特定的功能或提供特定的服务。应用的设计和开发旨在满足用户的需求,并通过提供用户界面或API接口与用户进行交互。
举个生活例子,可以将一个应用比作为了完成一个任务而组成的一个团队。这个团队中的每个成员都有自己的职责和任务,彼此之间相互协作,共同努力完成整个任务。类似地,一个应用中的每个模块或组件也都有自己的功能和职责,它们通过相互调用和协作,共同实现了应用所需的功能和服务。
在软件系统中,应用可以是一个独立的软件产品,也可以是一个较大系统中的一个子系统。而系统则是指由一个或多个应用组成的整体,它们共同构成了一个完整的功能集合,为用户提供了更为丰富和复杂的服务。因此,应用和系统是软件开发中非常基础且重要的概念,对于理解和设计软件架构都具有重要意义。
模块(Module)/ 组件(Component):
在软件开发中,当应用变得较为复杂时,为了更好地组织和管理代码,通常会将具有清晰职责、内聚性强的部分抽象出来,形成模块或组件。模块或组件是软件系统中的独立单元,它们负责实现特定的功能或提供特定的服务,通过封装相关的逻辑和数据,使得系统更易于理解、维护和扩展。
举个生活例子,可以将模块或组件比作军队中的各个特定任务组成的小组。例如,在军队中为了完成某个据点的攻克任务,可能会组建突击小组、爆破小组、掩护小组、通信小组等,每个小组都有明确的任务和职责,彼此之间相互配合,共同实现了攻克据点的目标。类似地,模块或组件在软件系统中也具有类似的功能和作用,它们分工明确、职责清晰,通过相互调用和协作,共同构成了一个完整的软件系统。
模块或组件的设计和实现应该遵循高内聚、低耦合的原则,即模块内部的功能关联性强,模块之间的依赖关系尽量减少,这样可以提高系统的可维护性、灵活性和复用性。因此,模块或组件在软件开发中扮演着重要的角色,是构建复杂软件系统的基础。
分布式(Distributed):
在计算机领域,分布式指的是系统中的多个模块或组件被部署在不同的服务器或计算节点上,彼此通过网络进行通信和协作,共同完成系统的功能。这样的系统被称为分布式系统。分布式系统的设计旨在提高系统的性能、可用性和可扩展性,并能够更好地应对大规模数据和高并发访问的需求。
举个生活例子,可以将分布式系统比作一个工作小组,该小组的成员分散在不同的城市的不同工作场地中,通过远程协作完成共同的目标。每个成员都承担着特定的任务和责任,通过协调和通信,使整个工作小组能够高效地完成任务。类似地,分布式系统中的每个节点都承担着特定的功能和责任,通过相互通信和协作,共同实现系统的功能和服务。
在分布式系统中,跨主机之间的模块通信通常需要借助网络进行支持。为了确保通信的可靠性和效率,需要使用一些通信协议和技术,如TCP/IP协议、HTTP协议、RPC框架等。同时,分布式系统的设计和实现也需要考虑一些分布式计算的基本原理和问题,如一致性、容错性、负载均衡等,以确保系统能够稳定、高效地运行。
总之,分布式系统是现代计算机系统中的重要组成部分,广泛应用于各种大型软件系统和互联网服务中,为用户提供了高性能、高可用性的服务。
集群(Cluster):
集群是指部署在多台服务器上的、为了实现特定目标的一个或一组特定的组件,整体被称为集群。在软件系统中,集群通常由多个相同或相似的节点组成,它们共同协作,提供某种特定的服务或功能。
举个生活例子,可以将集群比作为了为了解决军队攻克防守坚固的大城市的作战目标而形成的一个炮兵打击集群。在这个集群中,大批炮兵部队被集中起来,共同实施炮击,以达到摧毁敌方防御的目标。
在软件领域,集群可以用于提高系统的可靠性、可扩展性和性能。例如,多个MySQL数据库分布在不同的服务器上,共同提供数据库服务目标,这就构成了一个数据库集群。集群中的每个节点都具有相同的数据库服务功能,客户端可以通过负载均衡等机制将请求分发到不同的节点上,从而提高系统的并发处理能力和容错性。
需要注意的是,分布式与集群在概念上有些许差别。通常来说,分布式更侧重于物理形态,即工作在不同服务器上并通过网络通信配合完成任务;而集群更关注逻辑形态,即是否为了完成特定服务目标。尽管如此,实际应用中两者的区别并不是十分严格,有时候可能会有交叉的情况。
主(Master)/ 从(Slave):
在集群中,通常会有一个程序或节点需要承担更多的责任,被称为主(Master);而其他承担辅助或附属职责的程序或节点被称为从(Slave)。这种主从关系通常用于分布式系统中的数据复制、任务调度等场景。
举个例子,以MySQL集群为例,在一个MySQL集群中,可能会有多个MySQL服务器节点。其中,只有其中一台服务器上的数据库允许进行数据的写入操作(增、删、改),而其他数据库节点的数据修改都需要从这台主节点同步过来。这台允许写入操作的数据库服务器被称为主库(Master),而其他数据库服务器则被称为从库(Slave)。
主从架构通常用于实现数据的冗余备份和高可用性。主库负责处理写入操作,而从库则负责同步主库的数据,并在主库发生故障时提供备用数据服务。这样可以提高系统的可靠性和容错性。
需要注意的是,主从关系并不仅限于数据库领域,在其他分布式系统中也经常会遇到类似的主从架构,用于实现数据复制、任务分发等功能。
中间件(Middleware):
中间件是一类软件,它提供了不同应用程序之间相互通信的桥梁,处于不同技术、工具和数据库之间,起到了连接、协调和转换数据的作用。中间件通常位于应用程序和底层系统之间,充当了信息传递和处理的中介。
举个生活例子,可以将中间件比作为一家饭店中的采购部门。刚开始时,饭店可能会每天去市场挑选购买菜品,但随着饭店业务量的增加,为了更高效地采购,饭店成立了一个专门的采购部门。这个采购部门负责与菜市场进行沟通、采购和协调,将市场上的菜品采购回来,然后提供给厨房使用。在这个过程中,采购部门就像是厨房和菜市场之间的桥梁,起到了连接和协调的作用。
类似地,中间件在软件系统中起到了类似的作用。它可以是消息队列、缓存服务器、API网关、分布式事务处理器等,帮助不同的应用程序之间实现数据的传输、通信和协作,提高了系统的灵活性、可扩展性和可靠性。中间件能够隐藏底层系统的复杂性,为应用程序提供统一的接口和服务,简化了开发和维护的工作。
评价指标(Metric)
可用性(Availability)
可用性是指系统在单位时间段内能够正常提供服务的概率或期望。通常用一个时间段内系统正常运行时间与总时长的比例来表示。例如,年化系统可用性可以通过系统正常提供服务的时间长度除以一年总时长来计算。这个指标暗含着一个重要问题,即如何评估系统提供服务是否正常,这是一个深入的话题,超出了当前讨论的范围。
在日常讨论中,我们通常使用“几个9”的表示法来描述系统的可用性。例如,“4个9”表示系统可以提供99.99%的可用性,“5个9”表示99.999%的可用性,依此类推。这种表示方法简明地描述了系统所追求的高可用性目标。
“高可用性(HA)”是一个常用的术语,用来表达系统追求的非常重要的目标。通过确保系统在面对故障、错误或其他意外情况时仍然能够保持正常运行,高可用性可以保证服务的连续性和稳定性,从而提升用户体验和系统的可靠性。
响应时长(Response Time,RT)
响应时长指的是用户完成输入到系统给出用户反馈的时间长度。例如,在点外卖的业务中,响应时长可以定义为拿到外卖的时刻与完成点单的时刻之间的时间间隔。通常,我们需要衡量的是最长响应时长、平均响应时长和中位数响应时长。这个指标原则上越小越好,因为它直接影响着用户的体验和系统的性能。
然而,在实际情况中,响应时长受到多种因素的影响,包括系统设计、网络延迟、服务器负载、系统架构等。有时候,由于实现的限制或者系统的特性,响应时长可能会受到一定的限制,需要根据具体情况来进行评估和判断。通常,优化系统架构、提高服务器性能、优化网络通信等手段可以有效地降低响应时长,从而提升系统的性能和用户体验。
吞吐(Throughput)vs 并发(Concurrency)
吞吐(Throughput)是指在单位时间内系统成功处理的请求的数量。它衡量了系统在给定时间段内的处理能力和效率。例如,一条单车道高速公路,如果一分钟可以通过20辆车,那么吞吐量就是20辆车每分钟。
并发(Concurrency)指的是系统在同一时刻所能支持的请求的最高数量。它衡量了系统同时处理多少个请求的能力。使用同样的高速公路的例子,如果在同一时刻有两辆车同时行驶在道路上,那么并发量就是2。
在实践中,并发量往往很难直接获取,因此通常会使用极短时间段(比如1秒)内的吞吐量来代替。例如,在一秒钟内成功处理了100个请求,那么可以认为并发量大致是100。
我们通常使用“高并发(High Concurrency)”这个非量化目标来简要表达系统追求处理大量并发请求的能力。高并发系统能够有效地处理大量的请求,提高系统的性能和响应能力,从而提升用户体验。
架构演进
单机架构
单机架构指的是整个应用系统部署在单台服务器上的架构方式。在初期阶段,我们需要利用我们精干的技术团队,快速将业务系统投入市场进行检验,并且可以迅速响应变化要求。
选择单机架构是合适的,因为它具有以下优势:
-
快速上线: 利用技术团队的专业知识,可以快速将业务系统投入市场进行验证和测试。单机架构可以快速部署和配置,加速系统的上线速度。
-
简单易维护: 初期用户访问量较少,系统运行负载较轻,不需要复杂的架构和专业的运维团队来维护。整个系统的维护和管理相对简单,降低了运维成本和复杂度。
-
快速响应变化: 单机架构下的系统可以快速响应变化的需求,通过技术团队的调整和优化,可以快速修改和升级系统,满足不断变化的业务需求。
-
低成本: 单机架构不需要额外的硬件设备和复杂的网络配置,减少了系统部署和维护的成本。在初期阶段,可以通过低成本的方式验证业务模型的可行性和有效性。
在单机架构中,当用户在浏览器中输入网站域名时,首先会经过DNS服务将域名解析为对应的IP地址,然后浏览器通过该IP地址访问对应的应用服务。
在单机架构中,常用的软件包括:
-
Web服务器软件: 如Tomcat、Netty、Nginx、Apache等,用于处理用户的HTTP请求和响应。
-
数据库软件: 如MySQL、Oracle、PostgreSQL、SQL Server等,用于存储和管理应用程序的数据。
应用数据分离架构
应用数据分离架构是一种在系统发展过程中应对性能压力的解决方案。随着系统上线成功,用户量逐渐增加,系统面临着越来越大的访问压力。为了提升系统的承载能力,我们需要进行系统重构和架构调整。然而,受限于有限的预算,我们需要寻找一种能够以最小成本提升系统性能的方法。
在应用数据分离架构中,主要的改变在于将应用服务和数据库服务分开部署。具体地,数据库服务被独立部署在其他服务器上,而应用服务则通过网络与数据库进行通信,获取所需的数据。这样的架构调整有助于减轻单个服务器的压力,提升系统的并发处理能力和稳定性。
这种架构调整的优势包括:
-
提升系统性能: 将数据库服务独立部署在其他服务器上,可以分担应用服务器的压力,提升系统的响应速度和并发处理能力。
-
提高系统稳定性: 应用数据分离可以减少单点故障的风险。即使某个应用服务器出现故障,数据库服务仍然可以独立运行,保障系统的稳定性。
-
便于扩展和维护: 分离应用和数据使得系统的各个部分更容易独立扩展和维护。可以根据需求灵活地添加或移除服务器,以适应不断增长的用户量和业务需求。
-
降低成本: 与全面重构系统相比,应用数据分离架构可以以较低的成本提升系统性能。通过充分利用现有的硬件资源和技术基础,可以快速实现架构调整。
应用数据分离架构是一种灵活、高效的系统优化方案,能够在有限预算下提升系统性能,满足不断增长的业务需求,是中小型企业应对性能挑战的有效手段。
应用服务集群架构
应用服务集群架构是一种针对系统扩展性需求的解决方案。随着系统的受欢迎程度和用户量的增加,单台应用服务器已经无法满足需求,因此我们需要采取措施来提升系统的承载能力。
在技术团队的讨论中,出现了两种主要的方案:
-
垂直扩展(纵向扩展)Scale Up: 这种方案是通过购买性能更优、价格更高的应用服务器来应对更多的流量。优势在于不需要对系统软件做任何调整,可以快速实现扩展。然而,劣势也显而易见:硬件性能与价格的关系是非线性的,选择性能加倍的硬件可能需要花费超过4倍的价格,而且硬件性能提升存在明显的上限。
-
水平扩展(横向扩展)Scale Out: 这种方案是通过调整软件架构,在应用层增加更多的硬件资源,将用户流量分担到不同的应用层服务器上,以提升系统的承载能力。优势在于成本相对较低,并且提升的上限空间较大。然而,劣势是带给系统更多的复杂性,需要技术团队有更丰富的经验来应对。
总的来说,水平扩展在应对系统扩展性需求方面具有更大的优势,尤其是在长期发展和成本效益方面。虽然需要面对一定的复杂性和技术挑战,但它提供了更灵活、可持续的解决方案,能够更好地满足系统不断增长的用户和业务需求。
经过团队的学习、调研和讨论,最终选择了水平扩展的方案来解决系统扩展性问题。然而,这种方案的实施需要引入一个新的组件——负载均衡系统。
负载均衡的作用是解决用户流量应该分发到哪台应用服务器的问题。在实际应用中,负载均衡不仅仅局限于应用层,甚至可能涉及其他网络层。
在负载均衡的实现中,流量调度算法至关重要。以下是几种常见的流量调度算法:
-
Round-Robin(轮询算法):即将请求依次分配给不同的应用服务器,非常公平。
-
Weight-Round-Robin(加权轮询算法):为不同的服务器(例如性能不同)分配不同的权重,使得性能更好的服务器处理更多的请求,实现了一种“能者多劳”的分配方式。
-
一致性哈希散列算法:通过计算用户的特征值(如IP地址)得到哈希值,然后根据哈希结果来进行流量分发。这个算法的优点是确保来自相同用户的请求总是被分发到同一台服务器上,从而提高了缓存命中率和系统的稳定性。这种算法常被用于实现专项客户经理服务等需求。
负载均衡软件是用于实现负载均衡功能的关键组件。以下是一些常见的负载均衡软件:
-
Nginx:Nginx 是一个高性能的开源的反向代理服务器和负载均衡器,同时也是一个非常流行的 Web 服务器。它具有高度的可靠性、灵活性和可配置性,常用于代理 HTTP、HTTPS、SMTP 和 POP3 等协议的流量,实现负载均衡和反向代理等功能。
-
HAProxy:HAProxy 是一个高性能的开源负载均衡器和代理服务器,专注于 TCP 和 HTTP 应用的负载均衡。它支持灵活的配置和强大的调度算法,常用于将流量分发到多个后端服务器,以实现高可用性和性能扩展。
-
LVS(Linux Virtual Server):LVS 是一个基于 Linux 内核的负载均衡解决方案,提供了四种负载均衡调度算法:轮询(Round-Robin)、加权轮询、最少连接和源地址散列。LVS 主要用于 TCP 和 UDP 流量的负载均衡。
-
F5 BIG-IP:F5 BIG-IP 是一款商业级的负载均衡器和应用交付控制器(ADC),提供了广泛的负载均衡、安全性和应用优化功能。它支持灵活的负载均衡算法、内容转发和 SSL 加速等特性,适用于大型企业和互联网应用的部署。
读写分离 / 主从分离架构
上⼀节提到,我们通过负载均衡将用户的请求分发到不同的应用服务器上,从而实现了并行处理,并且可以根据业务增长动态扩展服务器数量来缓解压力。然而,随着业务的持续增长,不管我们扩展多少台应用服务器,最终这些请求都会涉及到数据库的读写操作,而到达一定程度后,数据库的压力就会成为系统承载能力的瓶颈。
在这种情况下,我们可能会思考是否可以像扩展应用服务器一样扩展数据库服务器,但答案是否定的。这是因为数据库服务具有其特殊性:如果将数据分散到多个服务器上,将会导致数据的一致性难以保证。这里所说的数据一致性,是指对于同一个系统,在任何时间和地点,我们都应该看到一个保持统一的数据状态。举例来说,考虑银行管理的账户余额情况,如果一笔转账后,一份数据库的数据被修改了,但另一份数据库没有被修改,那么用户获取的存款余额将会是错误的。
为了解决这个问题,我们采用了一种解决方案:保留一个主要的数据库作为写入数据库,而其他的数据库则作为从属数据库。从属数据库中的所有数据都来自于主库的数据,并经过同步后,从属数据库可以维护与主库一致的数据。接下来,为了分担数据库的压力,我们将写入数据请求全部交给主库处理,但读取请求则分散到各个从属库中。由于大部分系统中,读写请求的比例是不成比例的,例如 100 次读取只有 1 次写入,所以只要将读请求分担给各个从属库处理,数据库的压力就会减轻许多。当然,这个过程并不是没有代价的,主库到从属库的数据同步实际上是有时间成本的,但我们暂时不会深入探讨这个问题。
应用中需要对读写请求进行分离处理,因此我们可以利用一些数据库中间件来托管请求分离的责任。
相关软件包括 MyCat、TDDL、Amoeba、Cobar 等类似的数据库中间件。这些中间件提供了读写分离、负载均衡等功能,帮助管理数据库集群,并优化数据库访问性能。它们能够自动将读操作路由到从库,而写操作则发送到主库,从而实现了数据库请求的分离处理。
引入缓存⸺冷热分离架构
随着访问量的持续增加,我们开始观察到业务中某些数据的读取频率远远超过其他数据的读取频率。这些数据通常被称为热点数据,因为它们是用户最频繁访问的数据,比如热门商品、热门文章等。相对应的,那些很少被访问的数据则被称为冷数据。针对热点数据,我们希望能够提高其读取的响应速度,以提升用户体验。为了实现这一目标,我们采取了多种缓存措施,其中包括在本地增加缓存(只存放一小部分热点数据),以及在外部引入分布式缓存系统。这样一来,热点数据的查询请求就可以在访问数据库(仍然存放全量数据)之前被缓存系统拦截,从而大大减轻了数据库的负载压力(二八原则)。在实施缓存策略的过程中,我们还需要解决缓存一致性、缓存穿透/击穿、缓存雪崩等问题,这些都是需要认真对待和处理的挑战。
但是,虽然引入缓存确实可以带来许多性能上的优势,它也同时也带来了一些与数据库数据同步相关的问题:
-
缓存一致性: 缓存中的数据应该与数据库中的数据保持一致。当数据库中的数据发生变化时,需要确保缓存中的数据及时更新,否则会导致数据不一致的问题。
-
缓存更新策略: 需要设计合适的缓存更新策略,以确保数据更新到缓存的时效性和准确性。常见的策略包括立即更新、定时更新、基于事件触发的更新等。
-
缓存穿透: 当查询请求的数据在数据库中不存在时,会导致大量的查询请求直接击穿缓存,访问数据库,增加数据库负载。需要采取相应的措施来缓解缓存穿透问题,如使用布隆过滤器、设置空值缓存等。
-
缓存击穿: 当某个热点数据过期或被移除时,大量的查询请求同时涌入,击穿缓存,访问数据库,导致数据库压力剧增。可以通过加锁、设置热点数据永不过期等方式来缓解缓存击穿问题。
-
缓存雪崩: 当缓存中的大量数据同时失效时,会导致大量请求直接访问数据库,造成数据库压力过大,甚至宕机。可以通过设置不同的失效时间、采用分布式缓存、限流等方式来缓解缓存雪崩问题。
综上所述,引入缓存虽然能够提升系统性能,但我们也需要仔细考虑数据同步的问题,并采取相应的策略和措施来保证系统的稳定性和一致性。
垂直分库
垂直分库是随着业务数据量的不断增长而引入的一种数据库架构调整策略。
随着时间的推移,单一数据库中存储的数据量可能会变得庞大,导致数据库性能下降,查询速度变慢,甚至影响到系统的稳定性和可用性。为了解决这个问题,我们可以根据业务需求,将数据按照特定的规则分散到不同的数据库中进行存储和管理。
具体而言,原本我们的系统架构中可能只包含一个单一的数据库服务器,该服务器上承载着多个数据库,每个数据库代表着逻辑上的数据集合,通过CREATE DATABASE命令创建。然而,随着系统的发展和业务的增长,单一数据库服务器可能会面临性能瓶颈、可用性问题或者容量限制等挑战。
为了应对这些挑战,我们可以引入多个数据库服务器。每个数据库服务器可以单独承担一个或者多个数据库的存储责任。这种分布式的数据库架构能够有效地分担系统的负载、提高系统的扩展性和容错性。
通过引入多个数据库服务器,我们可以实现以下目标:
-
性能提升: 将数据库负载分散到多个服务器上,可以减轻单一服务器的压力,从而提高整体系统的性能和响应速度。
-
可用性提高: 多个数据库服务器之间可以相互备份和容错,一台服务器出现故障时,其他服务器仍然可以继续提供服务,从而提高系统的可用性和稳定性。
-
容量扩展: 每个数据库服务器可以独立地扩展存储容量,根据需要动态调整硬件资源,满足不断增长的数据存储需求。
-
灵活性增强: 不同的数据库可以部署在不同的服务器上,根据业务需求和数据特性进行灵活配置和管理,从而更好地满足业务的需求。
总之,引入多个数据库服务器是一种有效的解决方案这样做可以有效地分散数据库负载和提高系统的可扩展性和容错性。我们采取以下步骤就可以实现这一目标:
-
选择合适的分片策略: 需要根据业务需求和系统特点选择合适的分片策略,将数据库中的数据按照一定的规则进行划分和分配到不同的数据库服务器上。常见的分片策略包括基于范围、哈希、分区等。
-
部署多个数据库服务器: 根据分片策略,在不同的物理或虚拟主机上部署多个数据库服务器,每个数据库服务器负责存储一个或多个数据库的数据。可以根据需要调整数据库服务器的配置,如CPU、内存、存储等。
-
配置数据库集群: 针对每个数据库服务器,需要配置数据库集群(如MySQL集群、MongoDB副本集等),确保数据库服务器的高可用性和容错性。
-
实现数据同步和复制: 在不同的数据库服务器之间实现数据的同步和复制,确保数据在各个服务器之间的一致性。可以使用数据库自带的复制机制,也可以借助第三方工具来实现数据的同步和备份。
-
更新应用程序逻辑: 更新应用程序的数据库访问逻辑,使其能够根据分片策略选择合适的数据库服务器进行数据读写操作。需要确保应用程序具备负载均衡和故障切换的能力,以应对数据库服务器的故障和负载不均衡等情况。
-
监控和管理: 建立监控系统,监控数据库服务器的运行状态和性能指标,及时发现并解决潜在的问题。同时建立管理系统,统一管理数据库服务器的配置、版本和维护计划,确保系统的稳定性和可靠性。
另外,如果某个表特别大,达到了一个主机的存储容量的上限,那么我们也可以考虑对该表进行拆分。拆分表是一种常见的数据库优化策略,可以将大表按照某种规则或逻辑分割成多个小表,分别存储在不同的数据库服务器上,从而降低单个服务器的负载压力,并提高系统的性能和可扩展性。
具体来说,拆分表可以采取以下几种方式:
-
按照时间范围拆分: 将大表中的数据按照时间范围进行拆分,例如按月份或按年份,将不同时间段内的数据存储在不同的表中,或者存储在不同的数据库服务器上。这样可以实现数据的分区管理,同时也便于进行历史数据的归档和清理。
-
按照业务逻辑拆分: 根据业务需求和数据访问模式,将大表按照某种业务逻辑进行拆分,例如按照地域、用户类型、业务类型等,将不同类别的数据存储在不同的表或服务器上,从而实现数据的分层管理和定制化存储。
-
按照数据量拆分: 当单个表的数据量过大时,可以将表按照数据量进行拆分,例如按照数据的哈希值、范围值等进行拆分,将数据均匀地分散到多个小表或服务器上,以实现数据的均衡存储和访问。
-
垂直拆分和水平拆分: 可以根据数据的字段特性进行垂直拆分,将表中的不同字段拆分到不同的表或服务器上;也可以根据数据的行特性进行水平拆分,将表中的不同行数据分散到不同的表或服务器上,以实现数据的集中存储和高效访问。
当然,具体分库分表应该怎么实现还是得结合世纪到业务场景展开。
举例来说,针对评论数据,我们可以按照商品ID进行哈希计算,将不同商品的评论数据分别存储到对应的数据库表中。对于支付记录等数据,我们可以按照小时级别创建表,每个小时的支付记录存储在对应的小表中,通过用户ID或记录编号来路由数据。只要每个小表中的数据量足够小,而且请求能够均匀地分发到多台服务器上的小表中,就可以通过水平扩展的方式来提升数据库的性能。
Mycat等数据库中间件可以支持在大表拆分为小表情况下的访问控制,使得这种垂直分库的方案得以实现。然而,这种做法也带来了一定的挑战,增加了数据库运维的复杂度,对DBA的要求也更高。当数据库采用这种结构时,可以被称为分布式数据库,但实际上,不同组成部分由不同的组件单独实现。例如,分库分表的管理和请求分发由Mycat实现,SQL解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现,这种架构实际上是MPP(大规模并行处理)架构的一种实现。
我们上面说,随着业务数据量的不断增长,单一数据库中的数据可能会变得庞大,影响系统性能和稳定性。为此,垂直分库将数据按照业务特点分散到不同的数据库中,使得每个数据库只负责处理特定类型或特定范围的数据,从而提高了系统的性能和可伸缩性。
这种架构中,数据库中间件如 Mycat、TDDL 等扮演了关键角色,它们能够支持大表拆分为小表的访问控制,使得垂直分库得以实现。但垂直分库也带来了一些挑战,增加了数据库运维的复杂度,对 DBA 的要求更高。
在实践中,有许多数据库软件可以支持垂直分库的架构,其中包括:
- Greenplum:一个开源的大数据分析数据库管理系统,可以支持并行处理和高性能的数据分析。
- TiDB:一个开源的分布式 NewSQL 数据库,具有水平扩展、高可用、强一致性和分布式事务等特性。
- PostgreSQL XC:一个基于 PostgreSQL 构建的开源分布式数据库,支持全局事务和分布式查询等功能。
- HAWQ:Apache HAWQ 是一个在 Apache Hadoop 上运行的 SQL 引擎,支持 SQL 查询和数据分析。
- 商用数据库如南大通用的 GBase、睿帆科技的 雪球 DB、华为的 LibrA 等,它们也提供了垂直分库的支持,并在商业应用中发挥着重要作用。
业务拆分
随着团队规模的扩大和业务的增长,我们开始将业务分配给不同的开发团队来管理。每个团队负责独立开发和维护自己的微服务。为了确保数据访问的隔离,我们采取了相应的措施,如利用 Gateway 和消息总线等技术。这样做可以实现各个微服务之间的调用关联,同时确保数据的安全性和一致性。此外,我们还考虑将一些通用的业务功能,如用户管理、安全管理和数据采集等,提取出来作为公共服务,供各个微服务共享和复用。
业务拆分是一种组织架构和开发方法,旨在将大型单一应用拆分成多个独立的、相对较小的服务单元,每个服务单元都专注于执行特定的业务功能。这种架构模式被称为微服务架构。
随着团队规模的扩大和业务的发展,采用微服务架构可以带来多方面的好处,例如:
-
团队自治: 每个团队负责维护和开发自己的微服务,这种分工明确的模式使得团队可以更灵活地决定技术栈、开发节奏和团队规模。团队成员可以根据自己的专长和兴趣参与到相关的微服务开发中,从而提高工作效率和团队凝聚力。
-
快速迭代: 微服务的独立部署和发布使得团队能够更快地进行迭代和更新。每个微服务都可以独立开发、测试和部署,不会影响其他微服务的运行,从而加速了业务的交付速度,提高了团队的敏捷性和响应能力。
-
弹性伸缩: 每个微服务都可以独立水平扩展,根据需求进行伸缩。这种弹性伸缩的能力可以根据流量的变化和业务需求进行动态调整,提高了系统的可伸缩性和弹性,确保了系统在高负载时依然能够稳定运行。
-
技术多样性: 不同的微服务可以选择最适合自身业务需求的技术栈。这样可以使得团队更加灵活和创新,充分发挥团队成员的专长和擅长领域。同时,技术多样性也有助于系统的健壮性和适应性,可以更好地应对未来业务发展的变化和挑战。
-
解决了人的问题: 微服务架构通过将复杂的系统拆分成小而自治的服务单元,降低了团队协作和沟通的复杂度。每个团队负责维护和开发自己的微服务,提高了团队的责任感和主动性,有利于团队成员的个人成长和发展。
-
更方便与功能的复用: 微服务的模块化设计使得不同的功能可以被封装成独立的服务,可以更方便地进行功能的复用。当其他服务需要使用某个功能时,可以直接调用相应的微服务接口,避免了重复开发和维护,提高了开发效率和代码质量。
-
可以给不同的服务进行不同的部署: 微服务架构使得每个微服务可以独立部署,可以根据需要将不同的服务部署在不同的服务器上,根据负载和需求进行动态调整。这种灵活的部署方式可以优化系统的资源利用和性能表现,提高了系统的稳定性和可用性。
同时,引入微服务架构也确实会带来一些成本和挑战,其中包括:
-
系统性能下降: 微服务架构将系统拆分为多个独立的服务,服务之间需要通过网络通信来进行数据交互,这可能导致性能下降。特别是在分布式系统中,网络通信的延迟和带宽限制可能会成为性能瓶颈。为了保证性能不下降,可能需要引入更多的硬件资源,例如更多的服务器、更高速的网络设备等。还好,现在随着硬件技术的发展,网卡有万兆网卡,读写速度已经能超过硬盘读写了,但是很贵。
-
系统复杂度增加: 微服务架构会引入更多的服务和组件,系统的复杂度会大大增加。管理和维护多个微服务、处理服务之间的依赖关系、版本管理、服务发现和路由等都会成为挑战。我们需要建立更丰富和灵活的监控系统,配套专门的运维人员,以便及时发现和解决问题。
-
可用性受影响: 系统中的每个微服务都可能成为单点故障,一旦某个服务出现故障或不可用,可能会影响整个系统的稳定性和可用性。因此,需要实施有效的容错和故障恢复机制,例如服务治理、负载均衡、自动扩展和自动恢复等,以确保系统的高可用性。
为了实现微服务架构,我们通常会采用以下技术手段:
- Gateway:作为微服务的入口,负责路由请求到相应的微服务,同时处理认证、授权、限流等功能。
- 消息总线:用于微服务之间的异步通信,实现解耦和异步处理。
- 服务注册与发现:用于管理和发现微服务实例的工具,如 Consul、Eureka 等。
- 容器化和编排:使用容器技术如 Docker 将微服务进行打包,并通过容器编排工具如 Kubernetes 进行部署和管理。
在实践中,业务拆分成微服务的过程需要仔细考虑业务边界和服务粒度,同时还需要考虑微服务之间的通信方式、数据一致性和治理等问题。
尾声
在构建高可用、高并发系统的基本框架至此已经初步完成。需要注意的是,上述架构演进顺序只是针对某个方面进行的单独改进。在实际场景中,可能会同时出现多个问题需要解决,或者另一方面可能会先达到瓶颈。因此,我们应根据实际问题实际解决,及时调整架构策略。
在政府类系统中,尽管并发量可能不大,但业务可能十分丰富,因此高并发可能并不是首要解决的问题。在这种情况下,优先考虑的可能是解决丰富的业务需求。
对于一次性实施且性能指标明确的系统,设计架构以满足系统的性能指标要求即可,但同时也要预留扩展架构的接口,以备不时之需。对于不断发展的系统,如电商平台,应设计架构以满足下一阶段用户量和性能指标要求,并根据业务增长不断迭代升级架构,以支持更高的并发和更丰富的业务。
所谓的“大数据”实际上是指海量数据采集、清洗、转换、存储、分析、服务等场景的解决方案的统称。在每个场景中都包含了多种可选的技术,例如数据采集有 Flume、Sqoop、Kettle 等,数据存储有分布式文件系统 HDFS、FastDFS,NoSQL 数据库 HBase、MongoDB 等,数据分析有 Spark 技术栈、机器学习算法等。总的来说,大数据架构是根据业务需求整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往由大数据架构来提供。