redis的数据结构、使用场景、单线程模型、持久化方式以及常见面试问题

一、redis中的数据结构

1、字符串(String)

SET key value //存入字符串键值对

MSET key value[key value...] //批量存储字符串键值对

SETNX key value //存入一个不存在的字符串键值对

GET key //获取一个字符串键值

MGET key [key ...] //批量获取字符串键值

DEL key //删除一个键

EXPIRE key seconds //设置一个键的过期时间(秒)

INCR key //将key中存储的数字加1

DECR key //将key中存储的数字减1

使用场景

单值缓存(一般用于库存扣减) SET key value GET key

对象缓存 (常用对象) SET key value(json格式字符串)

分布式锁 SETNX product:10010

计数器(文章阅读量) INCR article:reedcount:0 (因为redis是单线程,能够保证原子性)

Web集群session共享 spring session + redis实现session共享

分布式系统的全局序列号(数据库分库分表???)

2、哈希(hash)类似Map<String Map<String,String>>

HSET key field value //存储一个哈希表key的键值

HGET key field //获取哈希表key对应的field键值

HDEL key field //删除哈希表key中的field键值

HLEN key //返回哈希表key中的数量

HGETALL key //返回哈希表key中的所有键值

HSETNX key field value //存储一个不在的哈希表key的键值

HMSET key field value ... //在一个哈希表key中存储多个键值对

HMGET key field //批量获取哈希表key中的多个键值

使用场景

购物车 以用户id为key 商品id为field 商品数量为value

hset cart:userId productId num(某某用户在购物车中添加某某商品的数量)

a、添加商品 hset: cart:1001 10088 1

b、增加商品 hincrby cart:1001 10088 1

c、商品总数 hlen cart:1001

d、删除商品 hdel cart:1001 10088

e、获取购物车所有商品 hgetall cart:1001

优点:同类数据归类整合存储,方便数据管理、相比string操作消耗内存与cpu更小、相比string存储更节省空间。

缺点:过期功能不能使用在field上,只能用在key上、redis集群架构下不适合大规模使用。

 

3、列表(list)

LPUSH key value[...] // 将一个或者多个value插入到key列表的表头(最左边)

RPUSH key value[...] //同上(最右边)

LPOP key //移除并返回key列表的头元素

RPOP key //移除并返回key列表中的尾元素

LRANGE key start stop //返回列表key中指定区间内的元素,区间以偏移量start和stop指定

应用场景

常用数据结构 Stack(栈) = LPUSH + LPOP FILO(先进后出)、QUEUE(队列) LPUSH + RPOP (先进先出)、Blocking MQ(阻塞队列) = LPUSH + BRPOP。

微信和微博公众号信息流,列如:我关注了楼市新说、备胎说车等公众号。

a、楼市新说发了公众号,消息id为10018,LPUSH msg:{我--ID} 10018

b、备胎说车发微博 消息id为10086 LPUSH msg:{我--ID} 10086

c、查看最新微博消息 LRANGE msg:{我--ID} 0 5

4、集合(set)

SADD key member[member...] //往集合key中存入元素,元素存在则忽略

SREM key member [memeber...]//从集合key中删除元素

SMEMBERS key //获取集合key的所有元素

SCARD key //获取集合key的元素个数

SISMEMBER key member //判断member元素是否存在于集合key中

SRANDMEMBER key [count] //从集合key中选出count个元素,元素不从key中删除

SPOP key [count] //从集合key中选出count个元素,元素从key中删除

Set运算符

SINTER key //交集运算

SUNION key //并集运算

SDIFF key //差集运算

SINTERSTORE des key //将交集结果存入新集合des中

SUNIONSTORE des key

SDIFFSTORE des key

应用场景

微信抽奖小程序

a、点击参与抽奖加入集合 SADD key {userId}

b、查看参与抽奖所有用户 SMEMBERS key

c、抽取count名中奖者 SRANDMEMBER key [count] / SPOP key [count]

微信微博的点赞,收藏,标签

点赞 SADD like:{消息ID} 用户ID、取消点赞 SIDREM like:{消息ID} 用户ID

检查用户是否点赞 SISMEMBER like:{消息ID} 用户ID

获取点赞列表 SMEMBERS like:{消息ID}、获取点赞用户数 SCARD like:{消息ID}

集合操作:哔哩哔哩关注模型

5、有序集合(zset)

二、Reids分布式锁(主要redis是单线程模型)

synchronized(this){
   //获取redis中的库存
   int stock = redisTemplate.opsForValue().get("stock");
   //判断库存是否大于零
   if(stock > 0){
  //减库存
       int realStock = stock - 1;
  //从新往redis中set库存
       redisTemplate.opsForValue().set("stock",realStock);
  }else{
       log.info("库存不足");
  }
}
//该场景只支持单体应用,因为synchronized只针对JVM的Java进程,分布式场景不适用。
{
   //类似jedis.setnx("lockKey","lock")
   String lockKey = "lockKey";
   String uuid = UUID.randomUUID().toString
   try{
       //设置锁超时时间,业务逻辑还没有执行完,锁超时时间已经过了,这时存在风险(锁失效,解锁乱套)。
       //在加锁和解锁之间开辟新线程(定时器不断续命,三分之一),用于续命redis中的lockKey
       Boolean result = redisTemplate.opsForValue().
           setIfAbsent(lockKey,uuid,10,TimeUnit.SECONDS);
       
       if(!result){
               return "系统繁忙,请稍后重试";
        }
       //获取redis中的库存
       int stock = redisTemplate.opsForValue().get("stock");
       //判断库存是否大于零
       if(stock > 0){
           //减库存
           int realStock = stock - 1;
           //从新往redis中set库存
           redisTemplate.opsForValue().set("stock",realStock);
      }else{
           log.info("库存不足");
      }
  }finally{
     
       if(uuid.equals(redisTemplate.opsForValue().get(lockKey))){
           //如果在加锁和解锁之间出现异常,就无法解锁(死锁) 因此使用try{}finally{}
      //如果在加锁和解锁之间web应用挂了(如手动杀进程),redis怎样释放锁,此时设置锁的有效期。
           redisTemplate.delete(lockKey);
      }
 
  }

   
}

{
   try{
       redissionLock.lock(30,TimeUnit.SECONDS);
       //获取redis中的库存
       int stock = redisTemplate.opsForValue().get("stock");
       //判断库存是否大于零
       if(stock > 0){
           //减库存
           int realStock = stock - 1;
           //从新往redis中set库存
           redisTemplate.opsForValue().set("stock",realStock);
      }else{
           log.info("库存不足");
      }
  }finally{
redissionLock.unlock();
  }
}

 

posted @ 2020-03-05 11:03  码农的进击  阅读(127)  评论(0)    收藏  举报