如何做好一个基础的搜索功能?记一个因客户大数据量而导致的后发先至Bug

壹 ❀ 引
上篇文章算是开了一个新系列,因为工作缘故,我基本每天都在跟各式各样的bug打交道。其实站在一个开发的角度,我想每个人应该都更喜欢创造新代码,创造新bug,而不是每天都泡在茫茫代码海洋中定位和修复问题。
当然,当产品部资源不够时,我偶尔也会接手做做需求,比如上周有个比较急的需求没资源投入,产品管理那边就从几个组都抽出了一个人凑成了一个临时小组,前端这边工作就是由我来负责了。当我做完前端方案去评审的时候,技术委员会的大佬(入职的比较晚)对我发出灵魂拷问,你也做需求??

所以有时候真的需要对这份工作调整好心态,以前我也经常问自己,做前端不做需求写大量的代码,这真能学到东西吗,因此我也跟我所在的部门负责人抱怨过这件事,他对我说,你在一家公司从来就没有人能限制你学到什么。
我当时突然想起了一部看过的电视剧东京大饭店(我基本不看电视剧,因为没什么耐心...但这个是真好看= =),影片讲述了男主为打造世界最棒的米其林三星餐厅找回曾经的餐厅伙伴一起追梦的故事,而开店前期缺人因为机缘巧合招了个菜鸟新人(除了这个新人其余的人都算顶级厨师了)。男主与其他人苦思冥想研究新品菜系,新人不是在训练切5mm的萝卜丁就是在切5mm萝卜丁,每次想要得到表现的机会去做菜,一次次被拒绝,也因此觉得自己在这学不到东西得不到认可,差点跟男主闹翻。我当时就想,不是吧大哥,你深处一个顶级厨师的团队,身边都是世界级的大佬,哪怕不做菜,天天在这个氛围中花点心思学一学,出去都能吊打各种厨师了!!这么难得的经历,完全就是死脑筋!!
正所谓当局者迷旁观者清,我自己何尝不像是这个电视剧的菜鸟新人呢?项目代码就放在那,想学到什么想了解什么,完全是由自己的心来决定的,除了自己没有人能限制你学到什么。
所以后来我回顾自己经手的一些bug,才发现确实有很多值得研究和深思的问题(技术委员会大佬也觉得很多问题是值得搜集起来讲一讲),这也是为什么我开始写这类修复经历的原因。

那么东京大饭店的菜鸟新人最后到底有没有得到男主认可,男主一行人有没有追梦成功呢?本文肯定也不会提,而我也在接受当下,一步步改写自己的"命运"。
另外,本文所阐述的bug并不是专属于react,而是一个很常见的前端搜索场景问题,所以即便不会react我也推荐了解下,万一面试被问场景问题遇到了呢?好了,闲话说了一堆,本文正式开始。
贰 ❀ 问题场景再现
在上周csm就给我提了一个客户反馈的bug单,因为这个客户是一个大客户,所以反馈的bug我们都比较看重。但因为上周确实比较忙一直没时间跟进,结果本周一我刚出地铁还在路上,csm企业微信就滴滴我了,问我今天有没有时间远程下这个问题,想与我同步下方便约客户。
我其实之前也尝试复现过这个问题,但不管是我本地环境还是客户侧私有部署测试环境,都没能复现这个问题。所以我跟他说,上午给我点时间先熟悉下这块功能的代码,下午2点就可以远程。
远程其实就是远程链接客户的电脑,操作客户的电脑来排查问题,一般只有我们这边实在复现不了问题时才会提出远程。远程的环境因为都是正式环境,随便一个文件打开都是十几万行高度压缩的代码,可以说毫无阅读体验,不提前做下准备到时候远程了找不出原因,那我不得尴尬死。
等到下午远程链接了客户也是一番操作....对方演示就是能复现,我接管鼠标操作就是不能复现,几番摸索,终于还是在客户电脑上复现了这个问题,因为客户数据安全的问题,这里就不能直接贴问题原图了,但我们还是先来还原下问题场景。
bug现象其实很简单,在成员页用户可以通过搜索查找到符合条件的所有成员,然后可以勾选成员加入到当前项目。所以每次输入或者修改输入框内容,一定都会发起请求,然后前端响应让展示区域的列表发生改变。

比如上图中,一开始有个A,那么一开始内容区域展示的都是A相关的成员。紧接着用户做了一次清空操作,然后又输入了一个B,理论上来说,最终内容区域应该展示B相关的人员,但很遗憾,B相关的成员只展示了一会,紧接着区域的数据又变成了未加任何搜索字段的全部数据,也就是初始数据了。所以客户提单说,搜索输入框的结果每次都只能展示一会,过会自己就变了。
我在复现问题后,打开了客户电脑控制台的Network,看了眼复现操作中的请求调用,问题的原因马上就清楚了。我用一个图来表示这个过程。

这个行为中一共发起了两次请求,在我远程查看了两次请求耗时发现,第一次请求耗时了4s,第二次耗时只用了1S。也就是说这就是个后发先至的问题,后发的请求B先回来了,因此前端先展示了B的内容,没多久,第一次请求也就是空条件的结果又回来了,覆盖了B的请求结果,这就是这个问题的根因。
而后端对于这种请求处理都是并发的,相互之间并不会有所感知,你前端发几次请求过来,肯定是先处理完的我先反馈给你,如果后端加个队列严格按先后顺序处理,那要是炸一个接口半天没响应,其余的请求都没法玩了。
而为什么我自己没能复现这个问题呢?这是因为客户侧数据有几十万,而我自己的测试账号一共就3条user数据....没有这个数据量支撑,复现几率就是0。
同时我又想到了第二个问题,为什么没有筛选条件的请求反而比有筛选条件的慢?理论上来说我不传筛选条件,你都不用查了,直接给数据我,这边得到的结论是,后端那边的查询是越接近底层查询越快,越接近业务层查询速度越慢,当筛选条件为空时,因为没筛选,所以后端业务层面对的是几十万的数据量;而有了筛选条件,到业务层的数据已经被筛选过一次可能就只有几百条了,用时反而更少了。
那怎么解决呢?让后端解决?后端也明说了,数据量摆在那,接口又是并发不可能给你排队处理,所以问题还是得前端来解决,下面说说方案。
叁 ❀ 解决方案
让我们回到问题本身,一个看似简单的搜索居然能引发这样的有趣问题,假设这是个面试题又该如何解决呢?其实思路可分为两步。
我一开始看这个问题,怀疑是滥用防抖造成的,结果一看代码,好家伙根本没用防抖= =,也就是说假设用户是光速A-空-B,那确实会发起两个请求,第一次肯定是给这种高频修改加一个防抖,无意义的请求能不发就不发。
但事实上防抖并没有从根本解决问题,问题的根因是数据量太大,查询确实要那么久,我们设置防抖一般也就是300ms左右,假设用户A-空-B的间隔超过了你设置的防抖时间,前端还是会发起两次请求,而后端还是会有后发先至的可能性,所以单一个防抖解决不了问题。而这个时候,我们还需要加一步操作,那就是加个开关去取消上一次的请求,画个图:
(PS:防抖还是要加,假设现在数十万用户同时访问,不加防抖造成的无意义请求那就是数十万个了,还是会造成服务器资源大量浪费)

我们来解释下这个过程,一开始有个请求开关,默认值是false。
模拟一次请求:请求发起-->请求开关默认是false-->发起请求-->修改请求开关为true-->请求结束-->修改请求开关为false-->结束。
模拟上面的bug场景:请求发起-->开关是false-->发起请求-->修改请求开关为true-->请求还没结束又发起了第二次请求-->请求开关是true-->取消上次请求-->继续走正常请求路线...(假设过程中又操作了多次继续重复取消操作)...-->结束。
大家可以思考下这个过程,对于同一请求,如果用户确实操作了多次,对于用户而言TA关心的其实就是最后一次操作的结果,因此当前面一次请求没回来,我们完全可以舍弃掉这次请求,直接发起新的请求,后续操作同理。
当然,假设接口响应巨快,快到超出了用户操作间隔,那我们其实啥也不用干,毕竟后端返回数据先后顺序完全符合用户预期,requestSwitch开发自然会被合理切换,咱也不用做额外处理。
有同学可能要问了,你说的我都懂,那这个请求取消我该怎么做,其实axios就有提供一个API叫CancelToken,这就是解决上述问题的妙药,而取消的底层原理与XMLHttpRequest.abort()有很大关系。因为篇幅问题,关于取消原理还是另起一篇文章来介绍吧。
这篇文章其实说到这也没贴一点代码,因为前面也说了,这个问题并不属于react专属的问题,而是每一个搜索面对大数据量时都可能遇到的问题,重要的是方案,有了方案看看axios文档我还不信你还做不出来。
肆 ❀ 总
OK,那么到这里又介绍了一个有趣的bug排查经历。我想搜索功能大家应该都做过,但不一定都有遇到过这种场景问题,比如我前面前端三年还真没处理过此类问题,毕竟项目太简单了,这也是为什么我要写这类博客的原因。bug永远有的修,所以这类文章应该还会更新很多篇,也算是一个小科普了,下一个bug已经在安排中了,那么本文就到这里了!
更新cancelToken实现原理
JS axios cancelToken 是如何实现取消请求?稍有啰嗦但超有耐心的 axios 源码分析
如何做好一个基础的搜索功能?记一个因客户大数据量而导致的后发先至Bug的更多相关文章
- 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- 大数据量表中,增加一个NOT NULL的新列
这次,发布清洗列表功能,需要对数据库进行升级.MailingList表加个IfCleaning字段,所有的t_User*表加个IfCleaned字段. 脚本如下 对所有的t_User表执行 a ...
- 解决WCF大数据量传输 ,System.Net.Sockets.SocketException: 远程主机强迫关闭了一个现有的连接
开发中所用的数据需要通过WCF进行数据传输,结果就遇到了WCF大量传输问题 也就是提示System.Net.Sockets.SocketException: 远程主机强迫关闭了一个现有的连接 网上解决 ...
- (转)分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- 一个简单的使用Quartz和Oozie调度作业给大数据计算平台执行
一,介绍 Oozie是一个基于Hadoop的工作流调度器,它可以通过Oozie Client 以编程的形式提交不同类型的作业,如MapReduce作业和Spark作业给底层的计算平台(如 Cloude ...
- zList一个块状链表算法可以申请和释放同种对象指针,对于大数据量比直接new少需要差不多一半内存
zList是一个C++的块状内存链表,特点: 1.对于某种类别需要申请大量指针,zList是一个很好的帮手,它能比new少很多内存. 2.它对内存进行整体管理,可以将数据和文件快速互操作 3.和vec ...
- dede使用方法----实现英文版的搜索功能
搜索功能在网站中是最常见的一个功能了.我们在用dede做双语网站的时候,默认的会有中文版的搜索功能.但是怎么添加一个英文版的搜索功能.各位看官,方法如下: 1.复制plus目录下的serach.php ...
- PHP商城的搜索功能
大家好,今天分享一个商城的搜索功能,建立在上一篇文章的基础上实现的. 搜索功能简单的说就是通过sql语句在数据库中实现模糊查找 连接数据库,实现分页功能(可以参考上一篇文章) 定义一个变量接收传过来的 ...
- 玩转Django2.0---Django笔记建站基础十三(第三方功能应用)
第13章 第三方功能应用 在前面的章节中,我们主要讲述Django框架的内置功能以及使用方法,而本章主要讲述Django的第三方功能应用以及使用方法.通过本章的学习,读者能够在网站开发过程中快速开发网 ...
- 自写 zTree搜索功能 -- 关键字查询 -- 递归无限层
唠叨一哈 前两天朋友跟我说要一个ztree的搜索功能,我劈头就是一巴掌:这种方法难道无数前辈还做少了?自己去找,我很忙~然后我默默地蹲着写zTree的搜索方法去了.为什么呢?因为我说了句“找不到是不可 ...
随机推荐
- Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
本文为博主原创,未经允许不得转载: 目录: 1. 自定义生产消息 kafkaTemplate 实例 2. 封装 kafka 发送消息的service 方法 3. 测试 kafka 发送消息ser ...
- 2023年江苏“领航杯”MISC一个很有意思的题目(别把鸡蛋放在同一个篮子里面)
别把鸡蛋放在同一个篮子里面 题目附件:https://wwzl.lanzoue.com/i6HmX16finnc 1.题目信息 解压压缩包打开附件,获得5141个txt文档,每个文档都有内容,发现是b ...
- 【面试题精讲】Redis如何实现分布式锁
首发博客地址 系列文章地址 Redis 可以使用分布式锁来实现多个进程或多个线程之间的并发控制,以确保在给定时间内只有一个进程或线程可以访问临界资源.以下是一种使用 Redis 实现分布式锁的常见方法 ...
- [转帖]Docker最佳实践:5个方法精简镜像
https://juejin.cn/post/6844903880526921741 精简Docker镜像的好处很多,不仅可以节省存储空间和带宽,还能减少安全隐患.优化镜像大小的手段多种多样,因服 ...
- [转帖]SQL SERVER DBCC命令详解
https://developer.aliyun.com/article/867768 简介: SQL数据库开发 DBCC DROPCLEANBUFFERS:从缓冲池中删除所有缓存,清除缓冲区 在 ...
- [转帖]global cache cr request等待事件分析及优化
在RAC环境中,和全局调整缓存相关的最常见的等待事件无非就是:global cache cr request,global cache busy和equeue 在XX电信做了一次数据库巡检中发现,sp ...
- [转帖]《Linux性能优化实战》笔记(一)—— 平均负载
最近在看极客时间的<Linux性能优化实战>课程,记录下学习内容. 一. 平均负载(Load Average) 1. 概念 我们都知道uptime命令的最后三列分别是过去 1 分钟.5 分 ...
- [转帖]ssh时不输入YES
vim /etc/ssh/ssh_config 60行新添加 StrictHostKeyChecking no
- [转帖]性能分析之TCP全连接队列占满问题分析及优化过程(转载)
https://www.cnblogs.com/wx170119/p/12068005.html 前言 在对一个挡板系统进行测试时,遇到一个由于TCP全连接队列被占满而影响系统性能的问题,这里记录下如 ...
- 【转帖】【奇淫技巧】Linux | 查找文件,无所遁形
theme: channing-cyan 本文正在参与 "走过Linux 三十年"话题征文活动 在Linux系统上,最常见的操作莫过于处理文本.常见文件操作陈列.查找.排序.格式转 ...