Skip to content

Instantly share code, notes, and snippets.

@arrayadd
Last active July 4, 2017 17:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arrayadd/963610e9ae291ffa3555b4a9eb30004e to your computer and use it in GitHub Desktop.
Save arrayadd/963610e9ae291ffa3555b4a9eb30004e to your computer and use it in GitHub Desktop.
记一个需求开发的思维过程

昨天临下班的时候,意外接到一个叫**「我的足迹」**的需求,大意就是把用户访问过的商品记录下来,方便用户以后查看。


重审需求是否合理

从用户使用层面,上面这个需求提供的功能,或多或少有和我的收藏功能重叠嫌疑。但作为开发有质疑需求的意识还是非常必要的,尤其是做应用开发的,绝对不能PM给个需求就急着开发,时常驳回一些拍脑袋的需求还是必修的。养成这个意思非常重要。 当然关于这个需求,PM还提出了以下一些要求:

  • 每个用户只保留最近100条浏览记录
  • 不能重复,同一个商品浏览多次,算最近一次时间。
  • 查看时候,按照时间分页降序。

编码前需要考虑点

看完需求文档,作为开发,我们肯定要考虑实现上事情,大点需求甚至要出设计方案,讨论下。这里大概有以下几点考虑:

  • 怎么存用户的数据,关系型 or NoSql
  • 怎么保证100条,多出100条可以吗
  • 相同浏览记录不重复,但要保留最新时间,怎么处理
  • 业务场景是新增多,还是请求多。即读多还是写多
  • 接口 qps/tps/并发量 大概什么级别,预估数据量多少

上面这些点结合实际系统数据考虑完后,我这边最终选择了redis中的zset数据结构来存储。当然zset 的add操作,由于有排序,时间复杂度是O(log(N)),但我们这里需求数据量控制在100条左右,所以单key来说问题不大。


如何有效的保留100条记录?

咋一想,要保留某个用户100条记录还不简单,添加记录时候顺带判断下,如果超过100条了,就删除多余的不就行了。

当然单独看这思路没有问题,但结合我这边上面接口 qps/tps/并发量 大概什么级别,读多还是写多这几条调研的结果来看,就不行了。

首先这个业务场景是典型的写多读少情况,用户访问一个页面就会记录数据,但用户相对很少情况下会去查看自己的浏览记录。更可怕的是我们业务日活DAU还不小,详情页PV更是巨大。如果访问一次,就在新增记录数据的接口中顺带判断查询一下这个用户数据记录是否超过100条了,那么将会浪费很多没必要的请求

单个用来说,至少前100次没必要请求的,因为压根不会超过100条,不存在删除情况,而如果每天有1亿次,那么将会产生100亿无用的接口请求,白白占用了带宽,增加用户相应时间,浪费系统资源。

我们知道redis的数据过期策略中,有所谓的主动式,被动式。也就是在请求时候才判断key是否过期,及后台运行一个线程专门扫描数据,判断过期就删除。

同理,上面我们已证明主动式式不可行的,因为会增加没必要的请求。那我们换个思路,即所谓的被动式,也就是运行一个定时任务,定期的去扫描每个用户的记录,看有没有超过100条,超过了就删除。这样虽然可以避免冗余的请求,但是却有多引入了一个离线任务,维护起来不好使,而且用户量大时候,也不及时,浪费redis空间,况且知会有限的减少redis服务器的压力及无用的请求。但至少这个方案可行。


主动式再思考,巧用随机数来减少接口请求量

当遇到瓶颈时候,我们就再回到需求上来,重新审视,直到最后需求被认定为什么傻逼需求,完全没法做啊。但轻易不会是需求问题。

经过一番审视后,发现需求中提到这100条,是UI展示层面的,实际实现中我们数据库存110条也没问题,只要查询接口控制下,最多返回100条就行。

既然多存也没问题(多存太多不行,会占用更多空间,用户多时候量非常大),我们就可以这样来实现:

当新增用户记录时候,我们随机取一个1~100内的随机数,如果这个数大于某个值,例如大于90,我们就才额外请求一次接口,判断这个用总记录是否超过100条,超过就删除。

这样一来,按照上面比率(可以灵活调节),至少降低了90/100 次请求。而且理论上,这个用户最多存到110条时候就会再触发执行一次删除操作。

当然考虑到随机数算法一般是伪随机,且消耗cpu,我们可进一步以优化为,获取当前系统时间,如果以数字9结尾,就触发执行,类似思路。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment