【redis-7】redis高级知识点

一: 性能监控指标

使用info命令可以查看redis状态

connected_clients:68 # 连接的客户端数量
used_memory_rss_human:847.62M # 系统给 redis 分配的内存
used_memory_peak_human:794.42M # 内存使用的峰值大小
total_connections_received:619104 # 服务器已接受的连接请求数量
instantaneous_ops_per_sec:1159 # 服务器每秒钟执行的命令数量 qps
instantaneous_input_kbps:55.85 #redis 网络入口 kps
instantaneous_output_kbps:3553.89 #redis 网络出口 kps
rejected_connections:0 # 因为最大客户端数量限制而被拒绝的连接请求数量
expired_keys:0 # 因为过期而被自动删除的数据库键数量
evicted_keys:0 # 因为最大内存容量限制而被驱逐( evict )的键数量
keyspace_hits:0 # 查找数据库键成功的次数
keyspace_misses:0 # 查找数据库键失败的次数

二:缓存问题

1、缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,比如暴力攻击,查询一个不存在的id,请求穿透redis造成db压力过大。

解决办法:

缓存空对象: 当第一次查询db后,就往redis存空对象。  后来的请求就不会访问db了。

这种情况需要将过期时间设置短一点,但是也不能避免该时段内,出现数据不一致情况。。另外,缓存大量的空对象,也对redis造成浪费。

使用布隆过滤器(Bloom Filter):

在db和redis插入数据时,可以将redis的key存入bloom。 这样在查询前先校验bloom是否存在key,有才查redis和db

直观的说,bloom算法类似一个hash set,用来判断某个元素(key)是否在某个集合中。
和一般的hash set不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。

算法:
1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数
2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。

优点:不需要存储key,节省空间,查询效率很高。 

缺点:
1. 算法判断key在集合中时,有一定的概率key其实不在集合中
2. 无法删除。所以校验key有的时候,实际数据可能已经不存在了。

2、缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),如果是热点数据,可能并发很大,会造成db压力过大。

解决办法:

加锁。  当redis不存在需要从db查时,加锁,获取锁后,再从redis查一次,如果有,释放。如果没有从db查出来,存入redis。

3、缓存雪崩

某一时间大量key失效(比如key同时失效或重启redis),造成db压力过大。

解决办法:

1、做高可用,将数据分散到不通的实例

2、不同的key设置不通的缓存过期时间

3、可以再设置二集缓存

三:分布式锁

使用redis分布式锁,要注意几个最基本的原则:

首先,我们在使用分布式锁的时候,不能只依靠手动解锁,一定需要设置过期时间。 否则,如果程序执行失败,则会造成死锁。

其次,分布式锁的key-value中,value也需要保持唯一(通常跟上当前线程id),用来保证手动解锁的时候,一定是释放掉自己上的锁。

下面来看一下实现redis分布式锁的几种方式:

1、setnx

早期的redis使用setnx,原理是往redis添加一个key-value,如果key不存在则成功。 如果设置成功,再设置过期时间。 但是呢,这些操作都是非原子操作,存在一下问题:

1、查看key不存在后,set值可能失败。

2、set值成功后,设置过期时间可能失败。

3、在master-slave模式中,如果set值到master成功,还没有copy到slave,master挂掉,slave切换成新的master,则其他线程会获取到锁。

4、不能续约,如果执行时间超过过期时间,程序没有完成,也会释放掉锁。

2、set

String set(String key, String value, String nxxx, String expx, long time);
该方法是: 存储数据到缓存中,并制定过期时间和当Key存在时是否覆盖。 

nxxx: 只能取NX或者XX,如果取NX,则只有当key不存在是才进行set,如果取XX,则只有当key已经存在时才进行set
expx: 只能取EX或者PX,代表数据过期时间的单位,EX代表秒,PX代表毫秒。
time: 过期时间,单位是expx所代表的单位。

该方法获取锁是使用lua脚本实现,具有原子性。 问题在于释放锁的时候,比如,在finally中释放锁,当校验到自己上锁后,可能释放锁的操作失败了,因为校验和释放非原子操作。(校验是否自己上的锁是必须的,因为,可能自己的锁达到过期时间自动释放了,这个时候校验的锁:是别的线程加的锁)

3、redisson (推荐)

基本使用

使用其实就是先获取锁(代码中创建锁),再加锁和解锁的过程(这里操作redis)。 将一段逻辑命令发使用lua脚本发送给redis,保证原子性。来看一下实现:

加锁机制

以上的逻辑就是: 

a、如果没加过锁,则加锁,设置过期时间

b、如果已加过锁,则加重入锁(上锁次数+1),重新设置锁的过期时间

c、其他client已上锁,返回锁的时间

其中参数的意思是:

KEYS[1]) : 加锁的key

ARGV[1] key的过期时间,默认为30

ARGV[2] : 加锁的value,需要包含客户端ID (UUID.randomUUID() + “:” + threadId)

 
此外,redisson还实现了过期时间的续约机制,一旦client端加锁成功就会启动一个后台线程watch dog,每10s检查,如果client端还持有锁,则延长key的生存时间。
 
解锁机制
 
流程如下:

a、如果锁已释放,发布解锁消息

b、如果不是自己上的锁,不能解

c、如果是自己上的锁,看是否是多次,将次数-1。 否则,删除锁,发送解锁消息

 
4、分布式锁 redis 和 zookeeper对比
 
redis的实现方式和zk不同,zk是通过创建临时顺序节点,并获取所有节点来判断:是否当前自己是最小节点,如果是,代表获取到锁。  网上很多人在对比,说redis需要自己不断的尝试来获取,性能差,而zk只需要注册监听;  redis集群在master宕机后,会造成多client上锁成功。  但这种东西吧,并不绝对,还是要根据实际情况来选择。 比如,zk的优点是分布式调度,如果程序中上锁的地方非常多,在高并发情况下,就会不断的创建和删除节点,并且该操作只能leader执行,然后同步给其他follower。  加上各种监听再获取所有节点遍历是否最小,性能也好不了多少。   反而这种情况下,对redis存取数据的特性来说,会好很多。。。另外,假如项目架构中有redis,没有需要使用zk的地方,也没必要为了使用分布式锁专门去维护zk。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值