Redis
一、Redis的使用场景
① 缓存 ② 分布式锁 ③限流 ④购物车 ⑤Token存储 ⑥点赞关注 ⑦短信验证码存储 ⑧分布式Session ⑨发布订阅 ⑩排行榜
1、缓存
热点数据(经常查询,但不修改和删除)首选redis,性能高。
2、分布式锁
注:锁,即在多线程环境下,对共享资源的访问造成线程安全问题,通过锁的机制来实现资源访问互斥;比如Java语言有线程锁:synchronize / Lock 等。
分布式锁:在分布式环境下,所有应集群部署,锁(互斥)的范围发生了改变;
分布式锁是一种用于分布式系统中实现同步访问共享资源的机制。在分布式系统中,多个节点需要协调合作完成某项任务时,为避免资源竞争和数据不一致问题,可以使用分布式锁来确保在某一时刻只有一个节点可以访问共享资源。
redis分布式锁实现:① Jedis ② Lttuce ③ RedisTemplete ④ Redisson
一。命令实现:setnx(获取锁) ,expire(设置锁的过期时间),del(删除锁,释放锁)
1、获取锁:setnx key value
2、设置锁的过期时间:expire key 30
3、执行业务代码
4、删除锁:del key
二、Redisson实现
1、获取锁:redisson.getLock("lock");
lock.lock();
2、执行业务代码
3、释放锁:lock.unlock();
Redisson实现的分布式锁是可重入的吗?
注:可重入? 即可重复获取,它是指线程T获取到锁A之后,线程T再次去获取锁A是可以获取到的,Java中的synchronized、ReentrantLock都是可重入锁。
Redisson实现的分布式锁是可重入锁
redis实现分布式锁需要注意的问题?
1、不是原子操作
因为redis获取锁和设置锁的过期时间不是原子操作,在极端情况下就可以会出现问题,如果在获取锁后,redis宕机了,这个时候锁是没有过期时间的,会导致这个锁一直无法释放的情况。所以需要将获取锁和设置锁的过期时间在一个命令下执行,保证原子性。
2、没有释放锁
如果在执行业务代码后,服务突然宕机了,这个时候锁是还没有释放的,所以在获取锁的时候一定要设置过期时间,这样就算服务宕机了,我们没有手动释放锁,也会因为我们设置了锁的过期时间而自动释放锁,如果没有设置过期时间,会导致其他线程一直获取不到锁。
3、释放了锁。但业务还没有执行完
因为redis实现的分布式锁默认过期时间是30秒,但如果业务代码执行完要35秒,但是锁已经释放了,剩下的5秒内如果有线程进来获取锁并执行业务代码,这时就会有两个线程来执行业务代码,这样就会导致数据不一致,比如在减库存的时候会出现超卖的情况。所以锁的过期时间要让续期,redis已经提供了,是锁的过期时间的1/3。
4、释放了别人的锁
当前有两个线程,线程A和线程B,如果设置锁的过期时间是30s,但执行业务代码要35s,并且没有设置锁的过期时间的续期,此时线程A在执行,但锁已经释放了,然后线程B进来执行,因为线程A的锁已经过期了,此时线程A释放的锁是线程B的,这样就产生了释放了别人的锁的情况。在实现分布式锁时,要求自己的锁要自己释放,不允许释放别人的锁。线程可以在获取锁的时候,生成一个唯一的id,然后把这个id设置到key的值里面去,当要释放锁的时候,去判断当前锁是不是当前线程设置的。
5、大量请求竞争锁失败
① 重试,让锁自旋,重试3~5次,每一次获取失败间隔50ms,如果超过5次,就返回获取锁失败。
② 让业务执行尽可能短
③ 限流处理
6、多节点Redis主从复制的问题
假设当前有两个线程,线程A和线程B,并且当前redis是哨兵模式的主从复制,一主二从,当线程A去主服务器去获取key,并且将key同步到两个从节点,但是还没有复制,这个主服务器就宕机了,然后从服务器升级成主服务器,这个时候线程B就开始去这个新的主服务器去获取key,这样两个线程都获取到了锁,都会区执行业务代码,这样数据会有安全问题。可以使用redis提供的红锁来解决。
解决:红锁会向所有的redis节点加锁,但这种方法性能很低,也很复杂,如果要在redis集群环境下使用分布式锁,建议使用zookeeper来实现分布式锁。redis是保证AP,zookeeper是保证CP。
7、锁的性能问题
分段锁。
8、锁的可重入性
Redisson实现的分布式锁就具有可重入性。
3、Token存储
在现在的前后端分离的开发模式下,redis存储token很常见。
用户在前端登录成功后会生成token,并将token写入redis,并且前端也会将token进行保存,后续用户需要进行访问其他接口时,会携带token请求,在后端中经过过滤器,拦截器等校验,最后会判断当前token是否和登录成功时存入redis的token进行比较,一致则将结果返回到前端。
4、短信验证码存储
用户在前端点击发送验证码,在后台生成验证码之后,将验证码存入redis,并且将验证码发送给用户,用户输入验证码后发起请求,将用户输入的验证码和在redis里的验证码进行比对,一致则成功。
5、计数器
6、全局唯一id
7、排行榜
使用redis的Zset数据结构
放入一些数据
将这些数据按score从高到低排序
增加分数
8、限流
redis可以配合lua脚本来对数据进行限流
9、购物车
可以使用redis的hash结构来存储用户的购物车数据
10、点赞关注
点赞可以使用redis的hash结构,关注可以使用redis的set结构。
二、缓存穿透,缓存击穿,缓存雪崩
1、缓存穿透
缓存穿透是由于请求一个不存在的数据而导致的;如果数据库或者redis被一直这样恶意请求一个不存在的数据,会造成数据库的访问压力过大,导致数据库响应过慢,严重甚至会导致数据库宕机。
解决:
① 缓存空值。在缓存中,之所以会发生穿透,就是因为没有将那些不存在的值的key缓存下来,从而导致每次查询请求都要请求到数据库。所以就可以为这样key对应的值设置为null并放到缓存当中,这样再出现查询这个key的时候,直接返回null即可
② 布隆过滤器(BloomFilter)。布隆过滤器用于检索一个元素是否在一个集合中。它是采用一个很长的二进制数组,通过一系列的hash函数来确定该数据是否存在。
布隆过滤器是一种比较巧妙的概率性数据结构,它可以告诉你数据一定不存在或者可能存在,相比Map,Set,List等传统的数据结构它占用内存少、结构更高效。
布隆过滤器添加元素时,会使用多个hash函数对元素进行哈希,然后用数组的长度取余数,算出一个索引位置值,再把数组的位置值设置为1,这样就完成了元素添加
向布隆过滤器查询元素时,和添加元素一样,也会对元素进行多次hash计算出数组的位置,然后查看数组的位置值是否都为1,只要有一个位置值为0,就表示布隆过滤器不存在该元素。如果这几个位置都为1,则表示元素可能存在(并不是一定存在)
③布隆过滤器为什么存在误判?
具体实现:Guava,Hutool,Redisson,手动实现
redisson
标签:面试题,缓存,Java,过期,redis,获取,线程,高频,分布式 From: https://www.cnblogs.com/hwjres/p/18047842