原文连接: Redis在Web项目中的应用与实践
Redis做为一个开源的(BSD)基于内存的高性能存储系统,已经被各大互联网公司普遍使用,而且有着诸多的应用场景。本篇文章将基于PHP来详细讲解Redis在Web项目中的主要应用与实践。git
这里所介绍的缓存是指能够丢失或过时的数据。经常使用的命令有 set
, hset
, get
, hget
,使用redis做为缓存时须要注意一下几个问题:github
maxmemory
最大内存。在web项目中,redis可存储读写很是频繁的数据来缓解MySQL等数据库的压力。redis若是做为存储系统的话,为了防止数据丢失,持久化必须开启。web
典型场景redis
计数器的需求很是广泛,例如微博点赞数、帖子收藏数、文章分享数、用户关注数等。算法
好比使用Sets结构存储关注列表、收藏列表、点赞列表等。数据库
借助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的做者开发。
分布式锁主要解决的几个问题:
方案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]
命令来进行加锁
$lockStatus = $this->redis->set($lockKey, 1, "EX", 30, "NX"); if ("OK" === $lockStatus) { // 加锁成功,可进行后续操做 //业务逻辑执行完毕,释放锁 $this->redis->del($lockKey); } elseif (null === $lockStatus) { // 加锁失败 }
如上代码所示,若是 set
命令返回OK,那么客户端就能够得到锁(若是返回null,那么应用服务能够在一段时间以后从新尝试获取锁),而且能够经过 del
命令来释放锁。
此方法须要注意的问题:
del
就会释放了b服务加好的锁。能够经过以下优化使得上面的锁系统变得更加健壮:
del
命令。优化后的代码可参考以下:
$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在Web项目中的应用与实践
扫码关注微信公众号: Learn2Code