关于系统瓶颈的面试问题
求教一下各位大佬,
面试老是被问到系统瓶颈的问题,如果现在正常运行的系统,要改造提高 10(甚至 100)倍的 qps,会遇到哪些问题、瓶颈?
关于这类的问题,应该如何去分析和描述?
求教一下各位大佬,
面试老是被问到系统瓶颈的问题,如果现在正常运行的系统,要改造提高 10(甚至 100)倍的 qps,会遇到哪些问题、瓶颈?
关于这类的问题,应该如何去分析和描述?
问 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 的存储体系结构来分析,或者从网络层去分析,就差不多了
高并发读从来都不是问题,如果不要求强一致性,加副本 配合 分片路由算法,没有什么问题是加机器不能解决的,
真正的问题是 高并发读的时候 如何解决写的问题,写的话 如何保障副本跟主节点的数据同步问题,并发读写才是要命的,这个时候要么 要求主从强一致性 那就是选 CP ,要么要求主从弱一致性,或者存在延迟同步的情况,此时就是 AP
#8 属于削峰问题,正确的方法是入消息队列,前端线程只做任务入消息队列,后端线程消费任务。前端 cpu 爆了的话只能横向扩展机器,由于只入消息队列,所以是可扩展的。
#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 分配到不同子集群。
看你现有的系统是哪种了,单体,前后端分离单体,SOA 还是微服务架构。
Redis 在 Get 小数据时,十万并发是勉强能支撑住的,如果是 Get 超大的对象,那可能就不行了。要增加十倍或者一百倍就有些骚操作了。用 Codis 或者官方提供的 Cluster 方案,将原来的数据拆分到多个 Redis 实例上。Redis 6 的多线程就是增加了接受请求的线程而已,不是以前的复用同一个线程,Set 和 Get 操作还是以前的单线程执行。一般现在都是 NUMA 架构,然后你可以进行绑核这种骚操作。
我建议你看下《微服务架构设计模式》这本书,X 、Y 、Z 轴扩展。应用水平扩展,拆分成服务,最好是无状态服务(当然这是最好的情况)水平扩展没什么障碍,Z 轴就是根据用户请求来进行绑定某些机器,例如粘性 session 。
其实还有前端的 Cache 、DNS 、登录的无状态化、动静分离、F5 、LVS 、一致性 Hash 、业务 /数据 /系统隔离、七种负载均衡办法,面试官应该想听到的是这些。
前面的各位都只是说到丢给 redis,但是单就 redis 也有击穿、穿透、雪崩各种说法,1s 几十万全丢给 redis,也只能说各位逮到个好玩意就往死里操,还是太暴力太浪费了
缓存和持久层其实都还是局部性原理的范畴,既然局部性原理,就再进一步,内存缓存,内存缓存这块跟 redis 放一块的话也有多种实现方式,比如:
1. 热点 key 的数据,定时或者发布订阅或者其他什么更新机制,服务节点 load 到自己的内存,请求来了直接返回自己内存缓存的
2. 同 key 访问加锁串行化,上一个请求回来后把结果带回来,其他等待锁的先检查是否有结果了,有了就直接拿结果、不落到 redis 了,相当于合并了到 redis 的请求。这个过程当然也可以结合或者改成内存缓存,比如内存的 1s 过期,内存没有、再 redis 、持久层之类的
高配点的机器,如果不是大 key 、value,几十万 qps 没啥压力,我自己的 i7 PC 测自己的 arpc 都能 40 多万 qps
常用方案是知识储备,分析解决问题是经验思路
1. 懂得太少,只会拿 QPS 做考点
2. 以 QPS 做切入点,和你探讨延伸各个方向的问题。
不要跨区读缓存;作好 replication ;缓存读不出来的时候不要立马更新缓存;随机 TTL 避免缓存雪崩。
最后我真不知道假设在单机上的意义何在。。。
其实全贴没人假设必须是在单机
我看第四 LZ 提到的,面试官已经假设到一台机器上。