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

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

  abcbuzhiming · 62 天前 · 15653 次点击
这是一个创建于 62 天前的主题,其中的信息可能已经有所发展或是发生改变。
以前做的项目,要么服务器够多,要么访问量比较分散,一天虽然人多,但是都是不同时段。所以没遭遇到访问瓶颈。这次真刀真枪的需要进行一次单服优化,然后就发现单服性能不可思议的低。大致情况如下:
应用服务器是 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  
    101
kran   62 天前 via iPhone
要不你算算请求 /响应体大小,看看带宽吞吐能力是不是到极限了
    102
micean   62 天前
@jokerlee

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

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

或者,简单点儿的做法,你打点计时。
在关键步骤分别记下处理逻辑中此行代码的当前时间,
然后分别追加在 json 文件内部或者放 http response header 里,一并输出。
    107
veelog   61 天前 via iPhone
以前也做过类似的分析,最后定位到瓶颈就在 mysql
    108
veelog   61 天前 via iPhone
压测的时候可以监控下 cpu 内存,磁盘 io 网络 这些看看哪里占用高
    109
bobuick   61 天前
猜是猜不到的。压测+ profile 就知道了
    110
yhvictor   61 天前
@sagaxu 没必要的,那么多线程也是阻塞
    111
opengps   61 天前 via Android
查看一下硬盘队列吧。我看了目前题主的说法,依然认为出在硬盘 io 上
    112
doggg   61 天前   ♥ 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 应该是没问题的,再花点精力也许可以稍微提高一下。
    113
xuanbg   61 天前
根据楼主描述,似乎瓶颈就在数据库上,那么解决方案很简单:

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


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


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

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

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

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

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

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