美文网首页IT@程序员猿媛
读了这篇,还怕掌握不了redis吗?

读了这篇,还怕掌握不了redis吗?

作者: 祥哥去哪里 | 来源:发表于2019-11-03 21:34 被阅读0次

前言:

redis作为被广泛使用的非关系型数据,一直都是一门热门技术。作为后端开发工程师,redis的学习肯定是绕不过的。那么今天就分解下redis的各种数据结构及一些应用场景


头图

数据结构:

既然要分解数据结构,那么首先我么需要知道redis有哪些数据结构。
不同于memcache,redis的数据结构更加丰富。

  1. String:字符串
  2. Hash:散列
  3. List:列表
  4. Set:集合
  5. Sorted Set:有序集合

String

最基本的String类型也是最常用的类型。String类型的操作有有很多我介绍一些我比较常用的

命令 介绍
SET key value 设置指定key的值
GET key value 获取指定key的值
SETEX key seconds value 将值value关联到key,并将key的过期时间设为seconds(以秒为单位)
INCR key 将key中储存的数字增一(如果没有这个key,就创建一个并初始化值为1)
DECR key 将key中存储的数字减一(如果没有这个key,就创建一个并初始化值为-1)
  • SET key value 就是将一个字符串存储到一个键上面去。注意使用这个命令是不能设置过期时间的,往往我们在开发中主要使用编程语言去操作redis。set方法中都是可以选择设过期时间和不设置过期时间。其实在底层是使用的不同的命令
  • GET key value get命令算是String 类型中最常用的命令了,作用就是将存储在数据库中的数据读取出来
  • SETEX key seconds value 同SET 功能大致一样,主要是多了一个可以设置过期时间的功能。大多数情况我们还是会对缓存做过期时间的设置

get,set的主要应用场景就在热数据的储存。比如一个接口,数据很少发生变化,但是获取的频率又非常的高。这个时候我们就可以使用redis将这个数据存储起来。在频繁读取数据的时候直接去redis中读,而不走数据库

$cacheName="hot-cache-name";
$cacheData=$redisClient->get($cacheName);
if(empty($cacheData)){
  //如果没有缓存,则从数据库里去取数
  $hotService=new HotService();
  $data=$hotService->getData();
  //由于使用的是redis的String类型。但往往我们的数据都是数组或者对象。那么只需要做一下序列化就可以完美存入redis了
  $redisClient->set($cacheName,json_encode($data),(60*60));//设置1小时的缓存过期时间
}else{
  // 如果缓存不为空的话,就像数据反序列化为数组。
   $data=json_decode($cacheData,true);
}
return $data;
  • INCR key 将key存储的数字+1。且返回+1后的结果
  • DECR key 与INCR相反,是将key存储的数字-1。且返回-1后的结果

INCR 和 DECR 往往用在记数上面。在INCR可以用来统计点赞。DECR可以用来做错误次数的拦截下面搞两个例子来说明

  1. 简单的点赞。对用户点赞直接统计到缓存中,再使用计划任务将缓存中的数据同步到关系型数据库中
$userId=1;//用户id
$cacheName="good-num-user-".$userId;
$result=$redisClient->incr($cacheName);
return $result;
  1. 密码错误拦截,如果在2分钟内密码错误超过三次就拦截掉,不走数据库验证了
$ip=get_user_ip();//获取用户id的方法
$cacheName=$ip;
$times=$redisClient->get($cacheName);
if(empty($times)){
    //如果没有数据则说明在2分钟内是第一次。那么就设置一个2分钟过期的缓存
    $redisClient->set($cacheName,3,(60*2));
}else{
    //判断是否超过三次
    if($times==-1){//为什么是-1。因为3减3次1 就是-1了
        throw new Exception("对不起,你尝试密码次数过多");
    }
}
//这里假装是判断用户密码是否正确
$checkResult=$userService->checkPassword($userName,$password);
if(!$checkResult){
  $redisClient->decr($cacheName);
  throw new Exception("密码错误,请重试");
}
return "登录成功'

HASH

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

命令 介绍
HGET key field 获取存储在哈希表中指定字段的值
HGETALL key 获取某个hash表的所有字段和值
HLEN key 获取hash表中字段的数量
HSET key field value 将哈希表 key 中的字段 field 的值设为 value
HVALS key 获取哈希表中所有值
HDEL key field 删除hash中某个字段和对应的值
HEXISTS key field 判断一个字段是否存在

hash非常适合存储易变的对象。当一个对象不怎么变化的时候,大可以将对象json序列化后存储在String中。但是如果这个对象的属性值经常变化。那么你就需要将对象反序列化后,修改,然后再序列化存储。麻烦不说,还会带来线程安全的问题。这个时候选用hash就是个不错的办法

我们先设定一个电商购物车的场景。购物车里面有三个重要的要素

  1. 哪个用户的购物车
  2. 购物车里面有哪些商品
  3. 每件商品的数量

然后我们使用hash来玩一下这个购物车

1.新增一个商品到购物车中或者新增一件商品的数量

$goodsId=1;//商品id
$goodsNum=2;//商品数量
$userId=1;//用户id
//判断这件商品是否存在
$result=$redisClient -> hExists($userId,$goodsId);
//如果存在
if($result){
  //读取出原本存在购物车中的商品数量
  $oldGoodsNum=$redistClient->hGet($userId,$goodsId);
  //加上这次会话中加入的商品数量
  $goodsNum+=$oldGoodsNum;
}
$redisClient -> hSet($userId,$goodsId,$goodsNum);
  1. 将某件商品移除购物车
$delGoodsId=1;
$userId=1;
$redisClient->del($userId,$delGoodsId);
  1. 统计商品数(仅商品数,不是商品件数)
$userId=1;
$goodsNum=$redisClient->hLen($userId);
  1. 减少一件商品的数量
$goodsId=1;//商品id
$goodsNum=2;//减少的商品数量
$userId=1;//用户id
/判断这件商品是否存在
$result=$redisClient -> hExists($userId,$goodsId);
//如果存在
if($result){
  //读取出原本存在购物车中的商品数量
  $oldGoodsNum=$redistClient->hGet($userId,$goodsId);
  //判断原商品数量减少之后是否小于0
  if($oldGoodsNum>=$goodsNum){
      //参数合法
      $newGoodsNum=$oldGoodsNum-$goodsNum;
      //将新商品数量存入hash表
      $redisClient -> hSet($userId,$goodsId,$goodsNum);
  }else{
    //这里除了抛异常之外也可以将用户购物车中这件商品删除
    throw new Exception("减少商品数大于购物车的商品数");
  }
}else{
  throw new Exception("商品不存在")
}
  1. 获取购物车所有信息
$userId=1;
$allInfo=$redisClient->hGetAll($userId);

List

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

命令 介绍
LPUSH key value [value2] 将一个或多个值插入到列表头部
RPUSH key value [value2] 将一个或多个值插入到列表尾部
LSET key index value 通过索引设置列表元素的值
RPOP key 移除列表的最后一个元素,返回值为移除的元素
LPOP key 移出并获取列表的第一个元素

其实List的使用,在我之前写的redis秒杀中也有提到。我还是拿那个案例来举例吧。
首先场景是有20件秒杀商品,价格非常犀利,有1万人来抢。我们就用redis的列表来承接秒杀的任务

秒杀分为两步,第一步是装载商品

//首先从数据库中读取需要秒杀的商品id
$killGoodsService=new KillService();
$goodsIdArr=$killGoodsService->getKillGoods($killId);
$cacheListName="killGoodsList";
foreach($goodsIdArr as $goodsId){
  $redisClient->lPush($cacheListName,$goodsId);
}

第二部就是准备接受秒杀

//假装是用户的唯一标识
$uuid=md5(uniqid('user').time());
$listKey="killGoodsList";//装有秒杀商品的list
$orderKey="buyOrder";//用来装秒杀订单的hash key名字
$failUserNum="failUserNum";//用来记录秒杀失败的用人数
//从列表中取出商品(因为redis是单线程的原因,无论多大的压力,都不会出现,一个商品被两个人买到的情况)
if ($goodsId=$redis->lPop($listKey)) {
    //秒杀成功
    //将幸运用户存在集合中
    $redis->hSet($orderKey,$goodsId,$uuid);
}else{
    //秒杀失败
    //将失败用户计数
    $redis->incr($failUserNum);
}
echo "SUCCESS";

SET

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

命令 介绍
SADD key member1 [member2] 向集合添加一个或多个成员
SDIFF key [key2] 返回给定所有集合的差集
SINTER key [key2] 返回给定所有集合的交集
SPOP key 移除并返回集合中的一个随机元素

集合的应用,我举两个比较简单的。
一 利用集合的无序性,用来抽奖。将9999个代表为未中间的数据放进去。再放一个代表中奖的数据。当用户来请求抽奖的时候,就从集合里面取一个,判断是否中奖。

//首先把抽奖数字放进集合
$prizeSet="prizeCacheSet";
for($i=0;$I<=10000,$I++){
  $redisClient->sAdd($prizeSet,$i);
}
//用户进入抽奖环节的时候只需要,从集合中取出随机成员,在比对中奖号码判断是否中奖
$userId=1;
$prizeSet="prizeCacheSet";
$luckNum=$redisClient->sPop($prizeSet);
if($luckNum==999){
  //中奖啦,下面写中奖逻辑
  return "中奖啦";
}else{
  return "很遗憾您没中奖";
}

二 使用集合来做共同好友和二度人脉的推荐。集合提供了多个集合取交集和差集的能力。共同好友的逻辑很简单,就是将两个或多个的好友拿来取合集,就是他们的共同好友了。二度人脉就可以将用户A的所有好友的好友拿出来和用户A的好友取差集。就能取到用户A的二度人脉,就可以做一个好友推荐

$userAFriend=array("zhangsan","lisi","wangwu");
$userBFriend=array("wangwu","zhubajie","sunwukong");
//将用户A和用户B的好友放入各自的集合
foreach($userAFriend as $friend){
  $redisClient->sAdd("userAFriendSet",$friend);
}  
foreach($userBFriend as $friend){
  $redisClient->sAdd("userBFriendSet",$friend);
}  
//取共同好友
$commonFriends=$redisClient-> sInter("userAFriendSet","userBFriendSet");

$userA="zhangsan";
$friendService=new FriendService();
//假装去取了userA的所有好友
$friends=$friendService->getUserFriends($userA);
//假装去取了userA的所有好友的好友
$secondFriends=$friendService->getUserSecondNetwork($userA);
//调用将数组放入结合的方法
$redisUntil->makeArrToSet("friendSet{$userA}",$friends);
$redisUntil->makeArrToSet("secondFriendsSet{$userA}",$secondFriends);
//获取10个还未成为朋友的人脉做推荐
$recommendFriends=$redisClient-> sDiff("friendSet{$userA}","secondFriendsSet{$userA}");
shuffle($recommendFriends);
return array_slice($recommendFriends,0,10);

ZSet

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

命令 介绍
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序

由于有序集合我用过的场景也不多。就介绍一个常见的排行版吧。排行榜肯定大家都非常熟悉了,尤其在游戏中,基本是一个固定元素了。有序集合的 有序性,唯一性,确定性 正好瞒住我们的需求

//现在5位同学,分别是小梅,小明,小米,小猫,小马
//将他们的考试分数计入有序集合
$redisClient->zAdd("testScore",95,"小梅");
$redisClient->zAdd("testScore",92,"小明");
$redisClient->zAdd("testScore",90,"小米");
$redisClient->zAdd("testScore",63,"小猫");
$redisClient->zAdd("testScore",12,"小马");
//通过ZREVRANGE 命令对分数排序,获得排行版
//如果指定0,-1 则代表获取所有同学的信息
$allRankingList=$redisClient->zRevRange('testScore', 0, -1, true);
//只取前三名的成绩
$topThree=$redisClient->zRevRang('testScore', 0, 2, true);

结语

这篇redis介绍文章就写到这里,如果有哪里写的不对的地方,欢迎大佬指出。如果你觉得这篇文章写的不错的话,欢迎点赞加关注。谢谢大家支持

相关文章

  • 读了这篇,还怕掌握不了redis吗?

    前言: redis作为被广泛使用的非关系型数据,一直都是一门热门技术。作为后端开发工程师,redis的学习肯定是绕...

  • 现在还怕吗?不了!

    从万达离开之后,有在一家公司待过短暂的一段时间,从来没有对外说起过,后来变成我的一个心结。 当时的那份工作是:微信...

  • 加油

    加油干,好好干,不放弃 只要把握好这几点那你还怕不成功吗? 你还怕完不成任务吗? 你还怕拿不到好的成就吗? 不在意...

  • 绕口令练习

    今天这个绕口令,难啊,读了很多遍,还是感觉掌握不了,先打卡了,慢慢练习……

  • 【PU】你还怕大雨吗

    *破镜重圆 *建议BGM:你还怕大雨吗-周柏豪 *OOC和BUG都是我的 你还怕大雨吗 00 谁也舍不得 但要勇敢...

  • Java基础:看完这篇你还怕碰到异常吗?

    前言 在日常的开发以及平时的学习练习中,异常相信对于大家来讲并不陌生,但是对于异常的具体使用、底层实现以及分类等等...

  • 数据库--重难

    掌握 SQL熟练DQL语句 msql交互Python redis语句 redis交互Python redis与my...

  • Redis入坟(六)分布式集群,概念、原理、实操

    课程目标1、 掌握 Redis 主从复制的配置和原理2、 掌握 Redis 哨兵机制(Sentinel)原理和实战...

  • 走近源码:Redis命令执行过程(客户端)

    前面我们了解过了当Redis执行一个命令时,服务端做了哪些事情,不了解的同学可以看一下这篇文章走近源码:Redis...

  • 还在犹豫什么!!

    只有自己努力,还怕得不到回报吗

网友评论

    本文标题:读了这篇,还怕掌握不了redis吗?

    本文链接:https://www.haomeiwen.com/subject/hdecbctx.html