Redis 在 Web 项目中的运用与实践

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常识的同学可参阅以下链接进一步学习。

宣布我的谈论

撤销谈论
表情 插代码

Hi,您需求填写昵称和邮箱!

  • 必填项
  • 必填项
m88 188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 m88 help