redis话术

Redis话术

1.请介绍一下你对redis的理解以及在项目中的应用

redis是一个基于key-value的非关系型数据库,支持两种持久化方式(rdb和aof),支持5种数据类型。

在之前的项目中,我们会使用redis做缓存数据库来加快请求的响应速度,也会利用redis数据可以设置有效期的特性来做一些功能。

  • 使用String缓存首页商品分类菜单
  • 使用Hash缓存大广告位信息
  • 使用redis来做分布式锁
  • 使用redis来保存token

2.Redis是什么

简述:Redis是一个高性能的键值对缓存数据库,是个nosql数据库,提供了5种数据类型供我们使用。另外redis支持两种持久化方式RDB和AOF。

3.nosql数据库是什么?和关系型数据库有什么区别?

nosql是指非关系型数据库,一般用于超大规模数据的存储,相比关系型数据库表与表之前可以设定逻辑关系,而非关系型数据库的话,数据之间是没有什么关系的。我之前在项目中用过的nosql数据库有:redis、mongodb(文档型数据库)。

4.Redis支持的数据类型?

字符串类型

list 我们可以向list的两端添加数据

集合set 存放的数据是无序的,集合中的数据是不重复的,由于它是无序的,所以不能通过下标来获取制定元素

zset(order set) 有序集合

hash 适合存储对象

实际上我们常用的也就是string 和hash

redis是通过key-value存储的 set key value string

hset key value hget key value

5.redis的持久化方式

有RDB和AOF这两种,RDB是一种快照的方式来存储的,这也是redis的默认的持久化方式,每隔一段对数据进行一次存储,默认是15S,这个也可以通过配置文件里修改,这种存储方式性能比较高。

还有一种是AOF是即时性的持久化方式,只要数据发生改变都会保存到硬盘一份,这种方式对数据的保存完整性比较高,但是性能比较差。而RDB存在的问题主要是服务器宕机或者断电,会造成数据丢失。

两种持久化方式可以同时使用。

6.什么是缓存穿透,如何避免?

缓存穿透是指缓存和数据库中都没有数据,而用户不断发起请求则这些请求会穿过缓存直接访问数据库,如发起为id为“-1”的数据或id为特别大不存在的数据。假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRJLC7gI-1621474804617)(assets\1589718533834.png)]

穿透的解决方案

缓存空对象:

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间(避免控制占用更多的存储空间),之后再访问这个数据将会从缓存中获取,保护了后端数据源;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4RwT01bc-1621474804619)(assets\1589718137952.png)]

代码实现

@Override
public TbItem selectItemInfo(Long itemId) {
    
    
    //查询缓存
    TbItem tbItem = (TbItem) redisClient.get(ITEM_INFO + ":" + itemId + ":"+ BASE);
    if(tbItem!=null){
    
    
        return tbItem;
    }

    tbItem = tbItemMapper.selectByPrimaryKey(itemId);
    /********************解决缓存穿透************************/
    if(tbItem == null){
    
    
        //把空对象保存到缓存
        redisClient.set(ITEM_INFO + ":" + itemId + ":"+ BASE,new TbItem());
        //设置缓存的有效期
        redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ BASE,30);
        return tbItem;
    }
    //把数据保存到缓存
    redisClient.set(ITEM_INFO + ":" + itemId + ":"+ BASE,tbItem);
    //设置缓存的有效期
    redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ BASE,ITEM_INFO_EXPIRE);
    return tbItem;
}

7.什么是缓存雪崩,如何避免?

缓存雪崩,是指在某一个时间段,缓存集中过期失效。

比如在商品抢购的时候,缓存里存的商品都是在晚上12点失效,而这时候刚好大量用户来访问这些商品,这时候数据库的压力就会一下上来了

所以在做电商项目的时候,一般是采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过期时间,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些,也能节省缓存服务的资源。

解决方案:

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。

设置热点数据永远不过期。

8.什么是缓存击穿,如何避免?

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个key不停进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案:

  1. 设置热点数据永远不过期
  2. 加分布式锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drLcMfL1-1621474804620)(assets\20190520171934776.png)]

1、如何释放锁?del

2、业务处理失败?expire

代码实现

/**
     * 分布式锁
     * @param key
     * @param count
     * @param value
     * @return
     */
    public Boolean setnx(String key, Object value, long time) {
    
    
        try {
    
    
            return redisTemplate.opsForValue().setIfAbsent(key, value, time,
                                                           TimeUnit.SECONDS);
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }
/**
	 * 查询商品信息
	 * @param itemId
	 * @return
	 */
    @Override
    public TbItem selectItemInfo(Long itemId){
    
    
        //1、先查询redis,如果有直接返回
        TbItem tbItem = (TbItem) redisClient.get(ITEM_INFO+":"+itemId+":"+BASE);
        if(tbItem!=null){
    
    
            return tbItem;
        }
        /*****************解决缓存击穿***************/
        if(redisClient.setnx("SETNX_BASC_LOCK_KEY"+":"+itemId,itemId,30L)){
    
    
            //2、再查询mysql,并把查询结果缓存到redis,并设置失效时间
            tbItem = tbItemMapper.selectByPrimaryKey(itemId);

            /*****************解决缓存穿透*****************/
            if(tbItem!=null){
    
    
                redisClient.set(ITEM_INFO+":"+itemId+":"+BASE,tbItem);
                redisClient.expire(ITEM_INFO+":"+itemId+":"+BASE,ITEM_INFO_EXPIRE);
            }else{
    
    
                redisClient.set(ITEM_INFO+":"+itemId+":"+BASE,null);
                redisClient.expire(ITEM_INFO+":"+itemId+":"+BASE,30L);
            }
            redisClient.del(SETNX_BASC_LOCK_KEY+":"+itemId);
            return tbItem;
        }else{
    
    
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
           return selectItemInfo(itemId);
        }
    }

9.Redis到底是多线程还是单线程?线程安全?为什么效率高?

redis是单线程,线程安全

redis可以能够快速执行的原因:

(1) 绝大部分请求是纯粹的内存操作(非常快速)

(2) 采用单线程,避免了不必要的上下文切换和竞争条件

(3) 非阻塞IO - IO多路复用

但是我最近了解到,在redis6.0中是引入了多线程, 多线程任务可以分摊 Redis 同步 IO 读写负荷 。

10.除了redis,用过memcached的吗?

我知道他俩本质的区别就是Redis除了在内存中保存数据,还会把数据保存到硬盘中重启了服务器redis里的数据还有,但是memcached只保存到内存中,重启了服务器数据就没有了。还有就是应用场景不一样:Redis除了作为NoSQL数据库使用外,还能用做消息队列、数据堆栈和数据缓存等;Memcached适合于缓存SQL语句、数据集、用户临时性数据、延迟查询数据和session等。

  • redis的数据类型比memcached更丰富
  • redis支持持久化,而memcached不支持

11.Redis中数据同步问题

读取数据的时候先从redis里面查,若没有,再去数据库查,同时写到redis里面,并且要设置失效时间。存数据的时候要具体情况具体分析,可以选择同时插到数据库和redis(要是存放到redis中,最好设置失效时间),也可以选择直接插到数据库里面,删除内容时,把对应的redis里的数据的删除掉。第一个人查的时候从数据库里查询,把数据放到的缓存中,第二个人访问就可以直接从缓存中访问数据了。

12.Rdis中的数据淘汰策略,了解吗?

了解过。当redis的内存(默认500G)不足的时候,redis会根据配置的数据淘汰策略淘汰部分key,以保证写入成功。 当无淘汰策略时或没有找到适合淘汰的key时,Redis直接返回out of memory错误。 redis中数据淘汰策略有6种,

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

另外我们 在平时使用时应尽量主动设置/更新key的expire时间,主动剔除不活跃的旧数据,有助于提升查询性能

13.redis是否支持事务

支持。redis事务可以用来一次执行多个命令,批量操作在发送EXEC命令前被放入缓存,在事务中任意命令失败,其余命令仍然是可以执行的。事务可以结合watch命令来使用,在事务执行之前,使用watch监听某些键值,如果在EXEC命令执行之前,键值发生改变,事务就不能成功执行。

multi // 开启事务

set name a

set age 11

exec //事务提交

14.redis能否来做消息中间件

可以。redis有发布订阅机制,另外在5.0的版本中还新增加了一个redis stream数据结构来实现MQ。不过我们项目主要还是使用传统的MQ产品来做的,因为传统的MQ产品在使用上更加便捷一点。

Redis Stream 是 Redis 5.0 版本新增加的数据结构。

Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。

简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。

而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。

15.java如何使用redis?

两种方式:

  • 在之前做的项目中,我们是使用jedis在java中操作redis,使用spring集成jedis
  • springboot项目的话,我们是集成springDataRedis来操作redis

16.redis集群(了解)

据我了解的,redis集群有3种解决方案。

  1. 主从复制

    主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave),如下图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AltH6AQB-1621474804622)(H:/桌面文档/技术话术/Redis/assets/1.png)]

    客户端可对主数据库进行读写操作,对从数据库进行读操作,主数据库写入的数据会实时自动同步给从数据库。

    具体工作机制为:

    1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照(即上文所介绍的RDB持久化),并使用缓冲区记录保存快照这段时间内执行的写命令
    2. master将保存的快照文件发送给slave,并继续记录执行的写命令
    3. slave接收到快照文件后,加载快照文件,载入数据
    4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
    5. 此后master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性

    主从复制的优缺点

    优点:

    1. master能自动将数据同步到slave,可以进行读写分离,分担master的读压力
    2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求

    缺点:

    1. 不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复
    2. master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题
    3. 难以支持在线扩容,Redis的容量受限于单机配置
  2. Sentinel(哨兵)模式

    哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。

    哨兵顾名思义,就是来为Redis集群站哨的,一旦发现问题能做出相应的应对处理。其功能包括

    1. 监控master、slave是否正常运行
    2. 当master出现故障时,能自动将一个slave转换为master(大哥挂了,选一个小弟上位)
    3. 多个哨兵可以监控同一个Redis,哨兵之间也会自动监控
    2

    哨兵模式的优缺点

    优点:

    1. 哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有
    2. 哨兵模式下,master挂掉可以自动进行切换,系统可用性更高

    缺点:

    1. 同样也继承了主从模式难以在线扩容的缺点,Redis的容量受限于单机配置
    2. 需要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点作为备份节点不提供服务
  3. Cluster模式

    哨兵模式解决了主从复制不能自动故障转移,达不到高可用的问题,但还是存在难以在线扩容,Redis容量受限于单机配置的问题。Cluster模式实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y9sMWq7l-1621474804627)(H:/桌面文档/技术话术/Redis/assets/3.png)]

    Cluster采用无中心结构,它的特点如下:

    1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
    2. 节点的fail是通过集群中超过半数的节点检测失效时才生效
    3. 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

    Cluster模式的具体工作机制:

    1. 在Redis的每个节点上,都有一个插槽(slot),取值范围为0-16383

    2. 当我们存取key的时候,Redis会根据CRC16的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作

    3. 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点

    4. 当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了

    Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

17.分布式锁

我们使用redis的setnx来做分布式锁。

 if(redisClient.setnx("SETNX_BASC_LOCK_KEY"+":"+itemId,itemId,30L)){
    
    
     //2、再查询mysql,并把查询结果缓存到redis,并设置失效时间
     tbItem = tbItemMapper.selectByPrimaryKey(itemId);

     /*****************解决缓存穿透*****************/
     if(tbItem!=null){
    
    
         redisClient.set(ITEM_INFO+":"+itemId+":"+BASE,tbItem);
         redisClient.expire(ITEM_INFO+":"+itemId+":"+BASE,ITEM_INFO_EXPIRE);
     }else{
    
    
         redisClient.set(ITEM_INFO+":"+itemId+":"+BASE,null);
         redisClient.expire(ITEM_INFO+":"+itemId+":"+BASE,30L);
     }
     redisClient.del(SETNX_BASC_LOCK_KEY+":"+itemId);
     return tbItem;
 }else{
    
    
     try {
    
    
         Thread.sleep(1000);
     } catch (InterruptedException e) {
    
    
         e.printStackTrace();
     }
     return selectItemInfo(itemId);
 }

EM_INFO+":"+itemId+":"+BASE,null);
redisClient.expire(ITEM_INFO+":"+itemId+":"+BASE,30L);
}
redisClient.del(SETNX_BASC_LOCK_KEY+":"+itemId);
return tbItem;
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return selectItemInfo(itemId);
}




猜你喜欢

转载自blog.csdn.net/techn_panda/article/details/117063468
今日推荐