服务反应每慢一秒,转换率就会掉 12%

很多时候在讨论使用者友善,其实最简单粗暴,也最有感的改善之一就是让操作动作变快,也就是让用户更快得到他要的东西。毕竟只要操作上有卡顿,用户马上就会分心。
之前看 Google Startup School 的 SEO 课程中也有说

网页 loading 每慢一秒,转换率就会掉 12%

我们在优化我们 app 的体验时,很常做的一件事就是去看现在效能瓶颈在哪,然後去改善。
比如之前有个很容易成为效能瓶颈的情景是「随机配对」功能。这应该是游戏很常出现的功能,但我们不能说是完全随机,因为我们还有保证配对两边是以前没有配对过的。
当用户按下随机配对,我们要从 Queue 里面找出「还没跟他配过对」的人给他,所以最坏情况就是每次用户按一次这个钮,都要回去查找一遍目前所有存在的配对来决定。关於这个动作的改善,我们有过三种版本:

  1. MongoDB
  2. Redis
  3. Cache + Redis

初始版本:单纯用mongoDB

最一开始,很单纯的,就是每次从现有的对话 collection 里查找,顶多把 userID 设为 index。
但是资料量变大,时间跨度变大时,mongoDB 的话,一定会有一部份旧资料会放在 disk,导致每次查找都会出现 disk I/O ,因为这是一个很高频的操作,所以每次都有 disk 的操作会非常没效率。於是我们自然开始思考要怎麽改善。

导入 in-memory DB: Redis

号称最快的资料库 Redis,因为是完全在memory上处理,刚好符合我们的需求,所以我们就加了一个 Redis的 node,纪录所有配对过的帐号,专门用来解决这个问题。 Redis 用一个独立的 node 而不是跟後端程序放在一起,原因是,我们当时的後端已经有好几个 node。如果把 Redis 部署在其中一个 node,就会导致这些後端的 instance 产生 dependency。更不可能部署在所有的 node,这样就无法有一个单一的正确的 snapshot。
Redis 的好处是保障所有查找都是 in-memory ,解决了 mongo 的 disk I/O 的负担。但是量大之後,还是会受限於 Redis 所在 node 的规格,此时可以选择就直接花钱提升 CPU 解决,或是再从架构上改善。

导入 30 分钟的 cache

小团队节俭就是美德,改善规格看似每个月不贵,但一提升就是会变成每个月的固定支出,不可不慎呀。所以我们当然是选择从架构上改善。
导入 Redis 之後,还是会有问题的情况是,重度用户会在短时间内按好几次配对,所以只要尖峰时段有很多重度用户,就会塞车。所以我们在进入 Redis 查找之前,另外加上一个 30 分钟过期的 cache,纪录最近30分钟内的配对,只要在里面找到,就不用再去 redis 找。针对重度用户的使用情境,这个方法可以省掉很多 redis 的 query。 30 分钟的 cache 也不会造成记忆体太大负担。

结论

一开始为了解决 mongo 的负担,在查询过往配对上我们改用 redis 来实作。後来因为 redis 也不够用,我们为了降低资料库查找频率,加入了 30 分钟的 cache。目前流程就是会先看有没有 cache,没有的话就是去 Redis 查找。这个方法开始用之後,就几乎没听过有人抱怨这个功能的速度了。
让操作变快变顺畅,是很直观的用户体验优化,但是实际上要做的事通常并不像是改介面那麽直观。

最新文章会分享在脸书:https://www.facebook.com/gigi.wuwu/
欢迎留言讨论


<<:  Day1-网路与K8s的奇怪漂流

>>:  [30天 Vue学好学满 DAY8] v-bind

Day 22 利用transformer自己实作一个翻译程序(四) 输入资料处理

输入管道(input pipeline) 要建立适合训练的管道,需要对资料集做一些转换 def to...

Golang快速入门(Day4)

在这边要介绍一下go的基本用法 而这些用法在A Tour of Go也都有介绍 在下面的程序码如果有...

Day24-Kubernetes 那些事 - 内部架构

前言 之前的文章提到透过 K8s 的 Health Check,可以将不健康的 Pod 砍掉重建,或...

Day28:网页排名演算法(PageRank)

PageRank PageRank是一种连结分析演算法,它通过对超连结集合中的元素用数字进行权重赋值...

[Day_21]回圈与生成式 - 练习题

费氏数列 费氏数列氏将第1项与第2项相加等於第3项,第2巷与第3项相加等於第4项,依此类推,初始化费...