Redis作为一个开源的(BSD)基于内存的高性能存储系统,已经被各大互联网公司广泛使用,并且有着诸多的应用场景。本篇文章将基于PHP来详细讲解Redis在Web项目中的主要应用与实践。

缓存

这里所介绍的缓存是指可以丢失或过期的数据。常用的命令有 sethsetgethget,使用redis作为缓存时需要注意一下几个问题:

  • 由于redis的可用内存是有限的,不能容忍redis内存的无限增长,建议设置 maxmemory 最大内存。
  • 在开启maxmemory的情况下,可以启用lru机制,设置key的expire,当到达Redis最大内存时,Redis会根据最近最少用算法对key进行自动淘汰。
  • Redis的持久化策略和Redis故障恢复时间是一个博弈的过程,如果你希望在发生故障时能够尽快恢复,应该启用dump备份机制,但这样需要更多的可用内存空间来进行持久化。如果能够容忍Redis漫长的故障恢复时间,可以使用AOF持久化机制,同时关闭dump机制,这样不需要额外的内存空间。

存储

在web项目中,redis可存储读写非常频繁的数据来缓解MySQL等数据库的压力。redis如果作为存储系统的话,为了防止数据丢失,持久化必须开启。

典型场景

  • 计数器

计数器的需求非常普遍,例如微博点赞数、帖子收藏数、文章分享数、用户关注数等。

  • 社交列表

比如使用Sets结构存储关注列表、收藏列表、点赞列表等。

  • Session

借助redis高性能的key-value存储,可将用户登录状态保存到redis中。

队列

简单队列

一般使用redis的list结构作为队列,rpush 生产消息,lpop 消费消息,当 lpop 没有消息的时候,要进行适当的sleep操作。

$queueKey = "queue";// 生产者
$redis->rpush($queueKey, $data)// 消费者
while (true) {$data = $redis->lpop($queueKey);if (null === $data) {usleep(100000);continue;}// 业务逻辑...
}

由于没有消息时使用的sleep事件不好控制,生产环境尽量不要使用sleep来休眠,可使用 blpop 来消费消息,在没有新消息的时候它会阻塞到消息到来。

延时队列

延时队列可使用redis的 sorted set 数据结构,使用时间戳作为 score ,消息内容作为 member,使用 zadd 命令来生产消息,消费者使用 zrangebyscore 命令获取指定时间之前的消息数据轮询进行处理。

$queueKey = "queue";// 生产消息// 消费时间, 这里设置为1小时候
$consumeTimestamp = time() + 3600;
// $data需要添加随机串前缀(or后缀),防止出现重复member被丢弃
$data = $data . md5(uniqid(rand(), true));
$redis->zadd($queueKey, $consumeTimestamp, $data);// 消费消息
while (tue) {$arrData = $redis->zrangebyscore($queueKey, 0, time());if (!$arrData) {usleep(100000);continue;}// 业务逻辑foreach ($arrData as $data) {$data = substr($data, 0, strlen($data) - 32);// 消费$data}
}

多消费者

使用pub/sub主题订阅者模式,可以实现1:N的消息队列。这种模式中在消费者下线的情况下,生产的消息会丢失,在这里不推荐使用。

需要强调的是不推荐使用redis作为消息队列服务,这不是redis的设计目标。如果一定要用可考虑 disque,是由redis的作者开发。

分布式锁

分布式锁主要解决的几个问题:

  • 互斥性: 同一时刻只能有一个服务(或应用)访问资源
  • 安全性: 锁只能被持有该锁的服务(或应用)释放
  • 容错: 在持有锁的服务crash时,锁仍能得到释放
  • 避免死锁

方案1

我们可能会考虑使用 setnx 和 expire 命令来实现加锁,即当没有key存在时才会成功写入value:

$lockStatus = $redis->setnx($lockKey, 1);
if (1 === $lockStatus) {// 加锁成功,为锁设置超时时间$redis->expire($lockKey, 300);// 进行后续操作} elseif (0 === $lockStatus) {// 加锁失败
} else {// 其他异常
}

但这种操作不是原子性的,如果在进行setnx时服务崩溃,没有来得及对Key进行超时设置,该锁将一直无法释放。

方案2

我们推荐 set key value [EX seconds] [PX milliseconds] [NX|XX] 命令来进行加锁

  • EX: key在多少秒之后过期
  • PX:key在多少毫秒之后过期
  • NX: 当key不存在的时候,才创建key,效果等同于setnx
  • XX:当key存在的时候,覆盖key
$lockStatus = $this->redis->set($lockKey, 1, "EX", 30, "NX");
if ("OK" === $lockStatus) {// 加锁成功,可进行后续操作//业务逻辑执行完毕,释放锁$this->redis->del($lockKey);} elseif (null === $lockStatus) {// 加锁失败
}

如上代码所示,如果 set 命令返回OK,那么客户端就可以获得锁(如果返回null,那么应用服务可以在一段时间之后重新尝试获取锁),并且可以通过 del 命令来释放锁。

此方法需要注意的问题:

  • a服务获得的锁(键key)已经由于已到过期时间被redis服务器删除,但是这个时候a服务还去执行DEL命令。而b服务经在a设置的过期时间之后重新获取了这个同样key的锁,那么a执行 del 就会释放了b服务加好的锁。
  • 当同一时刻有大量的key过期的时候,删除key时会增加redis压力,会影响服务稳定。

可以通过如下优化使得上面的锁系统变得更加健壮:

  • 不要设置固定的字符串,而是设置为随机的大字符串,可以称为token。
  • 通过脚本删除指定锁的key,而不是 del 命令。
  • 在设置key过期时间的时候加上一个随机值。

优化后的代码可参考如下:

$lockToken = md5(uniqid(rand(), true));
// 此处超时时间根据具体业务逻辑配置
$expire = rand(280, 320);
$lockStatus = $this->redis->set($lockKey, $lockToken, "EX", $expire, "NX");
if ("OK" === $lockStatus) {// 加锁成功,可进行后续操作// 业务逻辑执行完毕,释放锁// 删除锁之前需要判断是否是自己上的锁$currentToken = $this->redis->get($lockKey);if ($currentToken === $lockToken) {$this->redis->del($lockKey);}} elseif (null === $lockStatus) {// 加锁失败
}

计算

redis提供的原子自增减方法以及有序集合结构等可以承担一些计算任务,例如浏览量统计等。

浏览计数

文章浏览量+1

$redis->incr($postsKey);

批量获取文章浏览量

$arrPostsKey = [//...
];
$arrPostsViewNum = $redis->mget($arrPostsKey);

排行榜

可以使用redis的有序集合来实现排行榜的功能,score作为权重排序并取前n条记录。

// 存储数据
$sortKey = "sort_key";
$redis->zadd($sortKey, 100, "tom");
$redis->zadd($sortKey, 80, "Jon");
$redis->zadd($sortKey, 59, "Lilei");
$redis->zadd($sortKey, 87, "Hanmeimei");// 获取排行// 由大到小排序
$arrRet = $redis->zrevrange($sortKey, 0, -1, true);// 由小到大排序
$arrRet = $redis->zrange($sortKey, 0, -1, true);

结尾

redis涉及的应用实践非常繁多的,由于篇幅所限无法全部顾及,本文只针对web应用中最常用的几个场景进行了展开介绍,渴望进一步拓展redis知识的同学可参考以下链接进一步学习。

  • Redis官网
  • Antirez

原文链接:Redis在Web项目中的应用与实践
关注作者:Dreamans

Redis在Web项目中的应用与实践相关推荐

  1. Redis在SSM项目中的简单使用

    Redis在SSM项目中的简单使用 一.基于SSM的Redis环境配置 前提是你的开发电脑安装和配置好了redis,如果没安装请看Window配置Redis环境和简单使用 1.1.pom文件中引入re ...

  2. JAVA Web项目中所出现错误及解决方式合集(不断更新中)

    JAVA Web项目中所出现错误及解决方式合集 前言 一.几个或许会用到的软件下载官网 二.Eclipse的[preferences]下没有[sever]选项 三.Tomcat的安装路径找不到 四.T ...

  3. maven mybatis mysql_Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问...

    标签: 本篇内容还是建立在上一篇Java Web学习系列--Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Ja ...

  4. 在java web项目中编写自己的代码生成器

    在java web项目中编写自己的代码生成器 转载于:https://www.cnblogs.com/punisher/p/5909943.html

  5. 在web项目中发布jaxws

    概述 在web项目中发布基于jaxws的webservice. 参考文章:用JAX-WS在Tomcat中发布WebService 参考文章说,如果不是servlet3.0及以上,需要配置servlet ...

  6. Web项目中引进EasyUI的路径问题

    场景 Web项目中引入EasyUI,新建test.html,页面中引入EasyUI相关资源文件. 运行后页面并没有改变,打开检查提示404找不到资源. 实现 项目路径为: 路径引用错误: test.h ...

  7. 由web项目中上传图片所引出的路径问题

    我在做javaweb项目的时候,有个项目中需要进行图片的上传,有次我重新部署项目后,发现之前上传的图片不见了,最后找出原因:图片上传在服务器目录上,而不是绝对路径,所以特别想弄清楚javaweb项目中 ...

  8. maven web项目中的web.xml的版本如何更改

    maven web项目中的web.xml的版本如何更改 问题 因web.xml的版本太低不支持el表达式的问题(maven3.6版本通过底层的maven web插件生成的最终的web.xml文件版本只 ...

  9. 在java web项目中实现随项目启动的额外操作

    前言 在web项目中经常会遇到在项目启动初始,会要求做一些逻辑的实现,比如实现一个消息推送服务,实现不同类型数据同步的回调操作初始化,或则通知其他客户服务器本项目即将启动,等等.对于这种要求,目前个人 ...

最新文章

  1. JavaScript模块化不算漫长的发展史
  2. Python编码风格
  3. 构建人人网社会化推荐引擎
  4. 如何打造一个高效的研发团队
  5. idm站点抓取不了html,IDM站点抓取功能使用详解(下)
  6. python开发cms企业官网用python php_基于Django的Python CMS
  7. Maven项目 cityFileFK02数据库连接(课外完成)
  8. 虚拟机ipv4和6都没访问权限_ipv4无访问权限,小编教你ipv4无internet访问权限怎么办...
  9. 复旦大学python教学视频_B站资源推荐:复旦大学机器学习、深度学习公开课,附PDF课件下载...
  10. 解决source insight3.5的下面窗口丢失的方法
  11. 16春季计算机应用基础,西交16春季《计算机应用基础》在线作业及答案
  12. C语言中的void和void*的定义及用法
  13. windows 开机不进入桌面自动进入自己的程序和恢复桌面显示
  14. php删除管理员,WordPress 移除管理员后台添加用户权限
  15. linux系统创建RAID0、1、10、50
  16. 微信小程序提示db.RegExp is not a function(已解决)附加代码
  17. tp6----结合AJAX实现分页
  18. 求圆的面积(Java实现)
  19. 2021年度十大热门流程图(亿图图示软件)
  20. Feathers 入门

热门文章

  1. c语言教程for,C语言菜鸟基础教程之for循环
  2. YIi 设置 ajax 验证
  3. UnixLinux大学教程目录
  4. mysql引擎和事务
  5. U盘安装CentOS 7错误 /dev/root does not exist, could not
  6. [USACO4.2] 草地排水 Drainage Ditches (最大流)
  7. 【转】Linux编程之UDP SOCKET全攻略
  8. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现
  9. 如何提高个人博客的访问量
  10. java mvel_mvel java和脚本的融合 -- kkito的博客