架构设计系列4:如何设计高性能架构

在架构设计系列1:什么是架构设计中,我们讲了架构设计的主要目的,是为了解决软件系统复杂度带来的问题,今天我们来聊聊软件系统复杂度的来源之一高性能。

一、什么是高性能架构?

要搞清楚什么是高性能架构,我们需先弄明白高性能是什么

高性能定义
首先,性能是什么,怎么理解它?
简单来说,性能,指的是一件事务的处理能力。

那么,什么是高性能呢?
高性能,指的是处理一件事务的速度更快,所消耗的资源更少。

高性能架构
那么,什么是高性能架构呢?
高性能架构,是指在有限的资源投入下,通过采用适当的技术和策略,让系统具备了优秀的性能。

对于技术人员来说,在有限的资源投入下,如何提高系统性能,这是一个挑战,更是一个机遇。
可以想象一下,同为架构师,你做的架构,在性能相同的情况下,成本更低,那是不是你的优势呢。


二、为什么高性能架构重要

在进入互联网时代后,业务的发展速度是远远超乎你的想象的。例如:

  • 2016 年,双11支付宝每秒峰值达 12 万笔支付
  • 2017 年,春节微信红包收发红包每秒达到 76 万个

我们提炼下关键信息,每秒12万笔支付,每秒收发76万个红包。这两个数字意味着,有成千上万,甚至上亿的用户,在同时使用系统。

在用户体量这么庞大的情况下,可以想象系统的压力有多大,尤其还是支付和红包这种复杂的业务,这要求整个链路都保证高性能。而一个复杂系统的链路往往是很长的,要保证链路上的每个环节配合起来达到高性能的目的,是一件个复杂且具有挑战性的任务。

如此,高性能架构就派上了用场。

我们可以通过高性能架构设计,最大限度地提高系统的处理速度、吞吐量和效率,从而提供稳定和可靠的系统服务,以满足大规模、高并发和复杂的业务需求。

高性能系统,一般具备如下特点:

  • 快速响应
  • 高吞吐量
  • 低延迟
  • 高并发性
  • 可扩展性

三、如何设计高性能架构

软件系统中高性能带来的复杂度主要体现在两个方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度

因此,我们可以从单机高性能集群高性能两个方面来理解软件系统的高性能。

接下来,我们一起来探讨一下有哪些常用技术可以提升单机性能和集群性能。

1、提升性能的发力方向

1.1 单机高性能

单机高性能最关键的地方就是操作系统

计算机性能的发展本质上是硬件发展驱动的,尤其是CPU的性能发展。著名的“摩尔定律”表明了 CPU 的处理能力每隔 18 个月就翻一番;而将硬件性能充分发挥出来的关键就是操作系统。

所以操作系统本身其实也是跟随硬件的发展而发展的,操作系统是软件系统的运行环境,操作系统的复杂度直接决定了软件系统的复杂度。

操作系统和性能最相关的就是进程线程

1. 多进程

计算机如何并行运行任务?
在早期计算机一次只能执行一个任务,如果某个任务需要从 I/O 设备(例如磁带)读取大量的数据,在 I/O 操作的过程中,CPU 其实是空闲的,而这个空闲时间本来是可以进行其他计算的。

为了提升性能,用进程来对应一个任务,每个任务都有自己独立的内存空间,进程间互不相关,由操作系统来进行调度。为了达到多进程并行运行的目的,采取了分时的方式,即把 CPU 的时间分成很多片段,每个片段只能执行某个进程中的指令。

虽然从操作系统和 CPU 的角度来说还是串行处理的,但是由于 CPU 的处理速度很快,从用户的角度来看,感觉是多进程在并行处理。

进程间如何通信?
多进程虽然要求每个任务都有独立的内存空间,进程间互不相关,但从用户的角度来看,如果两个任务之间能够在运行过程中就进行通信,会让任务设计变得更加灵活高效。

否则如果两个任务运行过程中不能通信,只能是 A 任务将结果写到存储,B 任务再从存储读取进行处理,不仅效率低,而且任务设计更加复杂。

为了解决这个问题,进程间通信的各种方式被设计出来了,包括管道、消息队列、信号量、共享存储等。

2. 多线程

进程内如何并行运行任务?
多进程让多任务能够并行处理,但本身还有缺点,单个进程内部只能串行处理,而实际上很多进程内部的子任务并不要求是严格按照时间顺序来执行的,也需要并行处理。

为了解决这个问题,人们又发明了线程,线程是进程内部的子任务,但这些子任务都共享同一份进程数据。为了保证数据的正确性,又发明了互斥锁机制。

有了多线程后,操作系统调度的最小单位就变成了线程,而进程变成了操作系统分配资源的最小单位。

3. 编码过程中如何优化单机性能?

要提升单机的性能,其中一个关键点就是服务器采取的并发模型,而这就涉及到多进程、多线程和异步非阻塞、同步非阻塞的IO模型。

IO 多路复用
IO 多路复用技术的两个关键点:

  1. 当多条连接共用一个阻塞对象后,进程只需要在一个阻塞对象上等待,而无须再轮询所有连接,常见的实现方式有 select、epoll、kqueue 等。
  2. 当某条连接有新的数据可以处理时,操作系统会通知进程,进程从阻塞状态返回,开始进行业务处理。

Reactor 和 Proactor 架构模式
在后端系统设计里面,要想实现单机的高性能,那在 IO 多路复用基础之上,我们的整个网络框架,还需要配合池化技术来提高我们的性能。

因此,业界一般都是采用 I/O 多路复用 + 线程池 的方式来提高性能。与之对应的,在业界常用的两个单机高性能的架构模式就是Reactor 和 Proactor 模式。Reactor 模式属于同步非阻塞网络模型,Proactor 模式属于异步非阻塞网络模型

在业内开源软件里面,Redis 采用的是 单 Reactor 单进程的方式,Memcache 采用的是 多 Reactor 多线程的方式,Nginx 采用的是多 Reactor 多进程的方式。

4. 小结

如果我们要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点。

多进程和多线程虽然让多任务并行处理的性能大大提升,但本质上还是分时系统,并不能做到同一时刻真正的并行。解决这个问题的方式,就是让多个 CPU 能够同时执行计算任务,从而实现真正意义上的多任务并行。
目前最常见的多核处理器就是SMP方案。SMP,全称 Symmetric Multi-Processor,对称多处理器结构。


1.2 集群高性能

虽然计算机硬件的性能快速发展,但和业务的发展速度相比,还是小巫见大巫了,尤其是进入互联网时代后,业务的发展速度远远超过了硬件的发展速度。

就像前面提到的,支付和红包这两种复杂的业务,单机的性能无论如何是无法支撑的,必须采用集群的方式来达到高性能。例如,支付宝和微信这种规模的业务系统,后台系统的机器数量都是万台级别的。

通过大量机器来提升性能,并不仅仅是增加机器这么简单,让多台机器配合起来达到高性能的目的,是一个复杂的任务。常见的方式有:

1. 任务分配

任务分配是指每台机器都可以处理完整的业务任务,将不同的任务分配到不同的机器上执行。

高性能集群设计的复杂性主要体现在需要增加一个任务分配器,以及选择一个合适的任务分配算法。

对于任务分配器,更流行的叫法是负载均衡器。但这个名称会让人潜意识里觉得,任务分配的目的是要保持各个计算单元的负载达到均衡状态。而实际上任务分配并不仅限于考虑计算单元的负载均衡,不同的任务分配算法有不一样的目标,有的基于负载考虑,有的基于性能(吞吐量、响应时间)考虑,有的基于业务考虑。

选择合适的任务分配器也是一件复杂的事情,需要综合考虑性能、成本、可维护性、可用性等各方面的因素。

常见的任务分配器的分类有:

DNS负载均衡
这是最简单最常见的负载均衡方式,经常用来实现地理级别的均衡。

DNS负载均衡的本质是不同地理位置的用户访问时,DNS解析同一个域名可以返回不同的IP地址。比如,北方用户访问位于北京的机房,南方的用户则访问深圳机房。以www.baidu.com来说,北方用户获取的地址是61.135.165.224,南方用户获取的是14.215.177.38。

  • 优点:实现简单、成本低,无须自己开发或者维护、就近访问,访问速度快
  • 缺点:更新不及时、扩展性差、分配策略简单

硬件负载均衡
通过单独的硬件设备实现负载均衡的功能,此类设备和路由器、交换机类似,可以理解为一个用于负载均衡的基础网络设备。当前典型的主要有两款:F5和A10。

  • 优点:性能强劲(支持100万以上的并发)、功能强大(支持所有层级的负载均衡,支持全面的均衡算法)
  • 缺点:价格昂贵、扩展能力差

软件负载均衡
通过提供负载均衡软件来实现负载均衡功能,比较常见的有LVS和Nginx,其中LVS是Linux内核的4层负载均衡,Nginx是软件的7层负载均衡。

除了使用开源的系统进行负载均衡,如果业务比较特殊,也有可能基于开源系统进行定制(例如,Nginx插件),甚至进行自研。

  • 优点:简单、便宜、灵活,4层和7层负载均衡都可以根据业务需求进行选择和扩展
  • 缺点:相对硬件负载均衡来说性能一般,功能没有那么强大

软件负载均衡和硬件负载均衡的最主要区别就在于性能上,硬件负载均衡性能远远高于软件。比如Nginx的性能是万级,一般的Linux服务器上安装Nginx后大概能达到5万/秒;LVS的性能是十万级,据说可达到80万/秒;而F5性能是百万级,从 200万/秒到800万/秒都有。

负载均衡典型架构
一般情况下,我们会对着三种负载均衡方式基于各自的优缺点进行组合使用。组合的基本原则为:DNS负载均衡实现地理级别的负载均衡;硬件负载均衡实现集群级别的负载均衡;软件负载均衡实现机器级别的负载均衡。

2. 任务分解

通过任务分配的方式,我们能够突破单台机器处理性能的瓶颈,通过增加更多的机器来满足业务的性能需求,但如果业务本身也越来越复杂,单纯只通过任务分配的方式来扩展性能,收益会越来越低。

例如,业务简单的时候 1 台机器扩展到 10 台机器,性能能够提升 8 倍(需要扣除机器群带来的部分性能损耗,因此无法达到理论上的 10 倍那么高),但如果业务越来越复杂,1 台机器扩展到 10 台,性能可能只能提升 5 倍。

造成这种现象的主要原因是业务越来越复杂,单台机器处理的性能会越来越低。为了能够继续提升性能,我们需要采用新的方式:任务分解

微服务架构就采用了这种思路,通过这种任务分解的方式,能够把原来大一统但复杂的业务系统,拆分成小而简单但需要多个系统配合的业务系统。

从业务的角度来看,任务分解既不会减少功能,也不会减少代码量(事实上代码量可能还会增加,因为从代码内部调用改为通过服务器之间的接口调用),那为何通过任务分解就能够提升性能呢?主要有如下几方面的因素:

  1. 简单的系统更容易做到高性能
    系统的功能越简单,影响性能的点就越少,就更加容易进行有针对性的优化。

  2. 可以针对单个任务进行扩展
    当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现,而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,不需要改动整个系统,风险会小很多。

既然将一个大一统的系统分解为多个子系统能够提升性能,那是不是划分得越细越好呢?

其实不然,这样做性能不仅不会提升,反而还会下降,最主要的原因是如果系统拆分得太细,为了完成某个业务,系统间的调用次数会呈指数级别上升,而系统间的调用通道目前都是通过网络传输的方式,性能远比系统内的函数调用要低得多。

因此,任务分解带来的性能收益是有一个度的,并不是任务分解越细越好,而对于架构设计来说,如何把握这个粒度就非常关键了。

具体如何做任务分解,可以参考读 架构设计系列3:如何设计可扩展架构 中的拆分 这个章节。


2、提升性能的常用手段

接下来,我们来简单分析一下提升性能的几种常见手段。

2.1 服务化设计

1. What:什么是服务化?

服务化是指通过任务分解的方式,把一个复杂的业务系统,拆分成多个小而简单且需要互相配合的业务系统。

大型复杂系统服务化的必然结果,是业务中台化。另外,单体架构并不一定是坏的架构,这取决于应用的复杂度。例如,一个初创的公司,要在互联网上开展业务,由于业务规模不大,业务复杂性有限,研发数量也不多,这个时候,单体架构就是最合适的。

2. Why:为什么要服务化?

服务化的目的,是将可重用的服务灵活组合在一起,来快速响应多变的业务需求,以支撑业务快速试错。

如何判断一个系统需不需要服务化呢?通常我们主要需要考虑以下几个因素:

  • 是否是大型复杂系统?
  • 是否存在重复建设?
  • 业务是否具备不确定性?
  • 技术是否制约企业发展?
  • 系统是否有性能瓶颈?

如果一个系统存在以上几个问题,那么通过服务化来对系统进行整体技术升级和业务重构,是一个较好的途径。

3. How:服务化怎么做?

组织架构调整到位
为了将系统服务化更好的落地,组织架构的调整,是非常关键的一步。

因为系统服务化以后,会延伸出来一些团队问题,例如团队的分工、协作等等。因此只有将组织架构调整到位,才能将服务化带来的好处最大化。

服务化的基础建设
服务化以后,其核心强调的是不同服务之间的通信,而这会衍生出一系列的复杂问题需要我们去解决,例如服务注册、服务发布、服务调用、负载均衡,监控等等,这需要一套完整的服务治理方案。

因此,服务化的必要条件就是要有一套服务化框架,这个服务化框架要能解决这些复杂问题,并且服务化框架的性能尤为重要。

目前主流的两种服务化框架:SpringCloud 和 Dubbo

服务化的重要手段

  • 无状态设计:无状态使服务更容易快速扩缩容。
  • 拆分设计:化繁为简,降低难度,分而治之。
4. 小结

一句话总结:业务解耦,能力复用,高效交付

2.2 异步设计

1. What:什么是异步?

异步是一种设计理念,是相对同步而言的。

同步指发出一个调用时,调用方得等待这个调用返回结果才能继续往后执行。

异步指发出一个调用时,调用者不会立刻得到结果,而是可继续执行后续操作,直到被调用者处理完成后,通过状态、通知或回调来通知调用者。

2. Why:为什么要异步?

通过异步可以降低时延,提升系统的整体性能,改善用户体验。

3. How:异步怎么实现?

1)IO 层面的异步
针对 IO 层面的异步调用,就是我们常说的 I/O 模型,有阻塞、非阻塞和同步、异步这几种类型。

在 Linux 操作系统内核中,内置了 5 种不同的 IO 交互模式,分别是阻塞 IO、非阻塞 IO、多路复用 IO、信号驱动 IO、异步 IO。针对网络 IO 模型而言,Linux 下,使用最多性能较好的是同步非阻塞模型。

异步调用的常用技术

  • 异步通信:NIO,Netty

2)业务逻辑层面的异步
业务逻辑层面的异步流程,就是指让我们的应用程序在业务逻辑上可以异步的执行。

通常比较复杂的业务,都会有很多步骤流程,如果所有步骤都是同步的话,那么当这些步骤中有一步卡住,那么整个流程都会卡住,这样的流程显然性能不会很高。

为此,在业内,我们如果想要提高性能,提高并发,那么基本上都会采用异步流程的方式。

异步流程的常用技术

  • 消息队列:异步解耦、流量削峰
  • 异步编程:多线程,线程池
  • 事件驱动:发布订阅模式(观察者模式)
  • 作业驱动:定时任务,XXL-JOB
4. 小结

一句话总结总结:虽然异步的执行效率高,但是复杂性和编程难度也高,所以切勿滥用

2.3 池化设计

1. What:什么是池化技术?

池化技术是一种常见的提高性能的技术,它将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,减少重复创建和销毁资源,方便统一管理和复用,从而提高系统性能。

2. Why:为什么需要池化技术?

通过池化技术来减少重复创建销毁所带来的系统开销,提高系统性能。

3. How:池化技术怎么实现?

线程池

  • ForkJoinPool
  • ThreadPoolExecutor
    线程池的核心参数,需基于业务场景来进行设置,例如线程数可根据任务是IO密集型,还是CPU密集型来进行设置。

连接池

  • 数据库连接池
  • Redis连接池
  • HttpClient连接池
4. 小结

一句话总结:统一管理和复用资源,提高性能和资源利用率

2.4 缓存设计

1. What:什么是缓存?

缓存是一种提高资源访问速度的技术。其特性是:一次写入,无数次读取。

缓存的本质是用空间换时间。其牺牲了数据的实时性,以内存中的缓存数据,代替从目标服务器(如DB)读取最新的数据,可减轻服务器压力和减少网络延迟。

2. Why:为什么要使用缓存?

使用缓存的目的很明显就是为了提升系统性能(高性能、高并发)。

使用缓存的优缺点是什么?

优点

  • 优化性能,缩短响应时间
  • 降低压力,避免服务器过载
  • 节省宽带,缓解网络瓶颈问题

缺点

  • 消耗额外的空间
  • 可能存在数据一致性问题
3. How:如何使用缓存?

1)怎么提高资源访问速度呢?
将资源存放在离用户较近或访问速度更快的系统中

2)哪些位置可以使用缓存?

缓存分类缓存维度描述
客户端缓存浏览器缓存1、离用户最近的缓存点,借助用户的终端设备存储网络资源,性价比最高。
2、一般用于缓存图片、JS、CSS等,可通过消息头中的Expires和Cache-Control等属性来控制。
服务端缓存CDN缓存1、存放HTML,CSS,JS等静态资源。
2、起分流作用,减轻源服务器的负载。
服务端缓存反向代理缓存1、动静分离,一般缓存静态资源,动态资源转发到应用服务器处理。
服务端缓存本地缓存1、内存缓存,访问速度快,适合缓存少量数据的场景。
2、硬盘缓存,数据缓存到文件,访问速度比通过网络获取数据更快,适合缓存大量数据的场景。
服务端缓存分布式缓存1、大型网站架构中必备的架构要素。
2、缓存热点数据,减轻数据库压力。

3)哪种类型的缓存引入成本更高? 为什么引入成本更高?
本地缓存 和 分布式缓存的引入成本会更高。因为这两种类型缓存的资源与业务相关,需要经过业务逻辑的计算,所以对缓存与原数据之间的数据一致性要求更高。

4)1个核心指标:缓存命中率
缓存命中率越高,性能越好 。其计算公式为:缓存命中率 = 命中的次数 / (命中的次数 + 未命中的次数)。

如何提高缓存命中率?常见的策略如下:

  • 缓存时长:同等条件下,缓存时间越长,缓存命中率越高
  • 缓存更新:数据变化时,直接更新缓存值,比移除缓存的命中率更高
  • 缓存容量:缓存容量越大,缓存的数据越多,缓存命中率越高
  • 缓存粒度:缓存粒度越小,数据变化越小,缓存命中率越高(降低大key的风险)
  • 缓存预热:热点数据提前缓存,提高缓存命中率

一句话总结:如何提高缓存命中率?就是让数据更长时间的驻留在缓存中

5)1个核心问题:缓存一致性
缓存一致性指缓存与源数据之间的一致性,要保障缓存一致性,事情将变得复杂起来。

如何实现缓存一致性呢?常用的缓存策略如下:
在Cache/DB 架构中,缓存策略就是如何从 Cache 和 DB 读取、写入数据。

1、过期缓存模式:Cache Expiry Pattern

  • 特点:实现缓存一致性最简的方式,为缓存设置过期时间,达到最终一致性
  • 缺点:需要容忍所设置过期时间的数据不一致

2、旁路缓存模式:Cache Aside Pattern

  • 读取:Cache Hit,直接返回缓存数据,Cache Miss,从DB中加载数据到缓存,并返回
  • 写入:先写DB,再将Cache中对应的数据删除
  • 缺点:该模式可能会出现缓存和数据库双写不一致的情况,可以采用延迟双删模式最大限度降低这种不一致性

3、异步写入模式:Write Behind Pattern

  • 读取:Cache Hit,直接返回缓存数据,Cache Miss,直接返回空
  • 写入:先写DB,通过 DB 将写入的新数据投递到 MQ 中,再由异步进程消费 MQ 最终将数据写入 Cache 中

一句话总结:如何实现缓存一致性?就是让每个读操作,能够获得最新的写操作数据

4. 小结

一句话总结:缓存是应对高并发的王者(缓存为王)

2.5 数据存储设计

1. What:什么是数据存储?

数据存储通常是指数据以某种格式记录在计算机内部或外部存储介质上。

常见的存储介质有:磁带、磁盘等。数据存储方式因存储介质而不同。在磁带上数据仅按顺序文件方式存取;在磁盘上则可按使用要求采用顺序存取或直接存取方式。数据存储方式与数据文件组织密切相关,其关键在于建立记录的逻辑与物理顺序间对应关系,确定存储地址,以提高数据存取速度。

常见的数据存储管理系统有:数据库(MySQL)、搜索引擎(Elasticserach)、缓存系统(Redis)、消息队列(Kafka)等。这也是我们接下来探讨的重点。

2. Why:数据存储设计为什么重要?

在互联网时代,当系统并发量达到一定阶段时,数据存储往往会成为性能瓶颈。如果不在一开始就进行良好的设计,则后期的横向扩展,分库分表都会遇到困难。

为什么性能瓶颈往往是数据存储,而不是应用服务呢?
因为应用服务基本是无状态的,可以较为方便的进行水平扩展,因此应用服务的高性能会相对简单。但是对于数据存储的高性能,相对来说会复杂很多,因为数据是有状态的。

3. How:如何做数据存储设计?

常见的解决存储高性能的方案有如下几种,业界大多是围绕这些来构建,或者是做相关衍生和扩展。

1)读写分离

互联网系统往往都是读多写少,因此性能优化的第一步就是读写分离。

读写分离就是将读操作与写操作分离的一种优化手段,我们可以通过这一技术来解决数据存储的性能瓶颈问题。

目前业界流行的读写分离方案,通常是基于主从模式的架构,通过引入数据访问代理层,来实现访问动作的读写分离。具体有如下两种方式:

通过独立Proxy实现读写分离
引入数据访问代理的好处是源程序不需要做任何改动就可以实现读写分离,坏处是由于多了一层中间件做中转代理,性能上会有所下降,数据访问代理也容易成为性能瓶颈,并且还存在一定维护成本。典型产品有 MyCAT、阿里云-RDS数据库代理等。

通过内嵌SDK实现读写分离
还有另一种方式,是将数据访问代理层前置到应用侧,通过SDK方式与应用集成在一起,可避免独立一层所带来的性能损耗和维护成本高的问题。但这种方式对开发语言有一定要求,存在适用性问题。典型产品有 ShardingSphere 等。

2)数据分区

“分区”是指以物理方式将数据分割成独立的数据进行存储的过程。将数据分割成分区,可以对这些分区进行单独进行管理和访问。 分区可以改善可伸缩性、减少争用,以及优化性能。 另外,它还能提供一种按使用模式来分割数据的机制。

为何要将数据分区?

  • 提高扩展性。纵向扩展单一数据库系统最终会达到物理硬件的限制。 如果跨多个分区来分割数据,则每个分区托管在独立的服务器上,使系统几乎能够无限横向扩展。
  • 改善性能。在每个分区上的数据访问操作通过较小的数据卷进行。 在正确操作的情况下,分区可以提高系统的效率。
  • 提供操作灵活性。使用分区可以从多方面优化操作、最大程度提高管理效率及降低成本。
  • 提高可用性。跨多个服务器隔离数据可避免单点故障。 如果一个实例发生故障,只有该分区中的数据不可用。 其他分区上的操作可以继续进行。

如何设计分区?

数据分区的三个典型策略:

  • 水平分区(即分片)。在此策略中,每个分区都是独立的数据存储,但所有分区具有相同的架构。 每个分区称为分片,保存数据的特定子集,例如特定的一组客户的所有订单。最重要的因素是分片键的选择。分片可以将负载分散到多台计算机,减少争用并改善性能。
  • 垂直分区。在此策略中,每个分区在数据存储中保存项字段的子集。 这些字段已根据其使用模式进行分割。 例如,将经常访问的字段放在一个垂直分区,将较不经常访问的字段放在另一个垂直分区。垂直分区的最常见用途是降低与提取频繁访问的项相关的 I/O 和性能成本。
  • 功能分区。在此策略中,数据已根据系统中每个界限上下文使用数据的方式进行聚合。 例如,电子商务系统可能在一个分区中存储发票数据,在另一个分区中存储产品库存数据。通过功能分区来改善隔离效果和数据访问性能。

3)分库分表

对于分库分表的概念大家应该并不会陌生,其拆开来讲就是分库和分表两个手段:

  • 分表:指将一个表中的数据按照某种规则拆分到多张表中,降低表数据规模,提升查询效率。
  • 分库:指将一个数据库中的数据按照某种规则拆分到多个数据库中,以降低单服务器的压力,提升读写性能(如:CPU、内存、磁盘、IO)。

分库分表的两种典型方案:

垂直拆分

  • 垂直拆表:即大表拆小表,将一张表中不同”字段“分拆到多张表中,比如商品库将商品基本信息、商品库存、卖家信息等分拆到不同库表中。
  • 垂直拆库:将一个系统中的不同业务领域拆分为多个业务库。比如:商品库、订单库、用户库等。

水平拆分

  • 水平拆表:将数据按照某种维度拆分为多张表,但是由于多张表还是从属于一个库,其锁粒度降低,一定程度提升查询性能,但是仍然会有IO性能瓶颈。
  • 水平拆库:将数据按照某种维度分拆到多个库中,降低单机单库的压力,提升读写性能。

常见的水平拆分手段

  • range分库分表:通过范围法对分片键按照范围进行切分。比如:根据时间范围分库分表。
  • hash分库分表:通过哈希法对分片键采用hash取模。比如:根据用户ID分库分表。

关于分库分表的更多信息见文章:开发设计实践:分库分表实现方案,此处不再赘述。

4)冷热分离

冷热分离是指将历史冷数据与当前热数据分开存储,冷库只存放那那些走到终态的数据,热库存放还需要去修改字段的数据,这样可以减轻当前热数据的存储量,可以提高性能。

如果判断数据到底是冷数据还是热数据?或者说,什么情况下可以使用冷热分离?

  • 时间维度:用户能够接受新旧数据分开查询。例如,对于订单数据,我们可以将三个月之前的作为冷数据,三个月之内的数据作为热数据。
  • 状态维度:数据走到终态后,只有读没有写的需求。例如,对于订单数据,我们可以将状态为已完结的订单作为冷数据,其他作为热数据。
4. 小结

一句话总结:通过拆分手段,分散读写压力,分散存储压力,从而提高性能

四、最后

在追求系统高性能的同时,千万不要忽略了成本这个因素。因为,高性能往往意味着高成本。

因此在设计高性能系统时,要特别注意,成本最小化,收益最大化。

最后,作为一名技术人员,我们应该要有一个技术追求:学会用同样的资源做更多的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/150282.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

R实现数据分布特征的视觉化——多笔数据之间的比较

大家好,我是带我去滑雪! 如果要对两笔数据或者多笔数据的分布情况进行比较,Q-Q图、柱状图、星形图都是非常好的选择,下面开始实战。 (1)绘制Q-Q图 首先导入数据bankwage.csv文件,该数据集…

【深蓝学院】手写VIO第2章--IMU传感器--作业

这次作业坑很多,作业说明的不清楚,摸索了很长时间才将此次作业完成,在这里进行记录。 1. T1 1.1 题干 1.2 解答 1.2.1 法1,ros related方法 不知道为什么我的launch不了,在imu_utils目录下面建立build后&#xff0…

【redis学习笔记】缓存

redis主要的三个应用场景 存储数据缓存消息队列(redis本来是设计用来作为消息队列的) redis常用作mysql的缓存 因为MySQL等数据库,效率比较低,所以承担的并发量就有限。一旦请求数量多了,数据库的压力就会很大&#…

代码随想录第36天 | 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

1049. 最后一块石头的重量 第一想法 /*** param {number[]} stones* return {number}*/ var lastStoneWeightII function (nums) {// 和分割两个和相等的子数组一样//dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的…

数据科学最佳实践:Kedro 的工程化解决方案 | 开源日报 No.47

leonardomso/33-js-concepts Stars: 58.4k License: MIT 这个项目是一个帮助开发者掌握 JavaScript 概念的资源库。该项目基于 Stephen Curtis 撰写的一篇文章,包含了对 33 个重要 JavaScript 概念全面深入地讲解,并被 GitHub 评为 2018 年最佳开源项目…

前端项目nginx部署

进入nginx下载地址:https://nginx.org/ 下载完安装包以后,解压在D盘中 双击进去> 将前端打包好的文件放在nginx的html文件夹中 可能80端口会被系统所占用 我们可以在nginx的conf文件夹中的nginx.conf文件中修改80为90 之后我们就可以在任务管理器中看到了 然后 localhost:…

JavaEE 网络原理——TCP的工作机制(中篇 三次握手和四次挥手)

文章目录 一、TCP 内部工作机制——连接管理1. 连接(三次握手)(1).有连接和确认应答之间的关系(2). 通过客户端和服务器详细描述三次握手 2. 断开连接(四次挥手)(1)讨论“四次握手”中间步骤的合并问题。(2) 根据简单的 TCP 代码解释断开连接(3) 四次挥手中的两个重要的 TCP 状…

【C语言】什么是宏定义?(#define详解)

🦄个人主页:修修修也 🎏所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 ​ 目录 一.什么是宏定义 二.宏定义的组成 第1部分 第2部分 第3部分 三.宏定义的应用 🎏类对象宏 🎏类函数宏 1.求两个数中的较大值 2.求一个数的…

测试时间不够,你会如何处理?

工作中经常会遇到测试时间不够充分的情况,当测试时间不足的情况下,如何做到不延误测试进度,又能保证测试质量? 1、根据测试目标和需求,确定测试的优先级,首先测试最重要和核心的功能和场景。 确保关键功能…

一文搞懂时间序列ARIMA模型

文章目录 1 ARIMA的定义2 差分(differencing)2.1 Order:差分的阶数2.2 Lag:差分的滞后2.3 滞后运算/滞后算子/延迟算子2.4 关于差分的两个误解 3 ARIMA的平稳性4 ACF与PACF5 时序模型的选择与评估5.1 超参数p、q、d的确定5.2 时间序列的评估指标 1 ARIMA…

【网络安全-信息收集】网络安全之信息收集和信息收集工具讲解(提供工具)

工具下载百度网盘链接(包含所有用到的工具): 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.…

Canvas--》使用Canvas完成基本绘图

&#x1f31f;Canvas介绍 <canvas>是一个可以使用脚本 (通常为javaScript) 来绘制图形的HTML元素。例如&#xff0c;它可以用于绘制图表、制作图片构图或者制作简单的动画。如上面效果示例就是使用 <canvas> 来实现示例&#xff0c;后续将一步步实现上面效果。 C…

2023-10-06 LeetCode每日一题(买卖股票的最佳时机含手续费)

2023-10-06每日一题 一、题目编号 714. 买卖股票的最佳时机含手续费二、题目链接 点击跳转到题目位置 三、题目描述 给定一个整数数组 prices&#xff0c;其中 prices[i]表示第 i 天的股票价格 &#xff1b;整数 fee 代表了交易股票的手续费用。 你可以无限次地完成交易&…

C++树详解

树 树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。n0时称为空树。在任意一颗非空树中&#xff1a;①有且仅有一个特定的称为根&#xff08;Root&#xff09;的结点&#xff1b;②当n>1时&#xff0c;其余结点可分为m&#xff08…

HP 喷墨一体机 - “检查墨盒“指示灯闪烁,怎么办?

适用机型&#xff1a; HP PSC 1118、1218 、1318、1350、1406/1408、1508 故障现象&#xff1a; “检查墨盒”指示灯闪烁&#xff0c;“份数”显示的是英文字母“E”&#xff0c;代表 Error&#xff08;错误&#xff09;的意思。&#xff08;无复印份数显示 &#xff09; “检…

Solidity 合约漏洞,价值 38BNB 漏洞分析

Solidity 合约漏洞&#xff0c;价值 38BNB 漏洞分析 1. 漏洞简介 https://twitter.com/NumenAlert/status/1626447469361102850 https://twitter.com/bbbb/status/1626392605264351235 2. 相关地址或交易 攻击交易&#xff1a; https://bscscan.com/tx/0x146586f05a451313…

基于SSM+Vue的鲜花销售系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

16哈希表-基础操作

目录 哈希表 散列思想 哈希表的实现 简单示例 开胃菜&#xff1a;LeetCode之路——242. 有效的字母异位词 分析 哈希表 英文名字为Hash table&#xff0c;散列表的英文叫“Hash Table”&#xff0c;我们平时也叫它“哈希表”或者“Hash表”。 哈希表&#xff08;Hash Ta…

typescript 类型声明文件

typescript 类型声明文件概述 在今天几乎所有的JavaScript应用都会引入许多第三方库来完成任务需求。这些第三方库不管是否是用TS编写的&#xff0c;最终都要编译成JS代码&#xff0c;才能发布给开发者使用。6我们知道是TS提供了类型&#xff0c;才有了代码提示和类型保护等机…

盒子模型的基础

盒子模型 边框&#xff08;border&#xff09; border可以设置元素的边框&#xff0c;边框分成三部分&#xff0c;边框的&#xff08;粗细&#xff09;边框的样式&#xff0c;边框的颜色 <style>div {width: 100px;height: 100px;border-width: 200;border-style: 边框…