redis相关技术点

1.关于jvm的synchronized关键字锁,只是针对当前运行的这台虚拟机有效果,当使用分布式架构nginx来部署系统时,项目部署在两个及以上的服务器上,使用nginx来管理对外的链接请求,此时再使用synchronized来控制原子性就会有问题;对于一个需要操作的对象,我们可以创建redis的分布式锁来处理。Redisson加锁代码,分布式锁操作redis,由于redis是单线程的,所以当有线程锁住某个对象的锁时,其它操作此对象的线程只能自旋等待,此处可以使用分段锁进行优化锁(拆分段、合并操作库存)。

String lockKey = "product_001";
RLock redissonKey = redisson.getLock(lockKey);
try {
   redissonKey.lock();  //加锁
   //处理业务代码,例处理库存
} finally {
    redissonKey.unlock();  //释放锁
}

2.缓存与数据库双写不一致问题:线程一往数据库中写入一个变量值,然后出现卡顿,线程二也往此变量中写入另外一个值,然后再把此值写入到缓存中,之后线程一才把它的值覆盖写入到缓存中,这样此时数据库中存的是线程二赋的值,而缓存中存的是线程一赋的值,导致数据库与缓存不一致

解决方案:可以使用分布式锁,是多线程的处理逻辑串行化,这样可以解决问题,但是性能偏低

3.redis底层存储基于hash表,使用数组加链表进行存储;对key进行取模运算,放到对应的位置。

4.redis的value类型:字符串string、哈希hash、列表list、集合set、有序集合zset;string运用:单个缓存、对象缓存(json来回转)、分布式锁;hash运用:对象缓存;对象缓存中:对象的单个属性修改比较多推荐使用hash缓存,整个对象一块修改的情况多推荐使用string缓存。list运用:stack(栈):LPUSH+LPOP;queue(队列):LPSHT+RPOP;阻塞队列:LPUSH+BRPOP(有元素直接获取,没有元素等待有元素了获取),可以用好list方式存放消息动态数据,每 产生一条记录,往list 中插入一条记录,这样从list的左边开始,数据是一个降序的排序方式,LRANGE ID 0,4:查看最新的5条数据。

set运用场景:sadd key {value}(添加一个数据到集合)、smembers key(查看某个set下的元素集合)、srandmembers key 【count】(从set集合中随机抽取几个数据)、spop key 【count】(从set集合中随机取出几个数据,并且删除它)

集合操作

set1{a,b,c}  set2{b,c,d} set3{c,d,e}

sinter set1 set2 set3 {c]:求交集

SUNION set1 set2 set3 {a,b,c,d,e}:求并集

SDIFF set1 set2 set3 {a}:求差集,第一个集合减去后面集合的并集,然后以第一个集合为准,找出第一个集合没有出现在在后面集合中的元素

Zset运用场景:展示当日排名前十:ZREVRANGE hostNew:20200120 0 9 withscores

5.微博微信的关注模型实现方式

(1)查询共同关注的人:Sinter set1 set2

(2)我可能认识的人:sdiff set1 set2

6.微博微信点赞收藏功能

(1)点赞:sadd like:消息id {用户id}

(2)取消点赞:srem like:消息id {用户id}

(3)检查用户是否点过赞:sismembers like:消息id {用户id}

(4)获取点赞的用户列表:smembers key

7.redis存储结构

(1)简单动态字符串

(2)哈希表

(3)压缩列表(ziplist)

(4)双向链表

(5)整数数组

(6)跳表(skiplist有序集合Zset可以使用跳表和压缩列表):有序链表每两个数据提取一个数字作为索引层,将有序链表改造成支持“折半查找”算法,可以进行快速的插入、删除、查询操作

8.简单命令

flushdb:清空表

type xx:xx对象的存储类型(5种)

object encoding xx:xx的底层存储结构(6种)

9.zset的存储结构有压缩链表(ziplist)变为跳表(skiplist)的条件,系统redis.conf中默认配置

zset-max-ziplist-entries 128
zset-max-ziplist-value 64

元素个数超过128个,由压缩链表变为跳表方式;单个元素大小超过64byte,使用跳表存储

10.数据持久化的操作RDB、AOP机制

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

(2)AOF:以日志的形式详细记录每一个redis的写、删除操作,查询操作不会记录,以文本的方式记录

11.redis过期键删除策略

(1)惰性过期:只有当访问一个key时,才判断此key是否过期,过期则删除。可以最大化的节省cpu资源,对内存不友好

(2)定期过期:每个一段时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除已经过期的key

12.redis单线程快的原因:

(1)纯内存操作

(2)核心是基于非阻塞的IO多路复用机制

(3)单线程避免了多线程的频繁上下文切换带来的性能问题

13.缓存雪崩、缓存穿透、缓存击穿

(1)缓存雪崩:缓存同一时间大面积的失效,后面的请求都会落在数据库上,造成数据库短时间内承受大量请求而崩掉

解决方案:

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

②给每个缓存数据增加缓存标识,记录缓存是否失效,如果缓存标记失效,则更新数据缓存

③缓存预热

④互斥锁

(2)缓存穿透:缓存和数据库中都没有的数据,导致所有的请求都落在数据库上,造成数据库短时间内承受大量请求而崩掉

解决方案:

①接口层增加校验,如用户鉴权校验,id做基础校验,id《=0的直接拦截

②从缓存中取不到的数,从数据库中也没有取到,这时也可以将key_value对写成key-null,缓存有效时间可以设置端点,设置太长会导致正常情况也没法使用

③采用布隆过滤器

(3)缓存击穿:缓存中没有,数据库中有,一般发生在缓存时间到期,这时由于并发用户特别多,同时读缓存没有读到数据,又同时去数据库中查询,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不一样的是,缓存击穿指并发查询同一条记录,缓存雪崩是不同数据都过期了,很多数据都查不到从而从数据库查

解决方案:

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

②加互斥锁

14.redis事务实现

(1)事务开始:MULTI命令的执行,标识着一个事务的开始,MULTI命令会将客户端状态的flags属性中打开REDIS_MULTI标识来完成

(2)命令入队:如果客户端发送的命令为multi、exec、watch、discard中的任何一个,服务器立即执行这个命令,若是其它命令,检查命令的正确性,命令不对的话直接返回错误信息给客户端,命令正确,放入事务队列中

(3)执行事务 

redis不支持事务回滚机制,但是会检查事务中的命令是否正确

15.redis集群方式

(1)主从(哨兵模式):sentinel哨兵主要功能:

集群监控:监控redis master和slave进程是否正常工作

消息通知:若果某个redis实例有故障,哨兵负责发送消息作为报警通知给管理员

故障转移:如果master node挂掉了,会自动转移到slave node节点上

配置中心:如果故障转移发生了,通知client客户端新的master地址

故障转移时,判断一个master node是否宕机了,需要大部门的哨兵都同意才行,涉及到分布式选举

即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的

哨兵通常需要3个实例来保证自己的健壮性

(2)redis cluster:是服务端sharding技术,3.0版本开始正式提供,采用slot(槽),一共分为16384个槽

(3)redis sharding:是客户端分片,由客户端进行hash计算,把key放到哪个节点上

16.redis主从复制原理

(1)全量复制:主节点通过bgsave命令fork一个子进程进行RDB持久化,改过程是非常消耗cpu,内存、硬盘io;

主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会发生很大的消耗;

从节点清空老数据,载入新的rdb文件是阻塞的,无法响应客户端的命令

(2)部分复制:复制偏移量:执行复制的双方,主从节点,分别会维护一个复制偏移量offset;

复制积压缓冲区:主节点内部维护了一个固定长度、先进先出对列作为复制积压缓冲区;当主从节点的赋值offset过大超过缓冲区长度时,进行全量复制;

服务器运行id:每个redis都有其运行的id,运行id有节点在启动时自动生成,主节点会将自己的运行id发送给从节点,从节点会将主几点的运行id存起来。从节点断开重连的时候,就是根据运行id来判断同步的进度

17.Redisson实现分布式锁的原理

(1)加锁机制

现在某个客户端要加,如果该客户端面对的是一个redis cluster集群,他首先会根据hash节点选择一台机器。

这里注意,仅仅只是选择一台机器!这点很关键!

紧接着,就会发送一段lua脚本到redis上,那段lua脚本如下所示:

为啥要用lua脚本呢?

因为一大坨复杂的业务逻辑,可以通过封装在lua脚本中发送给redis,保证这段复杂业务逻辑执行的原子性

那么,这段lua脚本是什么意思呢?

KEYS[1]代表的是你加锁的那个key,比如说:

RLock lock = redisson.getLock("myLock");

这里你自己设置了加锁的那个锁key就是“myLock”。

ARGV[1]代表的就是锁key的默认生存时间,默认30秒。

ARGV[2]代表的是加锁的客户端的ID,类似于下面这样:

8743c9c0-0795-4907-87fd-6c719a6b4586:1

给大家解释一下,第一段if判断语句,就是用“exists myLock”命令判断一下,如果你要加锁的那个锁key不存在的话,你就进行加锁。

如何加锁呢?很简单,用下面的命令:

hset myLock 

    8743c9c0-0795-4907-87fd-6c719a6b4586:1 1

通过这个命令设置一个hash数据结构,这行命令执行后,会出现一个类似下面的数据结构:

上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”这个客户端对“myLock”这个锁key完成了加锁。

接着会执行“pexpire myLock 30000”命令,设置myLock这个锁key的生存时间是30秒。

好了,到此为止,ok,加锁完成了。

  (2)锁互斥机制

那么在这个时候,如果客户端2来尝试加锁,执行了同样的一段lua脚本,会咋样呢?

很简单,第一个if判断会执行“exists myLock”,发现myLock这个锁key已经存在了。

接着第二个if判断,判断一下,myLock锁key的hash数据结构中,是否包含客户端2的ID,但是明显不是的,因为那里包含的是客户端1的ID。

所以,客户端2会获取到pttl myLock返回的一个数字,这个数字代表了myLock这个锁key的剩余生存时间。比如还剩15000毫秒的生存时间。

此时客户端2会进入一个while循环,不停的尝试加锁。

(3)watch dog自动延期机制

客户端1加锁的锁key默认生存时间才30秒,如果超过了30秒,客户端1还想一直持有这把锁,怎么办呢?

简单!只要客户端1一旦加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端1还持有锁key,那么就会不断的延长锁key的生存时间。

(4)可重入加锁机制

那如果客户端1都已经持有了这把锁了,结果可重入的加锁会怎么样呢?

比如下面这种代码:

这时我们来分析一下上面那段lua脚本。

第一个if判断肯定不成立,“exists myLock”会显示锁key已经存在了。

第二个if判断会成立,因为myLock的hash数据结构中包含的那个ID,就是客户端1的那个ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此时就会执行可重入加锁的逻辑,他会用:

incrby myLock 

 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1

通过这个命令,对客户端1的加锁次数,累加1。

此时myLock数据结构变为下面这样:

大家看到了吧,那个myLock的hash数据结构中的那个客户端ID,就对应着加锁的次数

(5)释放锁机制

如果执行lock.unlock(),就可以释放分布式锁,此时的业务逻辑也是非常简单的。

其实说白了,就是每次都对myLock数据结构中的那个加锁次数减1。

如果发现加锁次数是0了,说明这个客户端已经不再持有锁了,此时就会用:

“del myLock”命令,从redis里删除这个key。

然后呢,另外的客户端2就可以尝试完成加锁了。

这就是所谓的分布式锁的开源Redisson框架的实现机制。

一般我们在生产系统中,可以用Redisson框架提供的这个类库来基于redis进行分布式锁的加锁与释放锁。

(6)上述Redis分布式锁的缺点

其实上面那种方案最大的问题,就是如果你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。

但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。

接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。

此时就会导致多个客户端对一个分布式锁完成了加锁。

 这时系统在业务语义上一定会出现问题,导致各种脏数据的产生

所以这个就是redis cluster,或者是redis master-slave架构的主从异步复制导致的redis分布式锁的最大缺陷:在redis master实例宕机的时候,可能导致多个客户端同时完成加锁。

18.redis项目运用

(1)数据字典数据

(2)分布式系统加锁redisson

(3)rabbitmq避免重复消费时,abbitmq将消息交个其它消费者时,先执行setnx(如果key存在,什么事情都不做,如果key不存在,正常的set方法);若果key已经存在,获取到它的值,如果是0,当前消费者什么都不做,如果是1,其它消费者已经消费过此消息,直接ack

(4)存在幂等性问题的接口,使用redis生成token,当提交数据时,使用del key正常情况下返回结果为删除键的个数,假如删除了一个不存在的键,就返回0;判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行

(5)点赞、收藏数据

19.如何保证redis高可用

   (1)redis 主从架构

  单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。

redis 的高可用架构,叫做 failover 故障转移,也可以叫做主备切换。
  master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node的过程,叫做主备切换。这个过程,实现了 redis 的主从架构下的高可用。

(2)Redis 哨兵集群实现高可用

  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值