V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abcbuzhiming
V2EX  ›  程序员

遇到真正的高并发问题了,特来求助

  abcbuzhiming · 2018-09-16 20:25:02 +08:00 · 34971 次点击
这是一个创建于 2253 天前的主题,其中的信息可能已经有所发展或是发生改变。
以前做的项目,要么服务器够多,要么访问量比较分散,一天虽然人多,但是都是不同时段。所以没遭遇到访问瓶颈。这次真刀真枪的需要进行一次单服优化,然后就发现单服性能不可思议的低。大致情况如下:
应用服务器是 Tomcat,在阿里云上的 4 核心的 xero,8GB 内存,10MB 带宽,技术实现是 spring mvc,并不是特别复杂的计算业务,说难听点仅仅是 crud,而且输出的是纯粹的 json,没有其它的静态文件之类的东西。然后 MySQL 数据库在和应用服务器同一区域的另外一台阿里云服务器上,类似的配置,4 核心,8GB 内存,用的是高效 SSD。
这样的两台(一组)服务器,能抗住多少并发呢,500 不到。。。

然后开始初步分析,发现一个问题,就是哪怕是单纯的数据库读业务,从浏览器请求到服务器,服务器从数据库读取完毕到返回给前端,最快也要 20-30ms 左右,稍微复杂点的数据结构就上 100ms 了,如果按这个计算,每秒每个线程的处理能力理论最快也就 50 个并发请求,4 核心的机器上,JVM 的线程池一般也就核心的 1.5-2 倍,顶多不到 10 个线程,这样一算,单服理论并发处理能力确实只有 500 不到。。。

我不死心于是回头去找以前的类似服务器做测试,结果发现性能是类似的,只不过当年是靠着服务器够多顶住了罢了。

我知道肯定会有人说,上缓存啊,我当然知道上缓存,我的困惑是,难道只有上缓存一条路,那些并发上 w 的都是靠缓存顶住的吗?不上缓存的话,我现在服务器的性能指标到底是否正常?
183 条回复    2018-09-17 19:59:04 +08:00
1  2  
kran
    101
kran  
   2018-09-17 00:31:06 +08:00 via iPhone
要不你算算请求 /响应体大小,看看带宽吞吐能力是不是到极限了
micean
    102
micean  
   2018-09-17 00:32:07 +08:00
@jokerlee

CompletableFutrue 其实和 stream 一样为了跑并发的……
异步性能最好的还是 callback hell 那样
java 的协程好像是 Quasar 的作者在做?但是 Quasar 真的慢……
cominghome
    103
cominghome  
   2018-09-17 00:49:06 +08:00
@ittianyu 真的是张嘴就来,几万并发到你这好像分分钟就灰飞烟灭了
des
    104
des  
   2018-09-17 00:49:36 +08:00 via Android
同 50 楼,感觉是 MySQL 那台服务器 io 不太好。
先看看是不是响应时间随并发数增长的,以及看看有没有超长响应时间的请求
blless
    105
blless  
   2018-09-17 01:37:36 +08:00 via Android
500 不高吧…我还以为 java 下高并发场景已经很普遍了呢…我们以前用 python+gevent 方案感觉都不止 500,而且 python 最大问题是 GIL 只能用单核,后来转 go 以后压测一下基本限制都是数据库瓶颈
ETiV
    106
ETiV  
   2018-09-17 04:09:19 +08:00 via iPhone
我觉得应该用数据说话,比较科学。
就好比你去看大夫,都要验血验尿、拿到指标才行。不看指标的可能是中医 -。-

如果能单一请求就能复现出 20-30ms 或者更长的响应时长的话,你单机做一下 Profiling 比较好…

或者,简单点儿的做法,你打点计时。
在关键步骤分别记下处理逻辑中此行代码的当前时间,
然后分别追加在 json 文件内部或者放 http response header 里,一并输出。
veelog
    107
veelog  
   2018-09-17 06:05:42 +08:00 via iPhone
以前也做过类似的分析,最后定位到瓶颈就在 mysql
veelog
    108
veelog  
   2018-09-17 06:12:19 +08:00 via iPhone
压测的时候可以监控下 cpu 内存,磁盘 io 网络 这些看看哪里占用高
bobuick
    109
bobuick  
   2018-09-17 06:28:06 +08:00
猜是猜不到的。压测+ profile 就知道了
yhvictor
    110
yhvictor  
   2018-09-17 07:05:51 +08:00
@sagaxu 没必要的,那么多线程也是阻塞
opengps
    111
opengps  
   2018-09-17 07:58:16 +08:00 via Android
查看一下硬盘队列吧。我看了目前题主的说法,依然认为出在硬盘 io 上
doggg
    112
doggg  
   2018-09-17 08:02:15 +08:00   ❤️ 1
500 不管是 QPS 还是 TPS 都是相当低的水平。
大一曾经试图压榨一台 1c1g 的腾讯云服务器,Tomcat 与 MySQL 同服务器的情况下 QPS 达到 3000,TPS 达到 2000。没有任何 Redis 缓存之类的。

服务器的性能短板很难定位,但可以自己试着排除一下:
带宽问题:CND,OSS 存静态文件,减少业务服务器带宽输出,Tomcat 也可以配置 Gzip。
池优化:Tomcat 的线程池必须配置,数据库的连接池请选择 Tomcat 或者 Tomcat JDBC pool 或者 HikariCP,这两种数据库连接池差距微乎其微,几乎可以忽略不计。包括一些参数优化。
MySQL 连接参数:cachePrepStmts,prepStmtCacheSize,prepStmtCacheSqlLimit,useServerPrepStmts。
磁盘 IO 问题:这个也得自己诊断一下。


如果业务很复杂,楼主所说的 500 并发,每一个请求需要好几个 SELECT 话,那就是 500*N,那 QPS 应该是没问题的,再花点精力也许可以稍微提高一下。
xuanbg
    113
xuanbg  
   2018-09-17 08:23:13 +08:00
根据楼主描述,似乎瓶颈就在数据库上,那么解决方案很简单:

1、换高性能 RDS,大概性能可以提升 10 倍。
2、优化 SQL,这个能提升多少不确定。
D3EP
    114
D3EP  
   2018-09-17 08:38:11 +08:00 via iPhone
@neoblackcap 现在大多数语言和大多数框架都是同步模型啊。除了 nodejs 这种天热异步的,其它语言都是同步模型占绝对主流。
D3EP
    115
D3EP  
   2018-09-17 08:43:02 +08:00 via iPhone
@neoblackcap 你说的第二种方案就是绝大多数框架的方案。关键问题是对于 MySQL 这种耗时操作的处理方案,是应该挂起来等待,还是请求后塞入事件队列。
D3EP
    116
D3EP  
   2018-09-17 08:46:08 +08:00 via iPhone
@xiaoshenke 人家说的是业务层面的同步异步模型。绝大多数还是同步写法。dubbo 也就是 io 的时候是异步的,后面的操作还是塞入线程池,业务逻辑还是同步写的。
D3EP
    117
D3EP  
   2018-09-17 08:47:52 +08:00 via iPhone
@blless Java 也有纯异步的 vertx,但用的人不多
alwayshere
    118
alwayshere  
   2018-09-17 08:48:40 +08:00
与其大量优化代码和 cache,真的不如省点时间加点钱堆硬件配置,过来人之谈
v2orz
    119
v2orz  
   2018-09-17 08:52:23 +08:00
应该是 tomcat 没配置好。调一下参数或者用 resin


不过“单纯的数据库读业务,从浏览器请求到服务器,服务器从数据库读取完毕到返回给前端,最快也要 20-30ms ”
肯定不正常
star7th
    120
star7th  
   2018-09-17 09:06:49 +08:00
合理使用缓存能让单机服务器的性能提高数倍,尤其是 web 服务,基本瓶颈都在数据库查询上。把结果缓存到内存中,跟每次从磁盘读,差别很大的。
jswh
    121
jswh  
   2018-09-17 09:19:19 +08:00
@abcbuzhiming 浏览器前端到服务器数据返回,其中包含了网络延时的。建议看看内网的查询数据。
zqyisasd
    122
zqyisasd  
   2018-09-17 09:26:59 +08:00
之前公司的 tomcat 线上并发只有 100 多一点,不敢说话了。
lscho
    123
lscho  
   2018-09-17 09:31:15 +08:00 via Android
@v2orz 有什么不正常?网络延迟不计算吗? 20 多 ms 的网络延迟已经很低了。。。又不是内网测试
lttzzlll
    124
lttzzlll  
   2018-09-17 09:32:09 +08:00 via Android
先确定是不是数据库的问题,查询的部分可以直接返回假的内容。单独测试一下查询语句耗时。
sagaxu
    125
sagaxu  
   2018-09-17 09:32:43 +08:00 via Android
@yhvictor tomcat 多开线程就是用来阻塞的,200 个线程都在阻塞等结果,谁来处理新的请求?如果只开 10 个线程,平均阻塞 1 秒的接口,qps 只有 10。
@micean 不用等 Java,Kotlin 协程下个版本正式发布。
lauix
    126
lauix  
   2018-09-17 09:46:47 +08:00
单线程服务 500 已经很不错了,大多数都是 DB 不行,可以考虑 缓存 异步。
codingKingKong
    127
codingKingKong  
   2018-09-17 10:12:37 +08:00
看一下 GC, 之前我有一次是因为频繁 gc, 导致的吞吐量下降
q397064399
    128
q397064399  
   2018-09-17 10:15:53 +08:00
@neoblackcap #85 说实话我还是看好 Go 这种用户态线程的做法,异步 IO 回调这种方法始终是令人难受的
tailf
    129
tailf  
   2018-09-17 10:21:27 +08:00
并发上万都是靠架构顶住的,单机 java+MySQL 500 这个数字已经很强了,换 PHP 估计也就 100。
Mush
    130
Mush  
   2018-09-17 10:23:39 +08:00   ❤️ 1
缓存大法好, 适当的加缓存能显著提高响应速度. 我们公司有个请求量比较大(6 千万一天)的服务, 用了 1.75 个虚拟 cpu 就够了, 平均响应时间小于 5ms. 因为业务逻辑很简单, 多数请求直接命中缓存了.
mars0prince
    131
mars0prince  
   2018-09-17 10:24:01 +08:00
差不多了,mysql 最简单的 select 每条平均执行时间也要 10ms
mars0prince
    132
mars0prince  
   2018-09-17 10:25:54 +08:00
优化方法无非就是加机器,加缓存,读写分离,分库分表
tanranran
    133
tanranran  
   2018-09-17 10:26:07 +08:00
@mars0prince #131 #131 10ms 有问题吧
9684xtpa
    134
9684xtpa  
   2018-09-17 10:32:07 +08:00
感觉是资源加载太多,贷款的瓶颈啊,你们公司的网站的数据是不是都在服务器里啊,图片、视频啥的没有走 CDN,如果是的话,用户加载图片视频网页都会占用带宽的啊。


单纯的 curd,用 springmvc 和 mysql 不会有这么多大的延迟,只要你保证你的 sql 性能没问题,框架用的都是主流框架,剩下的除非一些框架 bug,没了。

加宽带把
Allianzcortex
    135
Allianzcortex  
   2018-09-17 10:54:37 +08:00
@Mush 加缓存还需要改代码逻辑吧~如果能通过扩展硬件来解决的话感觉会简单诶
sorra
    136
sorra  
   2018-09-17 11:03:34 +08:00
@rogerchen 似乎多数人只想要个简单直接的答案,不愿意哪怕多分析一下
面试看到爱动脑的都想赶紧发 offer,然而人家也很抢手啊
dragonsky
    137
dragonsky  
   2018-09-17 11:05:19 +08:00 via Android
@9684xtpa 你的输入法好像暴露了什么(・∀・)
9684xtpa
    138
9684xtpa  
   2018-09-17 11:07:50 +08:00
@dragonsky #137 哈哈,我刚特地看了一下我的输入法,第一个是贷款,第二个才是带宽。。。。。。。。尴尬,说到贷款估计就是和房子有关系呗,生活不易啊
meetocean
    139
meetocean  
   2018-09-17 11:12:03 +08:00
找到原因,才能针对性的解决问题。
v2orz
    140
v2orz  
   2018-09-17 11:13:08 +08:00
@lscho #123
前面描述限制那么多,难道性能分析的时候会找一个网络延迟那么大的环境吗?除非阿里云傻了,同一可用区网络延迟能那么惨烈。但是按题主前面的回复,可以排除这个情况

基本查询耗费 20ms 以上这难道还能是正常情况啊?
Mush
    141
Mush  
   2018-09-17 11:34:21 +08:00
@Allianzcortex #135 需不需要改逻辑要看实际情况. 我是用的 Python, 写个缓存的装饰器就 ok 了. 拓展硬件也能解决问题, 但不还得多花钱么. 要多快好省地建设社会主义.
PazuLee
    142
PazuLee  
   2018-09-17 11:34:50 +08:00
插一句:
1:服务器从数据库读取完毕到返回给前端,最快也要 20-30ms 左右
2:稍微复杂点的数据结构就上 100ms 了
=============
建议看下 avg,最快可能对 QPS 评估意义不大;
看下简单和复杂情况下,avg 的差距是因为什么;如果是数据库就升级 DB,如果是服务器考虑扩容+优化配置;如果是网络就买带宽;或者是多个综合原因,按权重*成本,计算一下方案
Mrbird
    143
Mrbird  
   2018-09-17 11:40:51 +08:00
可以将接口改为异步,提高吞吐量
GoForce5500
    144
GoForce5500  
   2018-09-17 11:50:56 +08:00
带宽是 10Mb 而不是 10MB 吧?
相当于 1.2MB/s,1.2/500=0.0024MB
如果 JSON 较大,都很容易把带宽打满,何况中间还有其它步骤。
jason19659
    145
jason19659  
   2018-09-17 11:56:42 +08:00
感觉没啥问题吧。。
monsterj
    146
monsterj  
   2018-09-17 12:05:17 +08:00
@doggg 相当低?敢问你大一压榨的那次,接口业务复杂不?一个复杂的接口和一个简单的查询接口,并发量简直是两码事
exmario
    147
exmario  
   2018-09-17 12:11:30 +08:00
楼主你先直接把数据写死(绕过 mysql )做个测试看看吧,500 并发不管怎么看都算低
exmario
    148
exmario  
   2018-09-17 12:12:38 +08:00
纯静态数据随便上 w~
raysmond
    149
raysmond  
   2018-09-17 12:20:50 +08:00
建议先把所有数据缓存起来,测一把 qps ;全部走数据库,测一把 qps。
就知道缓存到底作用有多大了。
opengps
    150
opengps  
   2018-09-17 12:23:22 +08:00
@exmario 数据库 1k 块大小(单行数据带下),你写个 sql 循环读取试试, 读取可能会多一点
我之前项目因为压力在写不在读,所以只测的写入,阿里云高效云盘,写入是只有 400 行每秒,几个语句同时执行也顶多 500
lscho
    151
lscho  
   2018-09-17 12:28:01 +08:00 via Android
@v2orz 看题主说的什么。。。从浏览器到服务器。。。难道他在阿里云上的浏览器做的测试?第一反应是在本地的浏览器吧。。。这个测试方法本来就有问题。
CFM880
    152
CFM880  
   2018-09-17 12:52:28 +08:00
JProfiler 分析一下
abcbuzhiming
    153
abcbuzhiming  
OP
   2018-09-17 12:54:05 +08:00
@GoForce5500 对,就是 10Mb,也就是 10M 位,但是阿里云平台的监控表明,最高也才被吃掉了 5Mb 不到。所以带宽的问题,貌似不是当前问题
blless
    154
blless  
   2018-09-17 12:54:25 +08:00 via Android
以前写 c#用过线程池,线程数量到 100 左右 cpu 切换就感觉消耗很大了,但是 100 以内线程池感觉还是可以的。而且哪怕是阻塞队列 500tps 按楼主说法一个请求 20ms 也就 10 个线程… 500 并发真的不算高…
abcbuzhiming
    155
abcbuzhiming  
OP
   2018-09-17 12:57:07 +08:00
@lscho 我当然是在本地用浏览器的 debug 工具来测试和阿里云服务器之间交互的时间。那到底如何测试才是对的?或者说,本地浏览器和服务器交互的时间,其实在服务器看来是不算在计算时间里的?
lynnworld
    156
lynnworld  
   2018-09-17 13:00:39 +08:00
确认是 500 并发? avg 20ms 的话,qps 已经是 2.5w 了,单机不错了
danc
    157
danc  
   2018-09-17 13:25:20 +08:00
一台机器 500 并发已经很不错了呢。你得加机器了
iyaozhen
    158
iyaozhen  
   2018-09-17 13:28:39 +08:00
有些回复没看全,说实话,单机 500 并发实际算高的了。

一次请求耗时 20-30ms 我觉得没啥优化空间了。
一些建议:
1.「 JVM 的线程池一般也就核心的 1.5-2 倍,顶多不到 10 个线程」因为对 JAVA 不精通这个楼主感觉可以再测试下,高峰期的使用 dump 一下线程,看看是否都在工作,是否可以多开点线程
2. 机器是否达到瓶颈?看看 cpu、内存和带宽,还有富余的话是否可以多实例,前面挂个 nginx。
iyaozhen
    159
iyaozhen  
   2018-09-17 13:30:11 +08:00
@abcbuzhiming 你要测试服务器的时间最简单的就是 tomcat access log,打印一下耗时。
lscho
    160
lscho  
   2018-09-17 13:41:47 +08:00 via Android
@abcbuzhiming 看来你不了解压力测试。。如果是测性能,就要尽量减少网络和带宽的影响。。比如在阿里云同机房开一台测试。

如果你在本地测试,就要考虑网络和带宽的影响。。。比如你 ping 一下你的服务器,这个时间就是网络延迟,这个不能计算在内的。。。然后要考虑带宽,比如 1m 带宽,上行也就 120k 左右。。你如果有 120k 的数据,再怎么优化,也不可能低于 800ms 的。
lscho
    161
lscho  
   2018-09-17 13:46:01 +08:00 via Android
@iyaozhen 所以 20ms 的话,如果算上你电脑网络到机房的网络延迟,已经算是很低了,并且你离机房还比较近。。如果北京到广州,绝对不可能低于 30ms 的。

还有你说复杂点的数据结构就超过 100ms,这个根据你的描述,没法判定原因。你要先确定不是网络带宽原因,再去看是不是服务器或者数据库的问题。
liuxu
    162
liuxu  
   2018-09-17 13:49:03 +08:00
可以看看 112,133 楼的建议
exmario
    163
exmario  
   2018-09-17 14:11:50 +08:00
@abcbuzhiming 当然不能公网做性能测试,那会受带宽或网络限制,本地的数据测试才是性能测试,你做的那个叫综合访问测试,已经不是单性能问题了
exmario
    164
exmario  
   2018-09-17 14:15:00 +08:00
正确做法应该是申请 2 台同网段的阿里云服务器,然后 2 服务器之间内网 ip 互连测试
flight2006
    165
flight2006  
   2018-09-17 14:36:38 +08:00
没做特殊优化的话单机 500qps 到头了,优化还是有空间的,从数据库到 jvm gc 到 tomcat 线程池
wph95
    166
wph95  
   2018-09-17 14:46:00 +08:00
你这个 500 并发是 500 qps 吗, 感觉楼主信息给的不足。// 虽然感觉要么 RDS 瓶颈了(写的 sql 有问题),要么 Tomcat 调好
我建议找个 apm 软件装上,跑个几天收集些信息。(反正常见几家 APM 公司都提供免费试用)
sampeng
    167
sampeng  
   2018-09-17 15:23:03 +08:00
哦。阿里云是么?
突然想到。。阿里云有个压力测试服务。不知道现在是否免费。可以用他那个测。
doggg
    168
doggg  
   2018-09-17 15:33:30 +08:00
@monsterj 晚点肛我。我已经说明 500 如果是接口的并发量,如果每次请求都需要 N 次 SELECT 那性能应该没什么大问题,如果 500 并发=500SELECT 那绝对存在问题。我的回答已经表明这个疑问了咯。
fakeJas0n
    169
fakeJas0n  
   2018-09-17 15:36:45 +08:00
换 jetty 试试?
ala2008
    170
ala2008  
   2018-09-17 15:44:33 +08:00
我一直以为是单个 tomcat 的上限是几百左右啊
yhvictor
    171
yhvictor  
   2018-09-17 16:07:03 +08:00
@sagaxu 我的理解是再多线程,能同时做的事情也就是核心数。所以多开无益,开个 queue 放进去就好了。
wjygamedev
    172
wjygamedev  
   2018-09-17 16:14:53 +08:00
楼主:是否考虑到做缓存。
1. memcached.
2. Redis
看看怎么上缓存方便,在同步读取数据库并返回的地方,用缓存做个隔离带,并发应该有所提升。
不过有时间的话,还是简易,从入口到出口,把统计数据做一下,这样更能有的放矢。
wawehi
    173
wawehi  
   2018-09-17 16:28:17 +08:00
把数据库用云 DB 测试看看, VPS 不适合建 Mysql, IO 太低
linuxchild
    174
linuxchild  
   2018-09-17 16:31:01 +08:00
看看慢查询呗~
dudesun
    175
dudesun  
   2018-09-17 16:44:04 +08:00
数据库用高效云盘不合理,建议 ssd 云盘或者直接 RDS
abcbuzhiming
    176
abcbuzhiming  
OP
   2018-09-17 16:52:26 +08:00
@wawehi 我用的是阿里云 ECS 建立的 MySQL 服务器,ECS,高 IO 类型,SSD 硬盘
sagaxu
    177
sagaxu  
   2018-09-17 17:06:57 +08:00 via Android
@yhvictor 逻辑简单用 queue 没问题,复杂的时候,异步编程风格心智负担太重了。如果异步写起来轻松,就不会搞协程解放双手了。
jjx
    178
jjx  
   2018-09-17 17:13:52 +08:00
阿里云的 云 ssd 同本地 ssd 性能差距一倍以上
wawehi
    179
wawehi  
   2018-09-17 18:04:45 +08:00
@abcbuzhiming 直接用阿里云 DB, 自建的 DB 远比不上他们建的.
woostundy
    180
woostundy  
   2018-09-17 18:40:04 +08:00
CPU 都没吃满一定是连接数太少了,把服务线程池加大到 CPU 到 80+%
KingHL
    181
KingHL  
   2018-09-17 18:51:52 +08:00
我觉得同步变异步,或者加大线程数可以解决。
wmlhust
    182
wmlhust  
   2018-09-17 19:30:37 +08:00
感觉很多时间都浪费在等待数据库返回结果了啊,如果数据库不是瓶颈。那么增加线程数应该是管用的,更好的是同时换成异步的方式。
ml071987
    183
ml071987  
   2018-09-17 19:59:04 +08:00
说实话,单机 500 并发,没加 redis,已经很厉害了
1  2  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2617 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 26ms · UTC 04:54 · PVG 12:54 · LAX 20:54 · JFK 23:54
Developed with CodeLauncher
♥ Do have faith in what you're doing.