经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
Redis 如何批量设置过期时间(PIPLINE的使用)
来源:jb51  时间:2021/11/24 11:07:35  对本文有异议

合理的使用缓存策略对开发同学来讲,就好像孙悟空习得自在极意功一般~

Redis如何批量设置过期时间呢?

不要说在foreach中通过set()函数批量设置过期时间

我们引入redis的PIPLINE,来解决批量设置过期时间的问题。

PIPLINE的原理是什么?

未使用pipline执行N条命令

使用pipline执行N条命令

通过图例可以很明显的看出来PIPLINE的原理:

客户端通过PIPLINE拼接子命令,只需要发送一次请求,在redis收到PIPLINE命令后,处理PIPLINE组成的命令块,减少了网络请求响应次数。

网络延迟越大PIPLINE的优势越能体现出来

拼接的子命令条数越多使用PIPLINE的优势越能体现出来

注意:并不是拼接的子命令越多越好,N值也有是上限的,当拼接命令过长时会导致客户端等待很长时间,造成网络堵塞;我们可以根据实际情况,把大批量命令拆分成几个PIPLINE执行。

代码封装

  1. //批量设置过期时间
  2. public static function myPut(array $data, $ttl = 0)
  3. {
  4. if (empty($data)) {
  5. return false;
  6. }
  7.  
  8. $pipeline = Redis::connection('cache')
  9. ->multi(\Redis::PIPELINE);
  10. foreach ($data as $key => $value) {
  11. if (empty($value)) {
  12. continue;
  13. }
  14. if ($ttl == 0) {
  15. $pipeline->set(trim($key), $value);
  16. } else {
  17. $pipeline->set(trim($key), $value, $ttl);
  18. }
  19. }
  20. $pipeline->exec();
  21. }

项目实战

需求描述

  • 打开APP,给喜欢我的人发送我的上线通知(为了避免打扰,8小时内重复登录不触发通知)
  • 每个人每半小时只会收到一次这类上线通知(即半小时内就算我喜欢的1万人都上线了,我也只收到一次喜欢的人上线通知)

要点分析

  • 合理使用缓存,减少DB读写次数
  • 不仅要减少DB读写次数,也要减少Redis的读写次数,使用PIPLINE

代码实现解析

  • canRecall() 写的比较优雅,先判断是否已发送的标记,再判断HouseOpen::getCurrentOpen(),因为HouseOpen::getCurrentOpen()是要查询DB计算的,这种代码要尽可能少的被执行到,减少DB查询。
  • array_diff() 取差集的思路,获得需要推送的人

封装工具类

  1. <?php
  2. namespace App\Model\House;
  3. .
  4. .
  5. .
  6. class HouseLikeRecallUser
  7. {
  8. protected $_userid = '';
  9. protected $_availableUser = [];
  10. protected $_recallFlagKey = '';
  11.  
  12. const TYPE_TTL_HOUSE_LIKE_RECALL = 60 * 30; //半小时后可以再次接收到喜欢的xxx进入通知
  13. const TYPE_TTL_HOUSE_LIKE_RECALL_FLAG = 60 * 60 * 8; //8小时重复登录不触发
  14.  
  15. //初始化 传入setRecalled 的过期时间
  16. public function __construct($userid)
  17. {
  18. $this->_userid = $userid;
  19. //登录后给喜欢我的人推送校验:同一场次重复登录不重复发送
  20. $this->_recallFlagKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL_FLAG, $this->_userid);
  21. }
  22.  
  23. //设置当前用户推送标示
  24. public function setRecalled()
  25. {
  26. Cache::put($this->_recallFlagKey, 1, self::TYPE_TTL_HOUSE_LIKE_RECALL_FLAG);
  27. }
  28.  
  29. //获取当前用户是否触发推送
  30. public function canRecall()
  31. {
  32. $res = false;
  33. if (empty(Cache::get($this->_recallFlagKey))) {
  34. $houseOpen = HouseOpen::getCurrentOpen();
  35. if ($houseOpen['status'] == HouseOpen::HOUSE_STATUS_OPEN) {
  36. $res = true;
  37. }
  38. }
  39. return $res;
  40. }
  41.  
  42. //获取需要推送用户
  43. public function getAvailableUser()
  44. {
  45. //获得最近喜欢我的用户
  46. $recentLikeMeUser = UserRelationSingle::getLikeMeUserIds($this->_userid, 100, Utility::getBeforeNDayTimestamp(7));
  47.  
  48. //获得最近喜欢我的用户的 RECALL缓存标记
  49. foreach ($recentLikeMeUser as $userid) {
  50. $batchKey[] = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
  51. }
  52.  
  53. //获得最近喜欢我的且已经推送过的用户
  54. $cacheData = [];
  55. if (!empty($batchKey)) {
  56. $cacheData = Redis::connection('cache')->mget($batchKey);
  57. }
  58.  
  59. //计算最近喜欢我的用户 和 已经推送过的用户 的差集:就是需要推送的用户
  60. $this->_availableUser = array_diff($recentLikeMeUser, $cacheData);
  61. return $this->_availableUser;
  62. }
  63.  
  64. //更新已经推送的用户
  65. public function updateRecalledUser()
  66. {
  67. //批量更新差集用户
  68. $recalledUser = [];
  69. foreach ($this->_availableUser as $userid) {
  70. $cacheKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
  71. $recalledUser[$cacheKey] = $userid;
  72. }
  73. //批量更新 设置过期时间
  74. self::myPut($recalledUser, self::TYPE_TTL_HOUSE_LIKE_RECALL);
  75. }
  76.  
  77. //批量设置过期时间
  78. public static function myPut(array $data, $ttl = 0)
  79. {
  80. if (empty($data)) {
  81. return false;
  82. }
  83.  
  84. $pipeline = Redis::connection('cache')
  85. ->multi(\Redis::PIPELINE);
  86. foreach ($data as $key => $value) {
  87. if (empty($value)) {
  88. continue;
  89. }
  90. if ($ttl == 0) {
  91. $pipeline->set(trim($key), $value);
  92. } else {
  93. $pipeline->set(trim($key), $value, $ttl);
  94. }
  95. }
  96. $pipeline->exec();
  97. }
  98. }

调用工具类

  1. public function handle()
  2. {
  3. $userid = $this->_userid;
  4. $houseLikeRecallUser = new HouseLikeRecallUser($userid);
  5. if ($houseLikeRecallUser->canRecall()) {
  6. $recallUserIds = $houseLikeRecallUser->getAvailableUser();
  7. $houseLikeRecallUser->setRecalled();
  8. $houseLikeRecallUser->updateRecalledUser();
  9. //群发推送消息
  10. .
  11. .
  12. .
  13. }
  14. }

总结

不同量级的数据需要不同的处理办法,减少网络请求次数,合理使用缓存,是性能优化的必经之路。

进一步思考

如果我喜欢的1万人同时上线(秒级并发),我只收到一个消息推送,要避免被通知轰炸,怎么解决这类并发问题呢?

到此这篇关于Redis 如何批量设置过期时间(PIPLINE的使用)的文章就介绍到这了,更多相关Redis 批量设置过期时间内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号