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

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

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

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

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

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

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


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


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

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

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

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

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

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