Redis的使用以及常见问题详解

Redis的数据类型

1.五大数据类型:

       string :(字符串)可以用来做最简单的数据,可以缓存某个简单的字符串、某个json格式的字符串、Redis分布式锁、计算器、Session共享、分布式ID

       hash:(哈希表) 可用来存储一些KV值,存储对象

       list:(列表)Redis的列表通过命令的组合,可以用来当作栈、队列,也可以用来缓存类似微信公众号、微博等消息流数据

       set:(集合)和列表类似,也可存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,共同好友、朋友圈点赞等功能。

       特点 :元素不重复、无重复、无序

       zset:(有序集合)元素不重复、无重复、有序,可用来实现排行榜功能。 

2.三种特殊数据类型:

      Geospatial    地理位置

      Hyperloglog  数据统计

      Bitmap(位图,数据结构) 统计用户信息只要是两种状态的业务都可用Bitmap处理。比如签到之类的业务。

   

使用场景

Redis的数据持久化策略有哪些?

RDB

        RDB持久化: 在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储

  fork采用的是copy-on-write技术:

  • 当主进程执行读操作时,访问共享内存
  • 当主进程执行写操作时,则会先拷贝一份数据,执行写操作

   页表:记录虚拟地址和物理地址的映射关系

   优点:恢复大数据集的速度比AOF快、对CPU和内存的影响比较小   

   适用场景:需要做冷备份、对数据恢复要求不高

   AOF

    AOF持久化: AOF机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,在 redis 重启的时候,可以通过回放 AOF 日志中的写入指令来重新构建整个数据集

    AOF默认是关闭的,需要在redis.config中更改配置来开启AOF

    AOF的命令记录的频率也可以通过redis.config文件来配,具体的执行策略如下:

   优点:更加稳定,数据的完整性更好、对数据安全性要求较高

   使用场景:购物车、订单等关键业务

AOF和RDB的优缺点对比

RDB:

优点:数据文件的大小相比于aof较小,使用rdb进行数据恢复速度较快

缺点:比较耗时,存在丢失数据的风险

AOF:

优点:数据丢失的风险大大降低了

缺点:数据文件的大小相比于rdb较大,使用aof文件进行数据恢复的时候速度较慢

缓存

什么是缓存穿透,怎么解决?

        缓存穿透:查询一个一定不存在的数据,redis中查询不到缓存也不会直接写入缓存,就会导致每次请求都查数据库,可能会导致DB挂掉。

解决方案:

   方案一:缓存一个空数据,查询返回的数据为空,仍把这个空结果进行缓存。

                优点:简单     

                缺点:消耗内存,可能导致发生不一致的问题

   方案二:布隆过滤器

              实现方案:Redisson      Guava  

               优点:内存占用较少

               缺点:实现复杂,存在误判

 什么是布隆过滤器?

布隆过滤器可以用于检索一个元素是否存在一个集合中。

bitmap:相当于是一个以(bit)为单位的数组,数组中的每个单元只能存储二进制数0或者1。

      存储数据::id为1的数据,通过多个hash函数获取hash值,根据hash计算数据对应位置改为1。

      查询数据:使用相同的hash函数获取hash值,判断对应位置是否都为1。

但是使用布隆过滤器也存在一定的误判,假如恰好一个不存在数据登经过hash函数的计算,误判存在,则也会导致判断的失误。误判率:数据越大误判率越低,反之则越高。但是同时也会带来内存过多的消耗。

解决方式:设置误判率,不超过5%以内或者增加数组长度

     推荐使用redisson实现布隆:

        它的底层主要先去初始化一个较大的数组,里面存放二进制,在一开始都是0,当一个key来了之后,经过3次hash计算,模于数组长度找到数据的下标,然后将数组中的原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在,查找过程也是一样的。

  缺点:可能产生一定的误判,通过设置误判率(不超过5%以内)或者增加数组长度来解决

误判率p受3个因素影响:二进制位的个数m,哈希函数的个数k,数据规模n

什么是缓存击穿,怎么解决?

缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并返回数据到缓存中,这个时候大量并发的请求可能会瞬间把DB压垮。

解决方案有两种:

   一、互斥锁(分布式锁)

          当缓存失效时,不立即去加载DB,先使用Redis的setnx去设置一个互斥锁,当操作成功返回时在进行加载DB的操作并回设缓存,否则重试get缓存的方法

         缺点:如果选择数据的强一致性,建议使用分布式锁的方式,性能上可能没有那么高,锁需要等,也有可能产生生死锁的问题。(涉及钱之类的业务推荐使用)

二、逻辑过期

  基本思路:

1、在在设置key时,设置一个过期时间字段,一块存入缓存中,不给当前key设置过期时间

2、当查询的时候,从redis取出数据后判断时间是否过期   

3、如果过期则开通另一个线程进行线程同步,当前线程正常返回数据,这个数据不是最新

    缺点:若优先考虑高可用性,性能比较高 ,但是数据同步上做不到强一致

什么是缓存雪崩,怎么解决?

       缓存雪崩是指同一段时间大量的缓存key同时失效(采用了相同的过期时间)或者redis服务宕机,导致大量请求到达DB,DB瞬间压力过重雪崩。

雪崩是很多key,击穿是某一个key

解决方案:

1、主要是可以将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值。比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的时间。(即给不同的Key的TTL添加随机值)

2、利用Redis集群提高服务的可用性

3、给缓存业务添加降级限流的策略(降级可作为系统的保底策略,适用于穿透、击穿、雪崩)

4、给业务添加多级缓存

redis双写一致问题?

即要求DB中的数据和redis中的数据保持一致

根据不同的业务情况可分为两种;

  对于实时性要求不高,允许延迟一致的业务,(热点类数据)采用异步的方案同步数据:

        1、使用MQ消息中间件,更新数据之后,通知缓存删除

        2、利用canal中间键不需要需改代码,伪装为mysql的一个从节点,当完成更新之后,canal会读取binlog数据的客户端获取到数据,更新缓存即可。

强一致性的,采用Redisson提供的读写锁

      1、共享锁:读锁readLock,加锁之后,其他线程可以共享读操作

      2、排他锁:(独占锁writerLock)加锁之后,阻塞其他线程的读写操作

场景:

      需要数据库和redis中的数据保持高度一致,因为要求时效性比较高,所以采用读写锁保证的强一致性。

   采用的是redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,他是读写,读读都互斥,这样就能保持在写数据的同时是不会让其他线程读数据,避免了脏数据。这里面注意的是读写方法上都需要使用同一把锁才行。

     排他锁的底层使用的是setnx,保证了同时只能有一个线程操作锁住的方法。

延迟双删:先删除Redis缓存数据,再更新MySQL,延迟几百毫秒再删除Redis缓存数据。

     为什么不使用?

     延时时间不好把控,在延时的过程中可能会出现脏数据,并不能保证强一致性,故不选择。

    

数据过期

Redis的过期键的删除策略?

     Redis是key-value数据库,我们可以设置redis中缓存的key的过期时间。redis的过期策略就是指当redis中缓存的key过期了,Redis如何处理。

   Redis的过期删除策略:惰性删除+定期删除两种策略进行配合使用。

惰性过期:

   惰性删除:设置该key的过期时间后,则不再去管他,只有当访问一个key时,才会判断该key是否已过期,过期则清除

   优点:该策略可以最大化的节省CPU资源。对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。

   缺点:对内存不友好。极端情况下可能会出现大量key过期没有被再次访问,从而不会被清除,占用大量内存。

定期过期

      定期删除:每隔一定时间,会扫描一定数量的key删除里面过期的key。该策略是一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

     定期清理有两种模式:

  • SLOW模式是定时任务,执行频率默认为10HZ,每次不超过2 5ms,已通过修改配置文件redis.conf的hz选项来调整这个次数
  • FAST模式执行频率不固定,单两次间隔不低于2ms,每次耗时不超1ms

优点:可以通过限制删除操作执行的时长和频率减少删除操作对CPU的影响,另外定期删除,也能有效释放过期键占用的内存

缺点难以确定删除操作执行的时长和频率

淘汰策略

数据的淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某种规则将内存中的数据删除掉,这种数据的删除规则被称为内存的淘汰策略。

Redis支持8种不同策略来选择要删除的key:

  • noeviction:不淘汰任何key,但是当内存满时不允许写入新的数据,默认使用
  • volatile-TTL:对设置了TTL的key,比较key的剩余TTL,TTL越小越先被淘汰
  • allkeys-random:对全体key,随机进行淘汰(数据访问频率不大,没有明显区分冷热数据,则推荐使用)
  • volatile-random:对全体设置了TTL的key,随机进行淘汰
  • allkeys-LRU:对全体key,基于LRU算法进行淘汰(推荐使用,保留最近最常访问的数据,若业务有明显的冷热区分,则使用)
  • volatile-LRU:对全体设置了TTL的key,基于LRU算法进行淘汰(如有置顶业务,则不设置过期时间,可保证一直置顶)
  • allkeys-LFU:对全体key,基于LFU算法进行淘汰
  • volatile-LFU:对全体设置了TTL的key,基于LFU算法进行淘汰

LRU最近最少使用。用当前时间减去最后一次访问时间,这个值越大淘汰优先级越高

LFU最少频率使用。会统计每个key的访问频率,这个值越小淘汰优先级越高

分布式锁

分布式锁使用的场景:集群情况下的定时任务、抢单、幂等性场景

Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则SET)的简写。

获取锁的方式:# 添加锁,NX是互斥,EX是设置超时时间 SET lock value NX 10;

                         # 释放锁,删除即可  DEL key

redisson实现的分布式锁-执行流程:(setnx和lua脚本:保证原子性)

加锁、设置过期时间等操作都是基于lua脚本完成的

      由于Redis的单线程,用了setnx命令之后,只能有一个客户端对某一个key设置值,在没有过期或者删除key的时候,其他客户端是不能设置这个key的。

控制redis实现分布式锁有效时长:

     采用的是redis的一个框架redisson实现的。

在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还未执行完的时侯,早redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果还持有锁就增加持有时间,当业务执行完之后需要使用释放锁就可以了。(WatchDog默认每隔10秒续期一次)

    还有一个好处就是在高并发下。一个业务有可能会执行的很快,先客户1持有锁时,客户2来了以后它并不会马上拒绝,他会选择不断尝试获取锁,如果客户1释放完之后,客户2就可以马上持有锁,性能也得到了提升。

redis实现的分布式锁是否时可重入的

      是的可重入的,这样做是为了避免死锁的产生,这个重入其实在内部就是判断是否是当前线程持有的锁,如果当前线程持有锁就会计数,如果释放锁就会在计算上减1,在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数。

Redisson锁不嫩解决主从数据一致的问题:     

         但是可以通过redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现分布式锁。

       (RedLock)红锁:不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n/2+1),避免在一个redis实例上加锁。缺点:实现复杂、性能差、运维复杂。

   根据需求选择不同的数据库:

      AP思想(redis): 要求最终一致性

      CP思想(zookeeper):强一致性

  

计数器

保存token

消息队列

延迟队列

redis集群及事务相关问题

集群

Redis都存在哪些集群方案?

1、主从复制

  • 保证高可用性

  • 实现故障转移需要手动实现

  • 无法实现海量数据存储

2、哨兵模式

  • 保证高可用性

  • 可以实现自动化的故障转移

  • 无法实现海量数据存储

3、Redis分片集群

  • 保证高可用性

  • 可以实现自动化的故障转移

  • 可以实现海量数据存储

事务

        首先,我们要清楚redis中的事务不同于MySQL中的事务,redis中的单条命令保证原子性,但是,Redis中的事务不保证其的原子性,也不存在事务隔离级别之类的概念。

       Redis中的事务并不是直接进行的必须通过EXEC命令才能执行

 Redis事务:

         开启事务(multi),总是返回OK。multi执行之后,客户端可以继续向服务端发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列之中,当EXEC命令被调用时,所有队列中的命令才会被执行。

         命令入队(进行赋值):例如进行GET SET 值之类的命令,会返回一个QUEIED,表示入队成功。

         执行事务(EXEC):执行事务块内的命令。返回事务块内所有命令的返回值,按照命令的先后顺序排列。当操作被打断时,返回空值nil; 每次事务执行完毕之后,必须再次重新开启事务

          WATCH命令(乐观锁):可以为Redis提供check-and-set(CAS)行为,可以监控一个或多个键,一旦其中有一个或者多个键被修改或者删除,之后的事务就不会执行,监控一致持续到EXEC命令。

         通过调用Discard,客户端可以清空事务队列,并放弃执行事务,并且客户端从事务状态中退出。

         Unwatch取消watch对所有key的监控

事务中的异常:

    1、编译时异常:即代码存在问题,则所有的命令都不会进行执行

    2、运行时异常:(1/0)如果事务队列中存在语法性错误,那么在执行命令的时候,其他命令还是可以正常执行的,错误命令抛出异常。

                      

  

  

Redis为什么快?

1、完全基于内存

2、采用单线程避免不必要的上下文切换可竞争条件

3、数据简单,数据操作也相对简单

4、使用多路I/O复用模型,非阻塞IO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值