成为工程师 - 如何提升系统读性能(下)

简介: 成为工程师 - 如何提升系统读性能(下)

今日内容


今天,我们继续来讲【读性能】剩下的部分,包括【读写分离】【并发思维】【异步设计】和【产品设计】。


01读写分离


所谓【读写分离】指的是:对访问DB的“读流量”和“写流量”引到不同的DB实例上。为什么我们要做读写分离呢?下图是最初没有做分离时候的情况:


这种情况在业务流量不大的时候,没有什么问题。但随着流量的上涨,会碰到如下问题:


【1】DB服务的连接资源不够。尤其连接DB的机器不断变多。


【2】大量写请求导致索引不断重建,这是耗时的动作。


【3】DB服务供的性能受到单机硬件的制约,无法持续提升服务能力。


那【读写分离】的方案是怎样的呢?如下图所示:



图中,我们把DB的实例分为“主实例”和“备实例”。


“主实例”通过主备同步的方式将数据以及数据的变化同步到“备实例”中。


“主实例”提供写服务,“备实例”提供读服务。分离以后。


首先,读写流量之间不会相互影响。


其次,更重要的是,读服务理论上可以无限扩容。这是多么大的好处啊?!


当然,有好处就有坏处。“主实例”和“备实例”可能会存在数据短暂不一致的情况,我们下面称之为“主备不一致”风险。


那么问题来了,“主备不一致”风险怎么降低或者解决呢?


02读写分离的主备不一致问题


我们一共介绍四种方案,请随我来。


01方案一,任性不管


没错,你没有看错,就是标题字面上的意义,放任不管。


主备同步的延迟一般在1s以内。在绝大部分的场景下,是完全可以接受的。根据奥卡姆剃刀原理,如无必要勿增实体,就放着别管了。


02结合产品设计


我们看如下这张图(请跟着序号看):


在上图中,第三步返回用户之后,产品在前端设计上可以考虑hold一小段时间,比如放一个可爱的loading图案,以此来“等待”后端完成主从同步。


当然,这个方法属于讨巧,不能严格保证主从同步的效果。但实现简单,在绝大部分情况下,可以起到不错的效果。


03强制读“主”


既然主从同步存在延迟,那就直接点,直接读主。但和最初的方案不同的是,我们只将必须读主的流量引到主库,能够容忍主从不一致的读流量,还是引到备库。


如下图所示:


04缓存路由


缓存路由这个名字是我自己起的。它的意思是,读请求在决定读取主库还是备库之前,先查询一个专门的缓存,根据查到的结果来判断。我们来看下图:



上图中,黑色线是写链路蓝色线是读链路。我们注意两个点:


【1】写链路中,写完数据库后,需要写一个能够代表这个数据的key到专门的缓存中(我们把这个缓存称之为”数据库路由缓存“)。这个key往往是读请求中的查询要素,例如产品id。重点来了,这个key写入缓存时会携带一个超时时间,这个时间往往是主备同步的安全值,例如1s。也就是1s之后,主备确保(也是绝大概率上)同步一致。


【2】读链路中,在查询db之前,先访问”数据库路由缓存“,如果查询key存在(表示主备不一定完成了同步),则访问主库,否则访问备库(不存在key则表示主备同步完成)。


这个方式是不是特别巧妙?没错!很巧妙。但是,我并不推荐,why?


因为这个方案引入了不小的复杂度。


首先,你需要再维护一个单独的缓存,这个缓存需要高可用(或者你要想清楚,如果缓存出现了问题,如何路由读请求)。


其次,无论是DB的“读流量”还是“写流量”,都引入了和业务无关的逻辑,即使是框架包了,也会存在出问题时候的查询工作量。


所以,很酷的技术往往不见得是最优解。简单的往往是最好的,你要慎重选择。


03并发思维很重要


在讲这个概念之前,我们先来看一个面试题:


有一个文件,有1亿条交易数据,从中找出交易金额最大的前100条数据


这是再经典不过的一道面试题了吧,你会如何作答呢?

【1】你是不是开始思考使用什么排序算法?

【2】你是不是开始考虑如何读取文件?

【3】你是不是想到了内存不够用?

【4】......


但是我觉得,你首先应该想到的一个角度是,如何使用更多的资源并发处理。


我对“更多的资源”和“并发”做了标注。说白了就是,与其想办法让一个人干活更快,不如去想如何让更多人加入一起干活。


这里的“人”可以是CPU核,也可以是机器等其他资源。


沿着这个思路,可以用下图这个方案解决这个问题:



如果有了解大数据的同学对这个图一定非常熟悉,没错,这其实就是MapReduce的思路。


04异步化设计


异步化是一个使用场景很多的词,在这里,我们指的是“保留同步过程中必要的部分,将其他的部分通过异步化的方式处理”


例如下图,这是一个标准的同步流程:



而如果,其中只有action1、action2、action3是必要的步骤呢?


所谓必要步骤,有两个条件,取其一即可。


【1】步骤会影响请求的结果。


【2】步骤有非常高的实时性要求。甚至无法容忍使用消息这种异步化方案带来的延迟风险。

一般来说,第二点的情况比较少。如果你的产品有第二点这样的诉求,我建议你们可以对产品做一些优化,这远比在技术上想办法要快捷和高效的多。


于是,我们可以将上述的链路变成如下这样:


图中我们可以看到,将后续三个动作异步化以后,我们的响应时间急速缩短。这种方式对读性能的提升也是非常有帮助的。


05产品优化


最后,我们再另辟蹊径,从技术外的角度来看如何提升读性能。那就是:从产品的角度来优化


不要小看这个角度,这个角度往往有奇效。下面我举几个例子:


【1】分页查询


页面展示列表数据,引入分页的功能。

如果查询的数据量平均有100条记录,默认分页是20条一条,则可以极大提升后端的响应速度。

尤其每一条数据如果还涉及各种rpc调用和数据库访问来最终聚合,则效果更明显。


【2】递进展示


这里的递进展示是指,例如一个页面有50个功能模块,可以根据用户滑动所停留的位置做指定加载。如此一来,不需要打开页面的时候一口气请求50个数据模块,只需要请求当前用户可见页面所需的数据即可。


【3】降低极致的准确性要求


对于一个产品来说,追求极致是一种精神,但是落向现实,可能有非常高昂的成本。


例如,如果你做一个热搜的功能。你要展示各种搜索条件下前50的热门话题。每次请求你要汇聚所有的话题,然后根据筛选条件筛选并且根据各种维度计算热度,再排序输出。


随着话题和维度的增多,计算量会增多,性能就会成为问题。


但如果你退而求其次,返回的前50热门话题只要求90%的命中率,也就是部分话题严格说可能在50名开外,技术上就可以用各种缓存、模糊计算的思想去做,就会容易很多。


反过来看产品,产品真的不能接受这样子的“让步”吗?


这个产品真的需要那么精确和严谨的结果吗?


用户真的愿意并且需要为了那么精确而多几秒甚至十几秒的等待吗?


“让步”往往不是得过且过的妥协,而是综合所有涉及方的利益来看,是一种最佳的选择。


【4】高峰期功能降级


在访问高峰期时,对一些次要功能做降级。


例如,你要展示用户信息,其中包含了用户余额信息、优惠券信息等等。但是优惠券服务的性能有限,如果对优惠券服务强依赖,在流量较大的时候,就会被“牵连”。但事实上,余额信息才是用户最关心的。


那么,你就可以在流量高峰期时,对优惠券功能做降级。


例如展示“点击查看”,这样就可以躲过高峰的一级流量。真正需要优惠券信息的用户会再点击,流量就会迅速下来。


更有甚者,如果二级流量也无法承接住,那就直接在高峰期时下掉查询入口,并且公告提示服务降级时间。


【5】控制重试


有些前端的页面往往为了优化对用户的体验,会在请求后端出现超时等异常的时候发起重试,这无疑给后端带来了压力。


在平时可能压力不大,但是在高峰期时,可以考虑去掉重试功能,以降低对后端的压力。


毕竟这里还有个细节,如果后端服务器是因为网络等偶发情况造成了超时,重试是有效的。但如果是因为后端服务器压力太大扛不住了造成超时,重试只会让情况更糟。


06其他方法


除了上面提到的这些方法外,还有一些常被使用到的方法。考虑到普遍性并不强以及篇幅的原因,不做具体的展开,仅做概述。


【1】优化协议。较多用于传输数据较为频繁或者数据量较大的场景下。比如即时通信软件往往会自定义协议,使用特定的分隔符或者长度约定来分割要素(是不是很像数据包的设计方式 ),避免使用xml或者json这种带有描述性字段的格式。


【2】页面缓存。也是用于特定的场景,把页面直接缓存起来作为静态资源分发到CDN上。例如某些排行榜的数据,30分钟更新一次。使用CDN的能力会比上一篇文章介绍的缓存方式更有效。


【3】数据压缩。顾名思义,就是通过额外的压缩和解压行为来换取传输数据的效率。因为压缩和解压需要引入额外的时间,所以务必通过验证来确保这样的方式是“划算”的。


本章小结

今天,我们接着上一篇文章,继续聊了如何提升读性能。


我们提到,可以使用【读写分离】【并发思维】【异步化】【产品方案】这些方向去优化。


这些方向直观上是“术”,也就是一个个具体可操作的方法,但其实你细品,你能透过这些具体方法看到背后的一些哲思。


例如,【读写分离】背后的“职责分工”,【并发思维】背后的“资源思维”,【异步化】背后的“主次分明”,【产品方案】背后的“全局思维”。其实我觉得,这些才是真正有意义的东西。

相关文章
|
缓存 NoSQL 关系型数据库
如何设计一个高并发系统?
如何设计一个高并发系统?
110 0
|
3月前
|
缓存 NoSQL 算法
成为工程师 - 如何提升系统读性能(上)
成为工程师 - 如何提升系统读性能(上)
|
3月前
|
存储 缓存 分布式计算
成为工程师 - 如何提升系统写性能(下)
成为工程师 - 如何提升系统写性能(下)
|
3月前
|
存储 缓存 NoSQL
成为工程师 - 如何提升系统写性能(上)
成为工程师 - 如何提升系统写性能(上)
|
5月前
|
Java 数据库 图形学
论系统的木桶理论与性能瓶颈
论系统的木桶理论与性能瓶颈
61 7
|
6月前
|
存储 缓存 监控
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
109 0
|
缓存 测试技术 数据库
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
365 0
|
存储 缓存 监控
浅谈系统性能提升的经验和方法
资金核对的数据组装-执行-应急链路,有着千万级TPS并发量,同时由于资金业务特性,对系统可用性和准确性要求非常高;日常开发过程中会遇到各种各样的高可用问题,也在不断地尝试做一些系统设计以及性能优化,在此期间总结了部分性能优化的经验和方法,跟大家一起分享和交流,后续遇到一些新的问题也会持续总结和补充。
39470 17
浅谈系统性能提升的经验和方法
|
5G 索引
带你读《5G 系统技术原理与实现》——3.4 5G SSB
带你读《5G 系统技术原理与实现》——3.4 5G SSB
带你读《5G 系统技术原理与实现》——3.4 5G SSB
|
存储 缓存 数据可视化
CPU缓存读写以及一致性问题,你大学课堂睡过去的现在再温习下,绝对受益颇多
CPU缓存读写以及一致性问题,你大学课堂睡过去的现在再温习下,绝对受益颇多