关于系统瓶颈的面试问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
yyyfor
V2EX    程序员

关于系统瓶颈的面试问题

  •  2
     
  •   yyyfor 2021-02-01 21:09:43 +08:00 4977 次点击
    这是一个创建于 1717 天前的主题,其中的信息可能已经有所发展或是发生改变。

    求教一下各位大佬,

    面试老是被问到系统瓶颈的问题,如果现在正常运行的系统,要改造提高 10(甚至 100)倍的 qps,会遇到哪些问题、瓶颈?

    关于这类的问题,应该如何去分析和描述?

    45 条回复    2021-02-03 10:43:26 +08:00
    Kirsk
        1
    Kirsk  
       2021-02-02 00:38:31 +08:00 via Android
    鬼知道 具体问题具体分析 稀里糊涂的提问的人也不知道 走过场看面试者对不对自己胃口=你猜猜看我最爱吃什么
    yyyfor
        2
    yyyfor  
    OP
       2021-02-02 09:08:43 +08:00
    @Kirsk 昨天遇到的问题还算具体的。redis 每秒十万个 get 请求,会不会有问题? 如果没有问题,每秒请求量继续增加,问增加十倍或者一百倍,制约请求量增加的瓶颈因素是什么?
    Kirsk
        3
    Kirsk  
       2021-02-02 09:20:35 +08:00 via Android
    @yyyfor 假设是单纯承载容量 那就是集群横向扩展了
    yyyfor
        4
    yyyfor  
    OP
       2021-02-02 09:23:47 +08:00
    @Kirsk 面试官已经假设是在一台机器上了。我当时觉得应该从 cpu 、内存、io 去分析,但是具体分析的过程不知道如何表述比较准确
    Kirsk
        5
    Kirsk  
       2021-02-02 09:27:31 +08:00 via Android
    @yyyfor 有思路就行了 剩下是上手干活去解决的
    yyyfor
        6
    yyyfor  
    OP
       2021-02-02 09:49:14 +08:00
    @Kirsk 面试官想要具体的分析过程比如 cpu 、内存、io 哪个最有可能最先成为瓶颈? 然后卡了好久。感觉面试官想要从具体的数据量级去分析这个问题
    lewis89
        7
    lewis89  
       2021-02-02 10:02:00 +08:00   30
    @yyyfor #4

    问 redis,10 万请求的,这个时候制约 redis 的是单核心问题吧,因为单线程模型最多能打满一个 CPU,上集群就可以打满多个 CPU 实例,然后上集群就要涉及到热点 key 的问题,如果 100 万的 key get 操作 90%路由到一个 redis 实例上 ,那么又回到老问题,你的 hash 算法是否合理,真实的业务场景要不要解决热点 key 的问题,甚至可以在 redis 的路由上再开发一套 ip 随机 负载均衡的 分片,把 redis 的 get 操作路由到不同的 slave 节点上,此时多个 slave 副本又要考虑 CAP 的问题,是保证强一致性 所有的 slave 副本跟 master 节点保障 强一致性,那么就是 CP 系统 此时就是牺牲高可用,如果是要高可用,那就是异步复制 AP 系统,牺牲强一致性 来保障高可用 /高并发读,此时 master slave 副本肯定会存在数据不一致的问题,另外 redis 本身是写入内存的,如果你想通过写入硬盘 AOF 这种操作 将日志写入这些低速设备来保障最终一致性也是不可行的,因为低速设备(持久化)本身就会拖慢系统的响应速度。

    另外瓶颈的问题,首先可以从网络层分析,是否存在 TCP SYN 超时大量 socket 描述符没有被回收的情况,因为 C10K 的问题我模拟过,默认的 ubuntu 设置 存在 fd 描述符数量上限以及 socket 超时描述符未被回收的情况,如果是大量的短连接频繁创建销毁会触发这个问题。

    然后就是考虑 TCP 的拥塞问题,是否可以考虑使用快启动算法,来避免滑动窗口缩小,然后导致原本 2-3 个报文就搞定的事情,结果因为拥塞的问题 多好几个 ip 报文才发送出去。

    其余的瓶颈可以查看机械硬盘的 IO 看是否有大量的随机读写 拖慢了机械硬盘的速度,导致大量的 CPU 空闲,然后所有进程都在等待磁盘 IO

    内存方面可以排查页交换的问题,是否存在频繁的内存页从低速磁盘设备换出到物理内存,一般都是不建议服务器使用 swap 空间

    CPU 飙升的话,可以看是不是 C10K 大量线程被频繁唤醒,然后频繁进入内核态上下文切换带来的非常大的开销


    性能瓶颈的话其实还是从 X86 的存储体系结构来分析,或者从网络层去分析,就差不多了
    qwer666df
        8
    qwer666df  
       2021-02-02 10:07:41 +08:00
    @lewis89 #7 请教一下, 昨天有一个类似的问题, 关于一个活动用户发短信的场景 他意思一个线程只能处理 10w 个任务, 然后系统如果这个时候只开了 5 个线程, 也就是 50w 任务, 但是活动太过火爆了之后. 只能突然来了几百万的请求, 这时候怎么处理, 不考虑服务器手动加脚本的操作
    lewis89
        9
    lewis89  
       2021-02-02 10:12:29 +08:00
    @qwer666df #8 一开始就要考虑复杂均衡跟平滑扩容吧,而且这种任务没有顺序依赖,很容易分片解决就好了
    lewis89
        10
    lewis89  
       2021-02-02 10:18:49 +08:00
    @qwer666df #8 所有的并发, 如果没有数据竞争或者顺序依赖的情况,都可以通过平滑扩容以及负载均衡来解决,nginx 可以通过监测你服务的响应时间把 这些请求进行负载均衡处理的,实际上每台机器或者线程处理的数量不会太大

    高并发读从来都不是问题,如果不要求强一致性,加副本 配合 分片路由算法,没有什么问题是加机器不能解决的,
    真正的问题是 高并发读的时候 如何解决写的问题,写的话 如何保障副本跟主节点的数据同步问题,并发读写才是要命的,这个时候要么 要求主从强一致性 那就是选 CP ,要么要求主从弱一致性,或者存在延迟同步的情况,此时就是 AP
    yyyfor
        11
    yyyfor  
    OP
       2021-02-02 10:24:08 +08:00
    @lewis89 受教了,谢谢老哥
    qwer666df
        12
    qwer666df  
       2021-02-02 10:30:24 +08:00
    @lewis89 #10 如果是单机咋是不是就没办法了 昨天没有想过加机器的办法.... 我以为他是让我在单机上面考虑
    lewis89
        13
    lewis89  
       2021-02-02 10:34:11 +08:00
    @yyyfor #6 另外 Redis 本身 AOF 也是会丢失数据的,如果每一个写操作 都 fsync 到硬盘,那么每次 redis 的写操作就退化到磁盘的写入速度了,虽然内核文件系统会 buffer 你的写操作,然后合并成连续写 来提升性能,但是也顶不住你每次写操作都 fsync 写入硬盘的操作,毕竟磁盘的磁头 寻道,要从外面的圈移动到里面的圈,从寻道的物理角度来讲,他的速度是很非常慢的,所以机械硬盘只适合连续读写,在 4K 随机读写的场景下,机械硬盘就是个渣渣,被 SSD nvme 硬盘吊打的
    lewis89
        14
    lewis89  
       2021-02-02 10:38:17 +08:00
    @qwer666df #12 不可能单机的,单机 CPU 都打满了,你再怎么优化都没用,通常来讲,所有的服务都是无状态的,服务本身不存储任何状态,这样高峰的时候,我们可以通过扩展集群实例数量来提升吞吐量,但是大部分互联网的业务其实瓶颈都在数据库,所以一般都会采取 水平拆分的操作 来提升写的并发能力,因为写的话 会分片写入多个 MySQL 实例,此时单机写入的压力就会降低,总体的写入性能就会提升。
    qwer666df
        15
    qwer666df  
       2021-02-02 10:40:09 +08:00
    @lewis89 #14 受教了, 现在业务代码写的多, 还真不懂怎么 "扩展集群实例数量", 谢谢老哥
    lewis89
        16
    lewis89  
       2021-02-02 10:43:57 +08:00 via iPhone
    @qwer666df 扩展集群实例很简单的,一开始考虑好怎么负载均衡 然后用 k8s 加实例就好了,现在基本上这些都是运维做的,但是程序员要考虑负载均衡的算法,避免单个实例负载很大的情况
    lewis89
        17
    lewis89  
       2021-02-02 10:49:24 +08:00
    @qwer666df #15 要随时能扩充服务实例,首先还是要考虑 服务的无状态化,例如以前单机的 session 就不能用了,session 得存放到 redis 里面,不然你扩充的实例 不知道 session 在哪里,用 token 令牌的现在也只能存到 redis 里面去,所有的服务本身不保存任何状态,这样就可以平滑的加机器 减少机器的数量.. 当然这种集群扩充减少大多只能解决 CPU 密集型问题,对于瓶颈在数据库 IO 的问题 没法解决
    liuxu
        18
    liuxu  
       2021-02-02 10:58:59 +08:00
    @qwer666df
    @lewis89

    #8 属于削峰问题,正确的方法是入消息队列,前端线程只做任务入消息队列,后端线程消费任务。前端 cpu 爆了的话只能横向扩展机器,由于只入消息队列,所以是可扩展的。
    lewis89
        19
    lewis89  
       2021-02-02 11:14:46 +08:00
    @liuxu #18 如果我不削峰呢?我就要实时性,你这边 5 个线程,可能人家发送短信的 IO 可能根本都没打满,削峰只有 IO 被打满的情况下 不得已才考虑的一种情况,而且代码的主流程 是没法削峰的,发优惠券发金豆这种你削峰就削峰了,用户不会在意到账的及时性,像饿了么这种下订单的主流程,你怎么削峰?进消息队列?人家几秒钟的等待或者看到 提示错误就准备打开美团外卖了。
    lewis89
        20
    lewis89  
       2021-02-02 11:17:37 +08:00
    @liuxu #18 另外饿了么下订单这种主流程 只能做分表去提升并发写的能力,然后做冷热分离去提升读的能力,否则用户一看,订单没下成功,饿肚子了 分分钟就打开你对手美团的 APP 去下单了
    murmur
        21
    murmur  
       2021-02-02 11:26:03 +08:00
    全年提升 100 倍 qps 也就阿里腾讯能有这个级别
    其余的第一时间考虑不是错峰么,能错峰为啥要全年 qps 拉这么满
    最典型的例子,12306 优化了多少年,最终不还是靠错峰解决的问题,现在的双十一 双十二也都是错峰抢购
    yuedingwangji
        22
    yuedingwangji  
       2021-02-02 11:34:18 +08:00
    @lewis89 大佬呀
    liuxu
        23
    liuxu  
       2021-02-02 11:51:30 +08:00   1
    @lewis89

    #7 我补充一点,不知道对不对

    限制单机,100k 的 get 。
    由于 redis 从内存读,所以内存没有限制。
    主要是 cpu,假如单机 cpu 不够的话此问题无解,除非升级 cpu 。
    带宽问题需要保证,一个 get 10 字节,100k * 10 = 1MB/s = 10Mbps,100 个字节就需要 100Mbps 。
    linux 可使用端口一般默认为 32768 - 60000 之间,可以通过 sysctl(/etc/sysctl.conf)修改 net.ipv4.ip_local_port_range 增加更大范围,1024-65535 。但这样会存储一个问题,假如 3306 被 redis 分配掉,你再想启动 mysql,只能手动释放这个端口,然后启动 mysql 。这里只是举例,这种负载情况自然不建议单机起多服务。
    由于大量短连接会造成系统大量 TIME_WAIT 的端口,导致端口不可用,可以通过 sysctl(/etc/sysctl.conf)的 net.ipv4.tcp_tw_reuse 来快速重用。
    大量链接下系统 nofile 会快速占用,debian 系默认通常为 1024,通过修改 ulimit(/etc/security/limits.conf )的 nofile 解决,需要注意的是它是用户的 pam 限制,需要给启动 redis 的用户增加此选项,可直接修改全局文件全部增加即可。
    关于 redis 磁盘落地问题,使用三星的 1T pcie 4.0 接口的 nvme ssd,顺序读写基本在 3GB/s 以上,随机 4k 读写可达到 500k 以上,是读和写,感谢时代。
    网络拥塞问题,debian 系可以直接修改 /etc/sysctl.conf 文件的 net.core.default_qdisc=fq 和 net.ipv4.tcp_congestion_cOntrol=bbr 来抢占发包。rhel 系自行升级内核到 4.10 以上,我说的就是辣鸡 centos 。
    由于现在 aio 一般使用 epoll,C10K 导致大量上下文切换,可以使用最新 linux 内核 5.1 以上,使用 io_uring,根据有关评测,系统调用只有 epoll 的 1/10 。


    同时回答楼主,扩展到 10 倍或者 100 。做横向集群扩展的时候,压力在 LB 的 hash 均匀分配和性能上。同时可拆分业务,通过 LB 或者 DNS 分配到不同子集群。
    liuxu
        24
    liuxu  
       2021-02-02 11:56:19 +08:00
    @lewis89 你不用发这么多问号,不同业务有不同的处理办法。#8 的业务是发送短信,短信任务入队列削峰是正确的方式,现代短信发送一遍是 http 做接口调用,不仅有单机 100k 接收请求问题,还有单机 100k 发送 http 请求问题。
    lewis89
        25
    lewis89  
       2021-02-02 11:58:56 +08:00
    @liuxu #23 关键人家只有 5 个线程,并发能力只有 5..
    lewis8
        26
    lewis89  
       2021-02-02 12:00:11 +08:00
    @liuxu #23 5 个线程并发能打垮的系统,我还没见过 .. 我的垃圾树莓派 pi 都能 web 读并发到 100 以上
    liuxu
        27
    liuxu  
       2021-02-02 12:03:33 +08:00
    @lewis89 根据你说的饿了么订单这类及时任务,拆分业务,分表,是唯一选择。可以从 DNS,LB,业务才分,hash 分表,数据库集群解决,我没说有问题。
    liuxu
        28
    liuxu  
       2021-02-02 12:05:53 +08:00
    @lewis89 线程和并发不一定是 1 对 1 关系,同步任务是 1 对 1,现代 aio 下,协程 epoll 可以做到单线程多并发。
    togou
        29
    togou  
       2021-02-02 12:25:29 +08:00
    看傻了
    lewis89
        30
    lewis89  
       2021-02-02 12:54:04 +08:00
    @liuxu #22 另外 bbr 这种快启动也不是万能的,最好的办法还是根据包的大小来设置窗口跟窗口扩大缩小的策略,不过大部分场景 基本上都是小包..
    lewis89
        31
    lewis89  
       2021-02-02 13:06:34 +08:00
    @liuxu #27 有几个人会自己用 非阻塞式 IO 配合 epoll 关键字 自己手写多路复用,而且还要考虑写的 buffer 满了一大堆问题,另外用异步的更少见,有的异步甚至就是用线程池模拟出来的,一般默认 5 个线程就是 5 个阻塞式的 IO
    liuxu
        32
    liuxu  
       2021-02-02 13:47:39 +08:00   1
    @lewis89 bbr 当让不是万能的,满负荷下 bbr 还可能降低发包能力,但不至于自己设置窗口扩大缩小的策略吧,能改窗口,又觉得没几个人能手写 epoll,看来你是写 C/C++,做驱动开发的吧。应用层开发不会动 tcp 协议层,现代所有业务开发语言都支持异步和协程,很简单的语法糖。你说的手写异步模型超纲了,再说下去我就要开始改电路,处理 INT 中断,做真异步了,告辞
    young1lin
        33
    young1lin  
       2021-02-02 13:49:39 +08:00
    我做个补充。

    看你现有的系统是哪种了,单体,前后端分离单体,SOA 还是微服务架构。

    Redis 在 Get 小数据时,十万并发是勉强能支撑住的,如果是 Get 超大的对象,那可能就不行了。要增加十倍或者一百倍就有些骚操作了。用 Codis 或者官方提供的 Cluster 方案,将原来的数据拆分到多个 Redis 实例上。Redis 6 的多线程就是增加了接受请求的线程而已,不是以前的复用同一个线程,Set 和 Get 操作还是以前的单线程执行。一般现在都是 NUMA 架构,然后你可以进行绑核这种骚操作。

    我建议你看下《微服务架构设计模式》这本书,X 、Y 、Z 轴扩展。应用水平扩展,拆分成服务,最好是无状态服务(当然这是最好的情况)水平扩展没什么障碍,Z 轴就是根据用户请求来进行绑定某些机器,例如粘性 session 。

    其实还有前端的 Cache 、DNS 、登录的无状态化、动静分离、F5 、LVS 、一致性 Hash 、业务 /数据 /系统隔离、七种负载均衡办法,面试官应该想听到的是这些。
    lewis89
        34
    lewis89  
       2021-02-02 14:36:46 +08:00
    @young1lin #32 ryzen 的服务器版本还没流行吧,怎么 NUMA 已经成为标配了.. 不过多个物理 CPU 的服务器 确实存在 NUMA 的问题..
    OldCarMan
        35
    OldCarMan  
       2021-02-02 14:54:04 +08:00
    不错不错,感觉看了场高端局交流比赛。
    Lemeng
        36
    Lemeng  
       2021-02-02 15:07:53 +08:00
    问到了
    pavelpiero
        37
    pavelpiero  
       2021-02-02 15:26:40 +08:00
    lesismal
        38
    lesismal  
       2021-02-02 16:31:59 +08:00
    真热闹,我也补充一些吧

    前面的各位都只是说到丢给 redis,但是单就 redis 也有击穿、穿透、雪崩各种说法,1s 几十万全丢给 redis,也只能说各位逮到个好玩意就往死里操,还是太暴力太浪费了

    缓存和持久层其实都还是局部性原理的范畴,既然局部性原理,就再进一步,内存缓存,内存缓存这块跟 redis 放一块的话也有多种实现方式,比如:
    1. 热点 key 的数据,定时或者发布订阅或者其他什么更新机制,服务节点 load 到自己的内存,请求来了直接返回自己内存缓存的
    2. 同 key 访问加锁串行化,上一个请求回来后把结果带回来,其他等待锁的先检查是否有结果了,有了就直接拿结果、不落到 redis 了,相当于合并了到 redis 的请求。这个过程当然也可以结合或者改成内存缓存,比如内存的 1s 过期,内存没有、再 redis 、持久层之类的

    高配点的机器,如果不是大 key 、value,几十万 qps 没啥压力,我自己的 i7 PC 测自己的 arpc 都能 40 多万 qps
    boobo
        39
    boobo  
       2021-02-02 17:54:16 +08:00
    mark 一下先...
    janxin
        40
    janxin  
       2021-02-02 18:46:46 +08:00
    这是思路问题,先看问题出在哪里然后根据问题对应解决。

    常用方案是知识储备,分析解决问题是经验思路
    razertory
        41
    razertory  
       2021-02-02 19:07:38 +08:00
    首先你的网卡要支持足够的带宽,在确认 Max 带宽的情况下,你要会计算这个能承载多少 IOPS 。QPS 只是一个非常片面的指标。一个系统要健康稳定高效运转需要从各个维度去监控。那种以提升 QPS 做问题的面试官通常两类

    1. 懂得太少,只会拿 QPS 做考点
    2. 以 QPS 做切入点,和你探讨延伸各个方向的问题。
    lewis89
        42
    lewis89  
       2021-02-02 19:22:02 +08:00
    @janxin #39 对的,确实是要基础知识储备足够,然后解决方案跟思路 只是一个顺延展开基础理论知识的过程
    cassyfar
        43
    cassyfar  
       2021-02-02 19:40:02 +08:00
    100 万 QPS 的项目做过,直接上云,master-slave 就能跑,注意的点就是,

    不要跨区读缓存;作好 replication ;缓存读不出来的时候不要立马更新缓存;随机 TTL 避免缓存雪崩。

    最后我真不知道假设在单机上的意义何在。。。
    liuxu
        44
    liuxu  
       2021-02-03 09:32:03 +08:00
    @cassyfar #43 100 万 qps,具体什么业务,还是你们被人 cc 了

    其实全贴没人假设必须是在单机
    cassyfar
        45
    cassyfar  
       2021-02-03 10:43:26 +08:00
    @liuxu 电商和聊天都有做过,基础架构。

    我看第四楼 LZ 提到的,面试官已经假设到一台机器上。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3512 人在线 &nbs; 最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 10:33 PVG 18:33 LAX 03:33 JFK 06:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86