fu-ture

导航

性能测试

性能测试类型:常见有负载测试、压力测试,并发测试。

1、负载测试(可置性测试)
定义:在被测系统上不断增加压力,直到性能指标(如响应时间)超过预期指标或者某种资源使用已经达到饱和状态。可以找到系统的处理极限,为系统调优提供数据

特点:

1):该方法主要目的是找到系统处理能力的极限

2):该方法在给定的测试环境下进行,通常需要考虑被测系统的业务压力量和典型场景

3):该方法一般用来了解系统的性能容量,或者是配合性能调优来使用

性能容量:系统在保证一定响应时间的情况下能够允许多少并发用户的访问

2、压力测试
定义:系统在一定饱和状态下,例如CPU、内存等饱和情况下,系统能够处理的会话能力,以及系统是否会出现错误

特点:

1)该方法的主要目的是检查系统处于压力情况下是应用的性能表现

该方法通过增加访问压力,使系统资源使用保持在一定水平,检验此时应用的表现,重点在于有无出错信息产生,系统对应用的响应时间等

2)该方法一般通过模拟负载等方法,使得系统的资源使用达到较高的水平

3、验收性能测试
定义:特定条件下验证系统的能力状况

特点:

1)该方法主要目的是验证系统是否具有系统宣称的能力。

方法包括:确定用户场景,给出需要关注的性能指标,测试执行,测试分析几个步骤

2)该方法需要事先了解被测系统的典型场景,并具有确定的性能目标

3)这种方法要求在已确定的环境下进行

4、配置测试
定义:通过对被测系统软硬件环境的调整,了解各种不同环境对系统性能影响的程度,从而找到系统各项资源的最优分配原则

特点:

1)该方法主要目的是了解各种不同因素对系统系能影响的程度,从而判断出最值得进行的调优操作

2)该方法一般在对系统性能状况有初步了解后进行

需要在确定的环境、操作步骤和压力条件下进行

3)该方法一般用于性能调优和规划能力

5、并发测试
定义:模拟多用户并发访问同一个应用、模块或者数据记录时是否存在死锁或者其他性能问题

特点:

1)该方法主要目的是发现系统中可能存在的并发访问时的问题

2)该方法主要关注系统中可能存在的并发问题。比如:内存泄漏、线程锁和资源争用等问题

3)该方法可以在开发的各个阶段使用,需要相关的测试工具的配合和支持

常用工具:商业软件loadrunner:功能完整强大,内存占用大,需要收费

开源工具jmeter:开源免费,自由,操作较简单,能辅助完成日常的一些测试工作

6、可靠性测试
定义:给系统施加一定的业务压力,让其持续运行一段时间,测试在这种条件下能否稳定运行

特点:

1)该方法的主要目的是验证系统是否支持长期稳定的运行

2)该方法需要在压力下持续一段时间的运行

3)测试过程中需要关注系统的运行情况

比如:内存使用或者其他资源的使用以及响应时间有无明显变化

7、失效恢复测试
针对有多余备份和负载均衡的系统设计

定义:检测如果系统局部发生故障,系统能否继续使用

特点:

1)该方法主要目的是验证局部故障下系统能否继续使用

2)该方法需要指出:问题发生时“能支持多少用户访问”和“采取何种应急措施”

一般只有对系统持续运行能力有明确指标的系统才需要该类型测试

上述几种类型是针对书中的知识点的归纳,下面列出自己对并发,负载,压力的理解。。。。。。

负载测试:通过不断加压使系统达到瓶颈,为调优提供参考数据

8.压力测试:

1)稳定性压力测试:在不同的给定的条件下(比如内存的使用,一定时间段内有多少请求等),系统表现出来的处理,反应能力(这里会考虑系统的容错能力,恢复能力)

2)破坏性压力测试:不断加压,直至系统崩溃,挂掉,来得出系统的最大承受能力在哪儿

并发测试:简单理解就是业务场景短时间内有大量的请求需要处理,一般出现在登陆或者某些比较重要的模块

9.常用的性能指标
1、QPS(Queries Per Second)
概念:服务器每秒处理查询次数,是一台服务器每秒能够处理的查询次数。用户发起查询请求到服务器做出响应这算一次,一秒内用户完成了50次查询请求,那此时服务器QPS就是50。

2、TPS (Transactions Per Second)
概念:服务器每秒处理的事务数,一个事物是用户发起查询请求到服务器做出响应这算一次。纳尼?这难道不是QPS的概念吗?划重点,这里就要说清楚一个概念了,在针对单接口,TPS可以认为是等价于QPS的,如访问order.html这个页面而言,是一个TPS。而访问order.html页面可能请求了3此服务器(如调用了css、js、order接口),这实际就算产生了三个QPS
所以,总结下就是,在针对单接口的时候TPS = QPS ,否则QPS就要看实际的请求次数了。

3、RT(Res(onse Time)
概念:响应实际,就是从客户端请求发起到服务器响应结果的时间。RT这个参数是系统最重要的指标之一,它的大小直接反应了当前系统的响应状态。基本和咱们用户体验息息相关,现在好一点监控系统一般都有三个RT,即平均、最大、最小。
一般系统RT 100ms 以内是比较正常的,300ms 勉强可以接受,1s的话再加上一些其他的外因,给用户的体验就是实实在在的不爽了。

4、并发数
概念:系统能同时处理的请求的数量,很多人经常会把并发数和TPS理解混淆。举例,请求一个index.html 页面,客户端发起了三个请求(css、js、index接口),那么此时TPS =1 、QPS =3 、并发数 3。
SO,计算公式 :QPS=并发数/RT || 并发数=QPS*RT

5、吞吐量(Throughput)
概念:每秒承受的用户访问量,吞吐量(系统能承受多少压力)和当前请求对CPU消耗、内存、IO使用等等紧密相关。单个请求消耗越高,系统吞吐量越低,反之越高。
一个系统的吞吐量和其TPS 、QPS、并发数息息相关,每个系统针对这些值都有一个相对极限值,只要其中某一个达到最大,系统的吞吐量也就到达极限了。如此时压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,各种资源切换等等的消耗导致系统性能下降。

关系:
所以,理解上面几个关系后,就可以推算出:
QPS(TPS)= 并发数/平均响应时间

6、PV(Page View)
概念:即每个页面的浏览次数,用户每次刷新就算一次。

7、UV(Unique Visitor)
概念:独立访客数,每天访问的用户数,此数据需要根据用户唯一标识进行去重。

8、Load(系统负载)
概念:此数据指的是Linux系统的负载情况,也就是咱们平时所用Top命令时,最上面显示的数据信息( load average: 0.1, 0.2, 0.5)。此时会显示1分钟、5分钟、15分钟的系统平均Load,很显然load average 的值越低,你的系统负荷越小。
简单的说下这个值应该怎么看,如果你是单核cpu,那此值为1的时候就是系统已经满负荷状态了,需要你马上去解决。但实际经验告诉我们,当系统负荷持续大于0.7的时候(也就是70%),就需要你马上来解决问题了,防止进一步恶化。
为什么需要三个值 load average: 0.1, 0.2, 0.5,其实就是给你个参考。比如只有1分钟的是1,其他俩都是0.1,这表明只是临时突发的现象,问题不大。如果15分钟内,系统负荷都是1或大于1,那表明问题持续存在啊。所以你应该主要观察15分钟的系统负荷。

10.高并发场景下数据库怎么优化处理

一、分库分表

(1)为什么要分库分表

随着系统访问量的增加,QPS越来越高,数据库磁盘容量不断增加,一般数据库服务器的QPS在800-1200的时候性能最佳,当超过2000的时候sql就会变得很慢并且很容易被请求打死,而单表数据量过大也会导致数据库执行sql很慢,为了应付这种场景产生了分库分表这种思想和技术。分表就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。可以按照某一个维度来进行分表(例如按照用户id来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了)。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在200万以内。分库是什么?一般来说一个数据库服务器最多支撑到并发2000,一定要扩容了,而且一个健康的单库并发值你最好保持在每秒1000左右,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。

(2)分库分表中间件

常见的分布分表中间件有:cobar、TDDL、atlas、sharding-jdbc、mycat。它们可以分为client层和proxy方案。client方案的的优点在于不用部署,运维成本很低,但是如果要升级什么的得重新升级版本再发布,各个服务之间都需要耦合client依赖;proxy方案优点是对个各个服务都是透明的,如果需要升级什么的直接在中间件搞就可以了,但是得需要专门去部署运维。

cobar:阿里b2b团队开发和开源的,属于proxy层方案。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库join和分页等操作。

TDDL:淘宝团队开发的,属于client层方案。不支持join、多表查询等语法,就是基本的crud语法是ok,但是支持读写分离。目前使用的也不多,因为还依赖淘宝的diamond配置管理系统。

atlas:360开源的,属于proxy层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在5年前了。所以,现在用的公司基本也很少了。

sharding-jdbc:当当开源的,属于client层方案。确实之前用的还比较多一些,因为SQL语法支持也比较多,没有太多限制,而且目前推出到了2.0版本,支持分库分表、读写分离、分布式id生成、柔性事务(最大努力送达型事务、TCC事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从2017年一直到现在,是不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。

mycat:基于cobar改造的,属于proxy层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于sharding jdbc来说,年轻一些,经历的锤炼少一些。

(3)如何对数据库进行垂直拆分或者水平拆分?

水平拆分就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来抗更高的并发,还有就是用多个库的存储容量来进行扩容。垂直拆分就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表里去,然后将较多的访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。有两种分库分表的方式,一种是按照range来分,就是每个库一段连续的数据,这个一般是按比如时间范围来的,但是这种一般较少用,因为很容易产生热点问题,大量的流量都打在最新的数据上了;另外一种是按照某个字段hash一下均匀分散,这个较为常用。range来分,好处在于说,后面扩容的时候,就很容易,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了;缺点,但是大部分的请求,都是访问最新的数据。实际生产用range,要看场景,你的用户不是仅仅访问最新的数据,而是均匀的访问现在的数据以及历史的数据。hash分法,好处在于说,可以平均分配没给库的数据量和请求压力;坏处在于说扩容起来比较麻烦,会有一个数据迁移的这么一个过程。

二、如何让未分库分表的系统动态迁移到分库分表

(1)停机迁移方案

停机迁移是最简单的方案,思路很简单,通知用户系统需要升级维护时间,然后拒绝用户请求,

首先建好新的分库分表后的数据库,然后写一个导数据的程序把原先数据库里面的数据入到新库里面,在导完数据后,系统添加分库分表的配置然后重启系统即可。

这种方案现在不怎么用了,因为需要停机维护,况且数据量大了会导致停机时间很长,所以说很僵硬。

(2)双写迁移方案

双写迁移方案是现在常用的一种迁移方案,思路就是平滑上线一个新服务,把之前所有写库的地方,增删改操作,都除了对老库增删改,都加上对新库的增删改,这就是所谓双写,同时写俩库,老库和新库,读库还是读老库,然后在平滑的下线在之前直接走老库的老服务。然后新服务替换老服务后,由于新库数据差太远,用之前说的导数工具,跑起来读老库数据写新库,写的时候要根据updateTime这类字段判断这条数据最后修改的时间,除非是读出来的数据在新库里没有,或者是比新库的数据新才会写。在导完一轮数据后,有可能数据还是存在不一致,那么就程序自动做一轮校验,比对新老库每个表的每条数据,接着如果有不一样的,就针对那些不一样的,从老库读数据再次写。反复循环,直到两个库每个表的数据都完全一致为止。接着基于仅仅使用分库分表的最新代码,重新部署一次,不就仅仅基于分库分表在操作了么,还没有几个小时的停机时间,很稳。

三、动态扩容缩容分库分表方案

(1)停机扩容

这个方案就跟停机迁移一样,步骤几乎一致,唯一的一点就是那个导数的工具,是把现有库表的数据抽出来慢慢倒入到新的库和表里去。但是最好别这么玩儿,有点不太靠谱,因为分库分表就说明数据量实在是太大了,可能多达几亿条,甚至几十亿,这么玩可能会出问题。

(2)优化后的方案

一开始上来就是32个库,每个库32个表,1024张表,这个分法,第一,基本上国内的互联网肯定都是够用了,第二,无论是并发支撑还是数据量支撑都没问题。开始的时候一个数据库服务器可以放多个数据库和表,在扩容或者缩容的时候直接迁移数据库到其他数据库服务器就可以了。例如使用如下步骤:

1、设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是32库 * 32表,对于大部分公司来说,可能几年都够了

2、路由的规则,orderId 模 32 = 库,orderId / 32 模 32 = 表

3、扩容的时候,申请增加更多的数据库服务器,装好mysql,倍数扩容,4台服务器,扩到8台服务器,16台服务器

4、由dba负责将原先数据库服务器的库,迁移到新的数据库服务器上去,很多工具,库迁移,比较便捷

5、我们这边就是修改一下配置,调整迁移的库所在数据库服务器的地址

6、重新发布系统,上线,原先的路由规则变都不用变,直接可以基于2倍的数据库服务器的资源,继续进行线上系统的提供服务。

每个库正常承载的写入并发量是1000,那么32个库就可以承载32 * 1000 = 32000的写并发,如果每个库承载1500的写并发,32 * 1500 = 48000的写并发,接近5万/s的写入并发,前面再加一个MQ,削峰,每秒写入MQ 8万条数据,每秒消费5万条数据。除非是国内排名非常靠前的这些公司,他们的最核心的系统的数据库,可能会出现几百台数据库的这么一个规模,128个库,256个库,512个库。

1024张表,假设每个表放500万数据,在MySQL里可以放50亿条数据,每秒的5万写并发,总共50亿条数据,对于国内大部分的互联网公司来说,其实一般来说都够了。

谈分库分表的扩容,第一次分库分表,就一次性给他分个够,32个库,1024张表,可能对大部分的中小型互联网公司来说,已经可以支撑好几年了,刚开始就是一个mysql服务器可能建了n个库,比如16个库。后面如果要拆分,就是不断在库和mysql服务器之间做迁移就可以了。然后系统配合改一下配置即可。这么搞,是不用开发人员写代码做数据迁移的,都交给dba来搞好了,但是dba确实是需要做一些库表迁移的工作,但是总比开发人员写代码,抽数据导数据来的效率高得多了。

哪怕是要减少库的数量,也很简单,其实说白了就是按倍数缩容就可以了,然后修改一下路由规则。

四、分库分表后,id主键如何生成?

在我们原先单库的时候可以基于数据库自增来生成主键id,但是在分库分表后,这样肯定是不行的,所以需要一个全局唯一的id来生成主键,以下是几种常见的解决方案。

(1)数据库自增id

这个方案就是每次要获取一个全局id,要向一个单库单表中插入一条无意义的数据来获取他的自增id,在拿到这个id后再在分库分表中使用。

这个方案用的人几乎没有,因为分库分表就是因为并法量或者数据量问题,用单库单表去生成主键id,效率很低,不能支持高并发。这个方案还有一个弊端就是生成的是连续的id,如果用于订单什么的可能会暴露商业机密,这就很尴尬。

(2)redis的INCR指令

Redis Incr 命令将 key 中储存的数字值增一,然后返回执行完命令的值,这个方案规避了基于数据库的并发问题,因为redis天然吞吐量高,而且纯内存操作,速度非常快,但是就是有一个弊端,生成的是有顺序的id,同上描述的数据库自增id一样在一些场景下不可用。

(3)UUID

uuid好处就是本地生成,不要基于数据库来了;不好之处就是,uuid太长了,作为主键性能太差了,不适合用于主键。

uuid适合的场景一般是如果你是要随机生成个什么文件名了,编号之类的,你可以用uuid,但是作为主键是不能用uuid的。

(4)snowflake算法

twitter开源的分布式id生成算法,就是把一个64位的long型的id,1个bit是不用的,用其中的41 bit作为毫秒数,用10 bit作为工作机器id,12 bit作为序列号,其中具体含义如下:

1 bit:不用,为啥呢?因为二进制里第一个bit为如果是1,那么都是负数,但是我们生成的id都是正数,所以第一个bit统一都是0

41 bit:表示的是时间戳,单位是毫秒。41 bit可以表示的数字多达2^41 - 1,也就是可以标识2 ^ 41 - 1个毫秒值,换算成年就是表示69年的时间。

10 bit:记录工作机器id,代表的是这个服务最多可以部署在2^10台机器上哪,也就是1024台机器。但是10 bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2 ^ 5个机房(32个机房),每个机房里可以代表2 ^ 5个机器(32台机器)。

12 bit:这个是用来记录同一个毫秒内产生的不同id,12 bit可以代表的最大正整数是2 ^ 12 - 1 = 4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id。

这个方案属于现在开发中最常用的方案,各方面来说都不错。

五、mysql的读写分离

(1)如何实现mysql的读写分离?

mysql的读写分离基于主从复制架构,我们可以搞一个主库,然后主库挂多个从库,然后我们单单写主库,然后主库会自动把数据同步到从库,然后读从库来访问数据。

(2)mysql主从复制的原理

主库将变更写binlog日志,然后从库连接到主库之后,从库有一个IO线程,将主库的binlog日志拷贝到自己本地,写入一个中继日志中。接着从库中有一个SQL线程会从中继日志读取binlog,然后执行binlog日志中的内容,也就是在自己本地再次执行一遍SQL,这样就可以保证自己跟主库的数据是一样的。

在这个过程中从库同步主库数据的过程是串行化的,也就是说主库上并行的操作,在从库上会串行执行。所以这就是一个非常重要的点了,由于从库从主库拷贝日志以及串行执行SQL的特点,在高并发场景下,从库的数据一定会比主库慢一些,是有延时的。所以经常出现,刚写入主库的数据可能是读不到的,要过几十毫秒,甚至几百毫秒才能读取到。

而且会存在一个问题,就是如果主库突然宕机,然后恰好数据还没同步到从库,那么有些数据可能在从库上是没有的,有些数据可能就丢失了。

所以mysql实际上在这一块有两个机制,一个是半同步复制,用来解决主库数据丢失问题;一个是并行复制,用来解决主从同步延时问题。

这个所谓半同步复制,semi-sync复制,指的就是主库写入binlog日志之后,就会将强制此时立即将数据同步到从库,从库将日志写入自己本地的relay log之后,接着会返回一个ack给主库,主库接收到至少一个从库的ack之后才会认为写操作完成了。所谓并行复制,指的是从库开启多个线程,并行读取relay log中不同库的日志,然后并行重放不同库的日志,这是库级别的并行。

(3)mysql的主从同步延时问题

可以通过配置一些参数来降低延迟,但不是根本解决主从同步的方法,这个延迟时间一般在写并发在1000的时候延迟时间一般是几毫秒,在2000的时候会有几十毫秒,在4000、6000、8000并发时一般会达到几秒。

所以一般说,可以采用如下方案:

1.分库,将一个主库拆分为4个主库,每个主库的写并发就500/s,此时主从延迟可以忽略不计

2.打开mysql支持的并行复制,多个库并行复制,如果说某个库的写入并发就是特别高,单库写 并发达到了2000/s,并行复制还是没意义。

3.重写代码,写代码的同学,要慎重,插入数据之后,直接就更新,不要查询。

4.如果确实是存在必须先插入,立马要求就查询到,然后立马就要反过来执行一些操作,对这个查询设置直连主库。不推荐这种方法,这么搞导致读写分离的意义就丧失了。

 

————————————————

 

posted on 2022-11-16 09:37  *非梦  阅读(193)  评论(0编辑  收藏  举报