bingoohuang / blog

write blogs with issues
MIT License
177 stars 23 forks source link

TPS 那些事儿 #208

Open bingoohuang opened 3 years ago

bingoohuang commented 3 years ago

TPS 哪些事儿

TPS 具体是指什么?

TPS 的英文全称是 Transactions Per Second,即每秒事务数。例如,每秒钟签名数量,每秒钟加密数量,每秒钟生成订单的数量等。有时候,我们见到类似的 HPS (每秒点击数),QPS(每秒查询数),RPS(每秒请求数)等,我们可以理解成是 TPS 的事务映射成点击、查询、请求的更加具体的解释上,为了方便起见,以下我们都统称 TPS。

为了让大家有一个直观的概念,我这里从一篇 2019 年的博客Database Comparison - SQL vs. NoSQL (MySQL vs PostgreSQL vs Redis vs MongoDB)上,扒拉了一个图,如下:

image

从这张图中,我们可以看到,查询性能上,Redis 能达到 500 万左右 TPS ( 5000/0.001), MongoDB 达到 5000 万左右 TPS ,简直令人咂舌,而可怜的 MySQL/PostgreSQL,只有 500 TPS左右(5000/10)。那么问题来了,如果我们自己在本机压测一下,会达到类似效果吗?思考几秒钟先。对了,你就需要去仔细检查人家的测试环境,包括硬件环境、网络环境、软件环境、测试数据等等,可以说,我们几乎不可能测得类似的结果。

bingoohuang commented 2 years ago

TPS 那些事儿

网卡

  1. 千兆网卡的传输速率是1000 Mbps(bps 比特率,即位/秒),换算一下,是 125 MB/s
  2. 千兆网络一般都是全双工(vs 半双工),也就是上下行是并行工作的,也就是说上行下行同时能达到理论值 125 MB/s
  3. 2.5G 网卡具有高达 2500Mbps 的传输速率,是千兆网卡速度的 2.5 倍
  4. 带宽是千兆,无论下载还是上传 极限就是千兆,就像一条公路,就那么宽,无论你车是往那个方向开
  5. 要实现千兆网络,要路由器、交换机、网卡、带宽等所有硬件/指标都要达到千兆规格,实际上速度能达到 100MB/s 就不错了

TPS

  1. TPS:Transactions Per Second(每秒传输的事务处理个数),即服务器每秒处理的事务数
  2. 一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数
  3. TPS 是软件测试结果的测量单位。一般的,评价系统性能均以TPS来衡量。系统整体处理能力取决于处理能力最低模块的TPS值
  4. QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够处理的查询次数

RT(Response-time),响应时间

  1. 执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间
  2. RT 是一个系统最重要的指标之一,它的数值大小直接反应了系统的快慢。

并发数

  1. 并发数 是指系统同时能处理的请求数量,这个也是反应了系统的负载能力

吞吐量

TPS/RT的关系

  1. TPS = 并发数/平均响应时间
  2. 并发数 = QPS*平均响应时间

举例

我们通过一个实例来把上面几个概念串起来理解。按二八定律来看,如果每天 80% 的访问集中在 20% 的时间里,这 20% 时间就叫做峰值时间。

  1. 公式:( 总 PV 数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间TPS
  2. 机器:峰值时间 TPS / 单台机器 QPS = 需要的机器数量

例如:

  1. 每天 300w PV 的在单台机器上,这台机器需要多少QPS? ( 3000000 0.8 ) / (86400 0.2 ) = 139 (QPS)
  2. 如果一台机器的 TPS 是 58 ,需要几台机器来支持? 139 / 58 = 3

NGINX 的连接数概念

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

  1. NGINX平均每个连接物理内存占用约1k。多数内存都被内核TCP缓存占用。
  2. NGINX维持大量连接(少量活跃连接,本文中平均每秒活跃连接为总连接数的千分之一)占用很少CPU,上文仅为2%。
  3. 最好的优化就是不优化。整个测试除了提升文件数和连接数的这些硬限制外,没有任何参数调优,但仔细计算下就发现平均每个连接内存占用不到10k,远小于默认的缓存大小(net.ipv4.tcp_rmem = 4096 87380 4194304)和 (net.ipv4.tcp_wmem = 4096 16384 4194304)
  4. NGINX维持此类连接的主要瓶颈就是可用内存大小,我的2GB内存虚拟机其实可以支持15万长连接,只不过我物理机器没有内存再继续clone虚拟机客户端了:-(
  5. 虽然会遇到更多内核参数的限制,但大内存服务器支持100万连接是完全没问题的。

资源

  1. 性能测试:深入理解线程数,并发量,TPS,看这一篇就够了
bingoohuang commented 2 years ago

C10K 是 1 万 TPS 吗

C10K 就是 Client 10000 问题,即「在同时连接到服务器的客户端数量超过 10000 个的环境中,即便硬件性能足够, 依然无法正常提供服务」,简而言之,就是单机 1 万个并发连接问题。这个概念最早由 Dan Kegel 1999 年提出。C10K 就是单机同时处理 1 万个请求(并发 1 万连接)的问题。

现在我们早已经突破了 C10K 这个瓶颈,思路就是通过单个进程或线程服务于多个客户端请求,通过异步编程和事件触发机制替换轮训,IO 采用非阻塞的方式,减少不必要的性能损耗,等等。 底层的相关技术包括 epoll、kqueue、libevent 等,应用层面的解决方案包括 OpenResty、Golang、Node.js 等,比如 OpenResty 的介绍中是这么说的:

OpenResty 通过汇聚各种设计精良的 Nginx 模块,从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 C10K 乃至 C1000K 以上单机并发连接的高性能 Web 应用系统。

image

实际上,在大多数场景中,我们并不需要单机并发 1000 万的请求。通过调整系统架构,把这些请求分发到多台服务器中来处理,通常是更简单和更容易扩展的方案。

问题是,一些同学就记住了 C10K,是并发处理 1 万请求,把括弧中的"并发 1 万连接"给忽略了,把 Client 的 C 当成了 Concurrence 的 C,然后就慢慢与并发 1 万 TPS 画上了等号。然后就经常下意识的问,你们的系统能支撑住 10 万 TPS 么?搞得 10 万 TPS 是标配,没有 10 万 TPS 那系统就是做得太烂了似的感觉。

bingoohuang commented 2 years ago

空接口的意义

空接口的意义,在于提供了实际业务压测的在特定环境约束下的一个比较现实的基准。

很多网络应用程序,都提供一个 /status 的网络服务,返回例如 {"status":200,"message":"成功"} 的 JSON 响应报文,表明应用还在正常运行中。很多情况下,这个 /status 的接口实现,就是一个空接口,什么也不干,就是直接返回一个接口对象,例如, fastrest中的实现如下:

package fastrest

type Status struct{ DummyService }

func (p *Status) Process(*Context) (interface{}, error) {
    return &Rsp{Status: 200, Message: "成功"}, nil
}

我们使用 Berf 跑一下它的性能测试:

root@bx-PC:~/bingoohuang# ./berf :14142/status -c 26 -v -d30s
Berf benchmarking http://127.0.0.1:14142/status for 30s using 26 goroutine(s), 96 GoMaxProcs.
@Real-time charts is on http://127.0.0.1:28888

Summary:
  Elapsed                       30s
  Count/RPS      4489715 149652.214
    200          4489715 149652.214
  ReadWrite    208.316 196.345 Mbps
  Connections                    26

Statistics     Min       Mean     StdDev      Max
  Latency     31µs       163µs     136µs    5.598ms
  RPS       136749.48  149628.08  2858.46  153222.51

Latency Percentile:
  P50     P75    P90    P95    P99    P99.9   P99.99
  138µs  187µs  254µs  319µs  606µs  1.854ms  2.314ms

Latency Histogram:
  158µs    4337178  96.60%  ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  243µs     122233   2.72%  ■
  439µs      25696   0.57%
  1.051ms     3179   0.07%
  1.728ms      974   0.02%
  2.087ms      373   0.01%
  2.274ms       75   0.00%
  2.689ms        7   0.00%

image

可以看到,这个空接口的 TPS 能达到 15万,平均延时 163µs, P99 延时 606µs。

这时候小明同学就问,我这个啥业务也没有,测量这个空接口的意义何在呢?其实,它的意义非常重要:

空接口的意义,在于提供了实际业务压测的在特定环境约束下的一个比较现实的基准。(另一方面,空接口提供了框架底座的基本性能指标,可以帮助框架洗清”冤屈嫌疑“。)

一个实际的问题是,明明同学在自己的环境(比如他自己的个人电脑中)验证同样的空接口,可能达不到这个 15 万(应该说99%的情况下都达不到),例如,测得的结果是 8 万,那也不要气馁,无论 8 万还是 15 万,只是特定环境下的一个特定值而已,基准 的意义,仍然会发挥作用。

bingoohuang commented 2 years ago

一些常见中间件的直观 TPS 的测量

在测量之前,我们先在脑中做一下预估:

  1. MySQL 的读 TPS 在什么量级?千,万,十万,百万?
  2. Redis 的 读写呢?

以上只需要预估量级就行。

bingoohuang commented 2 years ago

Nginx 代理一下,会损失多少性能

bingoohuang commented 2 years ago

加大并发连接数,是否可以提升 TPS

image

实验步骤:

  1. 启动模拟 http 服务: ./httplive
  2. 启动第 1 轮 berf 压测:/berf :5003/status -ci 30:5s:30 -v -c 300,即每 5 秒增加 30 并发连接,达到 300 后,再每 5 秒减少 30 并发
  3. 启动第 2 轮 berf 压测: ./berf :5003/status -v -c 100 -d15s 持续使用 100 并发连接,压测15秒
  4. 启动第 3 轮 berf 压测: ./berf :5003/status -v -c 120 -d15s 持续使用 120 并发连接,压测15秒
  5. 启动第 4 轮 berf 压测: ./berf :5003/status -v -c 150 -d15s 持续使用 150 并发连接,压测15秒

直接使用 Berf 测试工具,我们可以看出:

  1. 在并发 120 个连接时,TPS 达到 90000 左右,平均延时 1.295 ms,P99 延时 6.453ms
  2. 增加到150 个连接时,TPS 达到 93000 左右,平均延时 1.584ms,P99 延时 7.891ms
  3. 再继续增加连接时,TPS 几乎不再增加,但是平均延时开始爬升,P99 爬升更厉害

因此,并发连接数不是越多越好,在一个合理值下,取得较大 TPS,以及较小延时,才是一个合理的压测并发连接数。