分页列表缓存,你真的会吗

Posted 勇哥java实战分享

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分页列表缓存,你真的会吗相关的知识,希望对你有一定的参考价值。

开源中国的红薯哥写了很多关于缓存的文章,其中多级缓存思路,分页列表缓存这些知识点给了我很大的启发性。

写这篇文章,我们聊聊分页列表缓存,希望能帮助大家提升缓存技术认知。

1 直接缓存分页列表结果

显而易见,这是最简单易懂的方式。

我们按照不同的分页条件来缓存分页结果 ,伪代码如下:

public List<Product> getPageList(String param,int page,int size) 
  String key = "productList:page:" + page + ”size:“ + size + 
               "param:" + param ;
  List<Product> dataList = cacheUtils.get(key);
  if(dataList != null) 
    return dataList;
  
  dataList = queryFromDataBase(param,page,size);
  if(dataList != null) 
       cacheUtils.set(key , dataList , Constants.ExpireTime);
  
 

这种方案的优点是工程简单,性能也快,但是有一个非常明显的缺陷基因:列表缓存的颗粒度非常大

假如列表中数据发生增删,为了保证数据的一致性,需要修改分页列表缓存。

有两种方式 :

1、依靠缓存过期来惰性的实现 ,但业务场景必须包容;

2、使用 Redis 的 keys 找到该业务的分页缓存,执行删除指令。 但 keys 命令对性能影响很大,会导致 Redis 很大的延迟 。

生产环境使用 keys 命令比较危险,发生事故的几率高,非常不推荐使用

2 查询对象ID列表,再缓存每个对象条目

缓存分页结果虽然好用,但缓存的颗粒度太大,保证数据一致性比较麻烦。

所以我们的目标是更细粒度的控制缓存

我们查询出商品分页对象ID列表,然后为每一个商品对象创建缓存 , 通过商品ID和商品对象缓存聚合成列表返回给前端。

伪代码如下:

核心流程:

1、从数据库中查询分页 ID 列表

// 从数据库中查询分页商品 ID 列表
List<Long> productIdList = queryProductIdListFromDabaBase(
                           param, 
                           page, 
                           size);

对应的 SQL 类似:

SELECT id FROM products
ORDER BY id 
LIMIT (page - 1) * size , size 

2、批量从缓存中获取商品对象

Map<Long, Product> cachedProductMap = cacheUtils.mget(productIdList);

假如我们使用本地缓存,直接一条一条从本地缓存中聚合也极快。

假如我们使用分布式缓存,Redis 天然支持批量查询的命令 ,比如 mget ,hmget 。

3、组装没有命中的商品ID

List<Long> noHitIdList = new ArrayList<>(cachedProductMap.size());
for (Long productId : productIdList) 
     if (!cachedProductMap.containsKey(productId)) 
         noHitIdList.add(productId);
     

因为缓存中可能因为过期或者其他原因导致缓存没有命中的情况,所以我们需要找到哪些商品没有在缓存里。

4、批量从数据库查询未命中的商品信息列表,重新加载到缓存

首先从数据库里批量查询出未命中的商品信息列表 ,请注意是批量

List<Product> noHitProductList = batchQuery(noHitIdList);

参数是未命中缓存的商品ID列表,组装成对应的 SQL,这样性能更快 :

SELECT * FROM products WHERE id IN
                         (1,
                          2,
                          3,
                          4);

然后这些未命中的商品信息存储到缓存里 , 使用 Redis 的 mset 命令。

//将没有命中的商品加入到缓存里
Map<Long, Product> noHitProductMap =
         noHitProductList.stream()
         .collect(
           Collectors.toMap(Product::getId, Function.identity())
         );
cacheUtils.mset(noHitProductMap);
//将没有命中的商品加入到聚合map里
cachedProductMap.putAll(noHitProductMap);

5、 遍历商品ID列表,组装对象列表

for (Long productId : productIdList) 
    Product product = cachedProductMap.get(productId);
    if (product != null) 
       result.add(product);
    

当前方案里,缓存都有命中的情况下,经过两次网络 IO ,第一次数据库查询 IO ,第二次 Redis 查询 IO , 性能都会比较好。

所有的操作都是批量操作,就算有缓存没有命中的情况,整体速度也较快。

查询对象ID列表,再缓存每个对象条目“ 这个方案比较灵活,当我们查询对象ID列表,可以不限于数据库,还可以是搜索引擎,Redis 等等。

下图是开源中国的搜索流程:

精髓在于:搜索的分页结果只包含业务对象 ID ,对象的详细资料需要从缓存 + MySQL 中获取。

3 缓存对象ID列表,同时缓存每个对象条目

笔者曾经重构过类似朋友圈的服务,进入班级页面 ,瀑布流的形式展示班级成员的所有动态。

我们使用推模式将每一条动态 ID 存储在 Redis ZSet 数据结构中 。Redis ZSet 是一种类型为有序集合的数据结构,它由多个有序的唯一的字符串元素组成,每个元素都关联着一个浮点数分值。

ZSet 使用的是 member -> score 结构 :

  • member : 被排序的标识,也是默认的第二排序维度( score 相同时,Redis 以 member 的字典序排列)
  • score : 被排序的分值,存储类型是 double

如上图所示:ZSet 存储动态 ID 列表 , member 的值是动态编号 , score 值是创建时间

通过 ZSet 的 ZREVRANGE 命令就可以实现分页的效果。

ZREVRANGE 是 Redis 中用于有序集合(sorted set)的命令之一,它用于按照成员的分数从大到小返回有序集合中的指定范围的成员。

为了达到分页的效果,传递如下的分页参数 :

通过 ZREVRANGE 命令,我们可以查询出动态 ID 列表。

查询出动态 ID 列表后,还需要缓存每个动态对象条目,动态对象包含了详情,评论,点赞,收藏这些功能数据 ,我们需要为这些数据提供单独做缓存配置。

无论是查询缓存,还是重新写入缓存,为了提升系统性能,批量操作效率更高。

缓存对象结构简单,使用 mget 、hmget 命令;若结构复杂,可以考虑使用 pipleline,Lua 脚本模式 。笔者选择的批量方案是 Redis 的 pipleline 功能。

我们再来模拟获取动态分页列表的流程:

  1. 使用 ZSet 的 ZREVRANGE 命令 ,传入分页参数,查询出动态 ID 列表 ;
  2. 传递动态 ID 列表参数,通过 Redis 的 pipleline 功能从缓存中批量获取动态的详情,评论,点赞,收藏这些功能数据 ,组装成列表 。

4 总结

本文介绍了实现分页列表缓存的三种方式:

  1. 直接缓存分页列表结果

  2. 查询对象ID列表,只缓存每个对象条目

  3. 缓存对象ID列表,同时缓存每个对象条目

这三种方式是一层一层递进的,要诀是:

细粒度的控制缓存批量加载对象


如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

产品经理需要自己的“敏捷开发”,你真的会吗?


本文共5860字,预计阅读15分钟


产品经理需要自己的“敏捷开发”,你真的会吗?


第13篇



产品经理需要自己的“敏捷开发”,

你真的会吗?


 1 

作为产品经理,我们除了了解竞品分析、需求文档以及活动策划外,还应该了解掌握各种开发模式,例如:迭代开发、增量开发、瀑布式开发、螺旋式开发以及敏捷开发。

而其中的敏捷开发可以说是已经到了“家喻户晓”的地步,并且许多公司或是团队至今都在沿用敏捷开发的模式进行日常工作的开展。

就是在这样“家喻户晓”、“人人沿用”的情况下,敏捷开发在实际应用中一直两极分化严重。敏捷开发他是压力、无脑冲刺的代名词,同时也是创业工作中的“救世良药”,产品创新的方法。

产品经理需要自己的“敏捷开发”,你真的会吗?


为什么会存在这样的两极分化?下面,咱们将通过三个方面进行讨论,来深入了解敏捷开发之后便知道为什么会如此两极分化啦。

1、推崇敏捷开发的根源是什么?

2、你使用的是流程敏捷还是思维敏捷?

3、我们该如何选择敏捷模式,它能给我们带来什么?

 2 
推崇的根源是什么

我们都知道,敏捷开发突出在敏捷上。
根据用户(顾客、甲方)的需求,进行短、平、快的研发冲刺,并且在冲刺过程中,还需要对需求、市场的变化进行灵活的调整,以适应变化。而恰恰因为这些特点,所以我们当前时代十分推崇敏捷开发。

首先,要想知道为什么推崇,就需要我们了解当前我们处于时代时代的背景。

产品经理需要自己的“敏捷开发”,你真的会吗?


我们现在处于互联网高速发展的时候,而这个时代叫vuca时间,vuca时代中的vuca代表这个时代的四种属性及volatility(易变性),uncertainty(不确定性),complexity(复杂性),ambiguity(模糊性)


易变性:
当今社会的变化,速度越来越快越来越不可预测。

一个变量的变化,十分容易引起另外的变量进行变化,且这种变化十分频繁。事物很难再保持较长的稳定。我们面对易变性要做好各方面的预备,保持良好的输入输出的习惯,积极迎合事物的变化,增强我们的反脆弱性。
产品经理需要自己的“敏捷开发”,你真的会吗?


不确定:
面对易变化的时代,我们以往的经验无法或说难以在适用现在时代的场景。尽管,以往的经验依然有很强的参考性,但面对现在这个因为易变化而充斥不确定的时代之前的经验,我们不能尽信。

对此,我们要尽可能的扩张信息知识面,对已收集信息进行加工和分析,以求通过扩大分析网络而降低这些不确定性,这也是我们常说的竞品分析。

产品经理需要自己的“敏捷开发”,你真的会吗?



复杂性
互联网给我们的生活带来飞速发展的同时,我们所面对的信息越加庞大;同时,增量的不光是信息还有问题,各种问题相互牵制,牵一发而动全身的情况越发明显。

单一的解决方案已经无法满足我们,因此我们需要定期进行复盘,重组。积累沉淀定向的专业知识,以垂直专业性应对复杂问题。

产品经理需要自己的“敏捷开发”,你真的会吗?



模糊性:
模糊性是最直接明了的,现在的时代我们对于事物的定义和边际之间变得更加的模糊不清,之前,我们非黑即白的判定也越加不再适用。

现在,我们需要去寻求那些第三选择。通过了解事物的因果,去主动地尝试失败,掌握失败的结果,在这些结果中寻找新的解决之道,并从模糊性中找到确定性。

特别是在今年2020年,因为疫情,公司裁员、项目延期被砍、资金断裂似乎成了平常事。互联网从业人员几乎人人自危,这也就加速了我们对vuca时代认知。

面对vuca时代,敏捷开发的低成本、快速试错、用户需求为引导的开发模式似乎十分契合时代特性。

如果我们进一步思考,为什么敏捷开发如此契合?

这就要从敏捷开发的由来,国外咨询公司说起。国外的咨询公司说直白一点就是高端的外包公司。从提供个性化定制软件到战略决策,他们都提供相对应的解决方案。

如此,他们也会和我们一样需要面对多变的用户(顾客、甲方),也会遇见我不知道我想要什么但是我就是有钱的金主,就在面对多变和不知道我想要什么的背景下敏捷开发自然而然孕育而生。

产品经理需要自己的“敏捷开发”,你真的会吗?



小故事

有个很典型的说敏捷开发的外包小故事,一天用户(甲方)找到资深咨询公司说,我要一个埃及金字塔我出钱你们帮我搞一搞,面对这个直接要什么的情况资深咨询公司肯定不会直接开动做一个胡夫金字塔给用户,而是尝试问用户你要金字塔的做什么?


用户说到:要金字塔当然是做墓地啦。咨询公司接着说到:你看你要墓地,我们先给你做个棺材,先看看效果,看合不合适还缺什么,我们到时候修改就行。甲方想了想也就同意了,随后咨询公司给用户做了个棺材。


用户看见棺材发觉目的达到了但是不够威严,和埃及金字塔差了十万八千里,便说要和金字塔一样威严。这时咨询公司说:你看金字塔有很多的人面狮身雕像,所以感觉有威严,你看这样我给你棺材也整个人面狮身的雕像,同时还给你配上兵马俑,让你选择看那个更符合你。


用户听后感觉不错还有的选,就用同意了。等木乃伊后兵马俑都出来后,用户对比了下,用户发现更喜欢兵马俑,更符合国人的身份,但又觉得几个兵马俑感觉没排面,想更有排面点。之后咨询公司给他搞了个兵马俑方阵(高达方阵.....)....


好了,上面小故事形象的说出敏捷开发的核心,就是就可能拆解需求,将需求聚焦到小范围中,不停的发布可观察(可使用)的产品进行交付,收集用户反馈,在进行下次的开发。同时这里我们不要忽视了,因为需要和用户(甲方)频繁的确认交付物,所以用户是可以感知进度的,而这些进度都是需要用户给钱的,这样会比直接交付给的钱更多(资本的力量)。


至此我们可以了解到,用户需求的变化其实与当前时代的特性存在着高度的共性。首先是不确定性:用户不知道我想要什么,到底想要的是墓地还是高达兵团或者是其他的。

其次是易变性:原本想要金字塔最后做了个秦式陵墓,最后可能还会是宇宙飞船。随后便是复杂性:单人兵马俑无法满足用户的需求,用户还是会追寻兵马俑方阵,甚至是给每一位兵马俑配备上现代化的步枪,坦克。最后是模糊性:需求的本质很简单,但实际结果因为表现而变得模糊不清。

那么如此推崇的敏捷开发为何在一些大公司其实并不完全受用?


 3 
流程敏捷和思维敏捷

在引入敏捷开发后,敏捷开发便在国内迅速崛起,初步成为屈指一数的开发模式。

但大部分都是“伪敏捷”,所谓的“伪敏捷”就是直接抄作业,将国外咨询公司那一套流程直接照搬到国内,并直接在团队内部或公司内部进行推广,不去考虑公司“基因”和敏捷开发的“基因”是否相契合。

直接造成公司团队内部怨声载道,产品、ui和测试每周为了冲刺任务(敏捷开发中每次定的小目标)进行加班输出,周而复始,与流水线工人毫无意义(这里没有鄙视流水线工人的含义,我想表明我们本该利用专业技能“脑动力”创造价值,而非手上的劳动力),这就是流程敏捷。

说流程敏捷存在的问题并不是否定流程敏捷,而是我们需要注意他的基因是否与我们自己相匹配。

我们要知道敏捷开发本就从咨询公司衍生而出,本身就带着一定咨询公司的“基因”,所以在拥有同“基因”的小公司、创业公司和内部孵化团队中便十分合适。

对于稍大点的公司,直接执行“伪敏捷”祸害深远。

比如,在流程敏捷过程中,作为产品经理的我们经常没有目标,不知道我们到底要做的是什么,而是不断的通过收集反馈,最后根据用户的反馈来输出下个版本,一旦遇见问题,就说这是敏捷开发要面对的问题,直接就把敏捷开发当成“盾牌”。

或者是每天为了忙而忙,每次冲刺输出尽是一些不健全的demo,随后在后续迭代中丢失等等。那么面对这些问题,真的敏捷开发到底该是怎样的?

我认为真的敏捷开发应该就像马克思主义进入中国变成中国特色社会主义一样,应该根据中国互联网特点形成中国特色敏捷开发(其实就是不要乱抄作业,抄作业也要改下名字,不然连名字都抄就过分了)。

在国内,我们的公司常分为产品部门、业务部门、研发部门、测试部门、数据部门等等,每个部门各司其职。

当需要进行敏捷开发时,我们是在每一个部门抽调人员暂时组成一个小团体组成敏捷团队,这样可以进行跨部门协同,这是一个现象。

我们常围绕这个现象做大团体的敏捷,除了做大团体的敏捷我们还需要做思维上产品的敏捷。我们要将大团体再次依据职能分成小团体,例如:产品、设计、研发和测试。

在每次小团体交付给下个小团体之前,如产品小团体交付给设计小团体前,我们要先做好小团体内部的敏捷任务,等小团体内部敏捷完成后再交付给下个小团队。

对于产品小团体交付给设计小团体前,我们要做需求、方案、原型三个方面的敏捷冲刺。

下面是主要思考的核心,为了避免干扰思考,尽可能不在放图。


需求敏捷

面对多变的用户和需求,我们不能只是记录用户遇见了什么问题,有个什么功能就好了。而是通过小故事的形式将用户所处的场景、问题、期望方案记录下来。

别看只是两种不同的记录形式,这对于我们产品来说却是存在天壤之别。

单记录一个问题和有什么功能(用户期望的解决方案),无法让我们复现问题,固定了我们思维方式。这让我们思考目标直接放在了用户遇见的问题和用户自身提出的解决方案上。

使用小故事的形式来进行记录,我们可以不光可以传达用户遇见的问题,还可以传达遇见问题时用户所处的场景,甚至对于用户自身的属性和路径都能表述出。

在随后思考解决方案的时候,我们有更加广阔的思维空间,更能捕捉问题后面的因果,而非现象的因果。

对于多变的用户,小故事形式记录更能让我们从用户多变的属性中找到不变属性,让我们加深对于用户的理解,在利用同理和共情我们玩完全可以在脑子里面复现场景(就像看小说)。

有了故事,我们可以快速的通过“假象”来进行场景再现,并利用线下实际操作来复现问题,结合产品本身的属性筛选出“真”需求。

到这里需求的敏捷就完成了。但是我们还需要完善两个信息,一个是用户画像,一个是用户路径。

用户画像需要我们尽可能的贴切真实,并且是依据我们小故事创建的。这样会使用户画像更加有真实感,如果我们使用卡通图片,卡通名字和虚假的内容进行填充用户画像,那么这个画像完全起不了用户画像本身指导的作用。

用户路径也就是在小故事中用户的经历,如果把小故事比作游戏场景,那么用户画像是游戏角色,而用户路径就是游戏角色的行为。游戏角色他们可以在游戏中唱、跳、rap.....
咳。所以用户路径很重要。

在梳理用户路径时,我们需要更加注重用户的行为,关注用户事前、事中、事后做了什么。有点像把大象放入冰箱需要几步,例如:先确认大象是真的还是假的(假的玩具大象),在打开冰箱门,拿起玩具大象,放入冰箱,关上冰箱门。

在这个例子中为什么没说需要先走到冰箱前,在打开冰箱门了,因为追求这样的细节反而无意义,毕竟除非特别的行走(跳着走)需要我们进行说明,其他我们只需要使用去上厕所,到了厨房等用词就包含了走路这些细节。

到这里需求的冲刺就完成了,我们也拥有了用户相关的三样东西,故事、画像、路径。至此我们可以进行下个方案的环节。

(过程中也会设计到用户调研,用户访谈,关键是根据自身公司属性进行调整,毕竟不是所有公司都用专门的问题反馈线:客户->Customer Service->Support Engineer->PM->SDM->SDE)


方案敏捷

方案的敏捷需要承接在需求之后,在这里我们了解了用户的故事,知道了用户的画像,掌握了用户的路径之后进行,虽然我们不保证得到的信息是100%正确,但是对我们具有很高的参考价值。

我们要从场景出发,结合实际问题作分析给方案,分析的方式我们一般常用竞品分析进行,这个可以参考我之前写的《竞品分析》文章。

思考分析的结果,将结果具现成实际的解决方案或功能,并将这些功能尽可能的通过可视化的纸和笔记录下来,再去评估这些方案,选出方案中的最优解,推进到下个环节。

上面说到的最优解是指,根据实际情况,考虑方案的时间成本、研发成本、资金成本以及预期效果综合得到的结果(可以说是最能自圆其说的方案)。

原型敏捷
说到原型敏捷就不得不提一些现象,我们经常吐槽一些公司动不动就要做一个功能庞大却完善的产品,从不去根据用户反馈来做产品,这些常常做出来之后就直接凉了。

殊不知这和我们直接画完所有的原型再去和其他人对接,随后频繁修改原型,最终几乎完全重构一样,没有太大的区别,唯一的区别可能是前者成本更高而已。

面对这种情况,需要我们产品以最小成本,最大化复刻功能。也就是想尽办法用最“假”的原型去验证“真”的功能。一般常使用纸、笔和白板画来进行呈现,将所涉及的页面、功能和逻辑等快速的通过可视化出要并于其他人进行沟通。

最后基本确定之后再进行原型的绘制。绘制完毕后并不是代表这个环节就结束,这是我们要进行原型敏捷最核心的一个应为,原型验证。

原型验证的思路其实很简单,大家可以想象一个场景,在一个写字楼大厅,放着这一台自助贩卖机,每当用户选择购买商品并投币支付后,都会有找零钱并给商品,在现在看起来很平常的东西,但实际因为自动贩卖机的老板没钱买真的自动贩卖机,只能自己上场。

产品经理需要自己的“敏捷开发”,你真的会吗?


就像这个自动贩卖机一样,我们不要迫切的将原型推进到下个环节去,而是我们可以利用已有手段结合原型进行一些简单的“验证”,去验证当前的方案是否解决了开头的需求。

如果未解决需求那么我们还需要对方案进行调整,如果已经解决那么我们可以推进到下个环节中。

如果原型实在达不到要求,我们还可以像github一样打个分支,分离出一个平行且相同方案,只是这个方案只执行样式上的变化,对样式进行开发只需要少量的时间即可,而他们的后台逻辑我们通过人工处理。这个原理和自动贩卖机的原理一样。同时这也减少了方案的不确定性和模糊性。

到了这里我们产品的敏捷就结束了,下面将是设计的主场,他们将完成设计的敏捷冲刺,这里就不再过多说明。


 4 
最后

我们该如何选择敏捷模式,它能给我们带来什么?说句实话,上面不适合所有人,上面不适合所有人,上面不适合所有人。

大家毕竟所处的环境各不相同,有的产品部门十几号人,一个项目团队都有几十号人支撑。有的甚至连产品部门都没有,全部统称为研发部,一个项目也才7-8人,甚至更少。

所以这个真不适合所有人,但是我想表述的事,敏捷这东西,在于让我们拆解目标,专注冲刺。那为什么我们不把自己要做的工作也进行拆解冲刺?

选择开发模式这个事情其实我们是无力去改变,只能去被动的接受,除非你真的到了一定的职位,你可以决定开发模式以及产品方向。

不然大部分我们的身份都是一个个互联网“流水线”的打工人,在外部不能改变的情况下,我们只有对自身进行改变,将自身看待问题的维度频率调节的与外界相通(都是敏捷)。

这样才能在完成工作的同时又一定的收获,而不是盲目在“流水线”上做相同的事情。

自身产品的敏捷,只是强迫我们去思考,去溯源场景,让后续的决策都有依据,而不是凭直觉或是看见别人做了我也要这样做,至于为什么这样做,我也不知道。

所以他只是暂时的方案,而不是解决万事的方法。


- END -

英文ID:wcofPM







以上是关于分页列表缓存,你真的会吗的主要内容,如果未能解决你的问题,请参考以下文章

pdf转换为word文件,你真的会吗?

Python代码编写规范,你真的会吗?

系统及其内核修复你真的会吗?

冒泡排序,你真的会吗?

数据分析很重要,但你真的会吗?

大数据依然很火,但是你真的会吗?