Redis面试_2

Redis供用户使用的几种数据类型(针对k-v结构中的value)

注意: Redis存储的时key-value键值对格式,其中key永远是字符串,value值可以有多重不同的数据结构
同时,不论value是什么类型,它里面的数据都是字符串类型

  1. String: 最基本的数据类型,二进制安全(可以存二进制的东西,比如图片等),也就是说可以存很多东西.
  2. Hash: String元素组成的字典,适合用于存储对象.
  3. List列表: 按照String元素插入顺序排序,可以当做队列使用.
  4. Set集合: String元素组成的无序集合,通过哈希表实现,不允许重复.
  5. Sorted Set: 通过分数(自己给的,可以重复)来为集合中的成员进行从小到大的排序.
  6. 关于基本数据类型我之前写过一篇博客: redis的基本用法.

问题1: Redis从海量key里查询出某一固定前缀str的key

留意细节: 摸清数据规模,即问清楚边界.

解法1: 如果我们直接回答使用keys指令(keys str*)来扫描出指定模式的key列表的话,会出问题.
如果面试官继续问你:该redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
会卡成皮皮虾

解法2(正确指令):我们可以使用scan指令,实现无阻塞的提取出符合指令模式的key列表.它每次执行只会返回少量元素,所以可以用于生产环境,不会像keys指令那样会卡.

使用keys对线上的业务的影响

KEYS pattern: 查找所有符合给定模式pattern的key.

它有如下缺点:

  1. keys指令会一次性返回所有匹配的key,当数据量很大时就很慢.
  2. 键的数量过大会使服务卡顿.

SCAN指令

命令格式: SCAN cursor [MATCH pattern][COUNT count].

其中,cursor表示游标,MATCH括号里就是我们要查找的模式了,就比如上面的str*,COUNT括号就表示希望每次返回的数量.

  1. SCAN指令是基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程.
  2. 当SCAN的游标参数cursor被置为0的时候,那么它会开始一次新的迭代,即SCAN指令是以0为游标开始直到命令返回游标0完成一次遍历.
  3. SCAN指令并不保证每次执行都返回给定数量的元素,支持模糊查询.
  4. 一次返回的数量不可控,只能大概率符合count参数.
  5. SCAN指令获取到的元素有可能重复,所以外部需要去重.

问题2: 如何通过Redis实现一个分布式锁

集群:多个人在一起作同样的事 。
分布式 :多个人在一起作不同的事 。

分布式锁:是分布式系统或不同系统之间共同访问一个共享资源的一种锁的实现,如果不同系统或同一个系统的不同程序之间共享了某个资源时,往往需要互斥来防止彼此干扰,进而保证一致性.

分布式锁需要解决的问题:

  1. 互斥性: 即任意时间只能有一个客户端获取锁,不能同时有多个客户端获取了锁.
  2. 安全性: 锁只能被持有该锁的客户端删除,不能被其他客户端删除.
  3. 死锁: 如果获取锁的客户端因为某些原因宕机导致未能释放锁,那么其他客户端就再也无法获取到该锁,导致了死锁.
  4. 容错: 当部分节点,比如一些redis节点宕机的时候客户端依然能够获取锁和释放锁.

答案:

  1. 通过SETNX指令加锁,其命令行格式为:SENTX key value, 如果key不存在,则创建锁并赋值.
    其时间复杂度为O(1).
    返回值: 设置成功返回1,设置失败返回0.
    例如(给key为locknx的资源加锁并赋值为test):
    setnx locknx test // 返回1
    setnx locknx task// 试图将locknx改为task,返回0,修改失败.
    get locknx //test
  2. 给setnx设置一个过期时间,其命令行格式为: EXPIRE key seconds,设置key的生存时间,当key过期时(生存时间为0),会被自动删除.
    例如(给key为locknx的资源设置超时时间为20秒):
    expire locknx 20 // 20秒后就可以重新对locknx设置值了.

上述两个操作会出现问题,比如说可能会加锁之后程序出错了,执行不到过期的指令,就会导致死锁

  1. 上述的两个操作在redis2.6.12版本开始,就可以通过一条指令执行了,这就解决了上面的问题.
    命令行: SET key value [EX seconds] [PX milliseconds] [NX | XX]
    其中:
    EX seconds用于设置键的过期时间为second秒.
    PX milliseconds用于设置键的过期时间为milliseconds毫秒.
    NX表示只有当键不存在时,才对键进行设置操作,此时效果等同于setnx.
    XX表示只在键已经存在时,才对键进行设置操作.
    SET操作成功完成时,返回OK,否则返回nil
    此时就满足了分布式锁的原子性需求(要么都提交,要么都不提交)

如果大量key同时过期的注意事项:
如果大量的key过期时间相同,由于清除大量的key很耗时,会出现短暂的卡顿现象.

解决方案:
在设置key的过期时间的时候,给每个key的时间上加上一个随机值,这样清除key的时间节点就会比较分散,就可以很大程度上避免卡顿现象的发生.

如何使用Redis做异步队列
首先,我们应该想到使用List作为队列,RPUSH生产消息(往右侧添加),LPOP消费消息(从左边弹出).

例如:
rpush testList aaa
rpush testList bbb

rpop testList // aaa

缺点:没有等待队列里有值就直接消费,即有可能list中没有值但是却会执行弹出指令,此时会立刻返回nil.

弥补办法1:可以通过在应用层引入sleep机制,适当sleep之后再去去调用LPOP重试.
弥补办法2:使用BLPOP指令.
其格式为: BLPOP key [key …] timeout: 没有消息的时候会阻塞队列,直到队列有消息(当在阻塞时消息进来后就会输出消息并停止阻塞)或者超时.

使用BLPOP/LPOP指令的优缺点:

缺点: 生产的消息只能提供一个消费者消费,即BLPOP/LPOP之后就没了,能不能实现生产一次,供多个消费者消费呢?.

答案是肯定的–通过redis的pub/sub主题订阅者模式

pub/sub主题订阅模式

它可以实现一对多的消费队列.

  • 发送者(pub)发送消息,订阅者(sub)接收消息.
  • 订阅者可以订阅任意数量的频道(topic),当频道中生产消息之后,订阅者都会收到消息.
    在这里插入图片描述

pub/sub的缺点

消息的发布是无状态的,无法保证该消息是否被接收到.
解决这个问题就要使用专业的消息队列,例如kafka等.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值