这种开放性的问题,我在面试的时候经常会问,原因在于:
(1)相比于挖掘候选人简历中不同业务领域的项目,它不用费脑子,面试舒适性很高。
(2)相比于去问候选人一些特定技术上的八股文,它能听到各种花式回答,不枯燥且趣味性很高。
btw:当你一天经历了好多场面试,都快面吐了的时候,面试舒适性和趣味性尤其重要。
(3)最重要的一点是,这个问题可以跟候选人你来我往地交流很久,能充分地挖掘出候选人的真实能力。
那么,回答“高并发解决方案”这个问题,最重要的是什么呢?
其实,最重要的是别“答非所问”。
有超过50%的候选人,在回答这个问题的时候,都会提到“限流、熔断、降级”之类的。

我尼玛,这不是“高可用解决方案”吗?难道这就是传说中的“一招鲜吃遍天”不成?
我承认,“高并发、高性能、高可用”,三者之间存在一些剪不断理还乱的联系,但任何解决方案中,还是应该有它最聚焦对应的主目标的。
下面我们一起来梳理一下,有哪些真正意义上的“高并发解决方案”。

横向扩容
哈哈哈,不装逼直白地说,就是堆机器。应用服务器是瓶颈,那就堆应用服务器,数据库服务器是瓶颈的话,那就堆从库。
你一千的QPS,我三台应用服务器+一台数据库服务器,你两千QPS,我就六台应用服务器 + 一主一从两台数据库服务器。

这种方案虽然low b了一些,但它至少可以解决50%以上的场景问题,尤其是那种读多写少的场景。
但有一点需要注意,从数据库从库去读取数据,是有主从延迟的,所以对实时性要求比较高的读场景,如:在程序中的一个方法体内的“写后读”,尽量还是要走主库。
另外,如果从库过多,会导致主库的负载压力变大和主从延迟变高,这点也需要注意。
引入缓存
读多写少的高并发场景,另一个常见的方案就是加缓存,这种方案比横向扩容的方案更加复杂一些,因为它会涉及到缓存选型和数据一致性的问题。
缓存选型的话,基本上都会在类似于HashMap、Guava Cache类的本地缓存和Redis类的集中式缓存中进行选择。
我的建议是,95%的业务场景下,可以无脑选择Redis。
除非满足如下两种条件可以考虑本地缓存:
-
大key,且热key,网络可能会成为瓶颈。
-
基本上不会进行更改。
符合这种条件的,比如:在线教育场景下的课程体系对象的json串。
数据库和缓存的数据一致性问题,我看很多人还为此专门写了一篇文章,里面列举了好多种方案,什么先写后写、先删后写,先删后写再删,还有什么同步删、异步删什么的。
这个问题本身,也是一个非常高频的面试题。
其实,真的没必要把简单的事情复杂化,我个人建议”先删缓存,后写数据库,再延时双删“就可以了。
无非是当你删缓存不成功,或者没有返回结果的情况下,你需要根据业务场景来判断,是否还要再进行重试而已。
引入ES
如果高并发的查询场景中,包含了一些多维复杂场景查询的话,Redis和本地缓存基本上都是Key,Value结构的,根本hold不住,而MySQL对这种场景处理起来也比较吃力。
唉,这一对儿难兄难弟啊!(手动狗头)
这时引入ES,确实是一个很好的补充,其分词器 + 倒排索引的实现方式,相比较于MySQL的B+ 树索引,对于多维复杂查询的支持要好很多。

数据一致性的解决方案,现在行业内的主流做法,是通过Canal或DataBus进行Binlog同步。我不建议用双写的发难,因为写操作的场景或入口太多,且不能进行收敛的话,容易产生遗漏。
分库分表
前面的三种方式,都是针对于高并发读场景的解决方案,而“分库分表”则更多地是应对读写混合场景的策略。
很多同学对于“分库”和”分表“各自的应对场景傻傻分不清,我这里先来解释一下。
分库:当数据库服务器的硬件成为瓶颈了,这是可以考虑分库,比如:CPU使用率100%了,IOPS和Load过高了,网卡被打满了,等等。
分表:当硬件资源不是瓶颈,而数据库锁成为瓶颈的时候,可以考虑分表作为方案。其实,数据库中不仅仅有记录锁、间隙锁、Next-Key锁,还有意向锁(表锁)、自增锁、插入意向锁,以及保护内存数据结构的latch锁等。
在高并发写场景下,解决了热点资源的锁征用问题,基本上就解决了50%的高并发问题。
分表又包括垂直分表和水平分表。
垂直分表就是把一些不常用的大字段剥离出去。
举个例子:user表中用户名、性别和年龄字段占用的空间不大,地址和个人简介占用的空间较大,但一个数据页的空间是有限的(16k),把一些无用的数据拆分出去,一页就能存放更多行的数据。
内存存放更多有用的数据,就减少了磁盘的访问次数,性能就得到提升。
水平分表,因为一张表内的数据太多了, B+ 树的层级就越高,访问的性能就差,所以进行水平拆分。
MQ消峰
MQ消峰是典型地应对高并发写场景的策略,通常有两种做法:
-
一种是请求来了就直接往MQ里扔,然后再由MQ的消费者按照节奏慢慢进行处理。
-
另一种是请求来了先做其中重要且不耗时的步骤,做完之后再往MQ里扔,MQ的消费者按照节奏慢慢做那些不重要且耗时的步骤。
当然,有一点需要提醒,MQ更多地是解决系统中瞬时高并发的问题。
如果用户的请求量长期居高不下,会导致MQ消费者需要处理的请求越来越多地积压,这时的解决方案就需要放在提升系统处理能力上了。
单元化
呵呵,”单元化“这个东西,一个公司一个叫法,阿里叫Cell化,美团叫Set化。
单元化体系是所有方案中最复杂的,但也是解决高并发场景最后的”大招“。
所谓单元化,就是将用户的请求流量,按照特定的规则,路由到不同的”单元“内,同时在”单元“内做到整个业务逻辑的闭环,起到分散系统高并发压力,快速支持扩容,快速切换容灾的作用。
其关键点在于三个词:路由、隔离、闭环。
其最大的考验点在于Sharding Key的策略,如果这个拿捏不准,对于系统架构是灾难性的。我听美团的朋友说,外卖的Sharding Key是商家所在的城市ID。

当然,这个方案的弊端是:成本高、风险大,所以不在大厂,且日订单量没上千万的话,那就别搞了。


结语
先说这么多吧,后续可以再聊聊高可用、高性能、海量数据、研发提效相关的内容。
原文始发于微信公众号(库森学长):面试官:有哪些高并发解决方案?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/165761.html