Redis缓存和redis分布式锁

一)redis缓存:

redis最主要的用途,主要是三个方面:存储数据+缓存+消息队列

如何知道redis应该存储哪些数据呢,如何知道哪些数据是热点数据呢?

这就涉及到缓存的更新策略了

1)定期生成:会把访问到的数据,以日志的形式进行记录下来,所谓的访问的数据,拿搜索引擎来说,所谓的查询词就是要关注的访问的数据,查询词就是搜索框中输入的词,正常来说要通过日志来都使用到了那些的词,进行记录下来,此时就可以针对于这些日志进行统计了,统计这一天,每一个词出现的频率,再来根据访问词的频率进行降序排序,再取出前20%的词,此时就可以把这些词称之为是热点词,也可以按照周,年,月来进行统计

但是这里的日志的数据量是非常大的,可以写个程序来进行统计,这个数据量可能说一台机器存不下,此时就需要使用分布式的系统来存储这些日志

接下来就可以把这些热点词,以及涉及到的搜索结果提前拎出来,就可以存放到像redis这样的缓存中了,虽然说之前没有真的使用redis作为缓存,此处的数据,就可以根据统计维度,来实现定期更新,按照天级别统计一次,就每天更新一次,按照月级别统计一次,就每月更新一次,本身可以通过定时任务来触发

1.1)完成统计热词的过程

1.2)根据热词,找到搜索结果的数据,也就是广告数据

1.3)把得到的缓存数据同步到缓存服务器上面

1.4)控制这些缓存服务器自动重启

优点:上述过程,实际上实现起来是比较简单的,过程也可控,缓存中有啥是比较固定的,方便排查问题

缺点:实时性不够,如果出现一些突发性事件,把本来一些不是热词的内容,变成热词了,新的热词就有可能给后面的数据库服务器造成一定的压力

2)实时生成:如果在redis中查询到了,直接返回,如果redis中不存在,那么就从数据库中查询,同时把查询到的结果也写入到redis里面,这样经过一段时间的动态平衡,redis中的key就逐渐都成为了热点数据了,这样不停的写redis就会使redis的内存占用越来越多,逐渐达到了内存上限,但是不一定是机器内存,redis中也可以配置,最多使用多少内存,redis内存使用完了,如果再次向里面插入数据,就会触发内存淘汰问题,本身在redis中也进行设置了,本身有一个配置项,就可以设置redis可以采用哪一种内存淘汰策略

FIFO:先进先出,把缓存中存在时间最长的,也就是说最先来的数据给淘汰掉

First in First Out

LRU:淘汰最久未使用的,记录每一个key的最近访问时间把最近访问时间最老的key给干掉

LFU:积累每一个key最近一段时间的访问次数,把访问次数最少的给淘汰掉

Random:随即淘汰

1)热点key永不过期是需要分析出key访问的次数,达到一定次数从而进行设置永不过期,限制访问数据库的访问频率,但是用户访问时间变长,降级;

一)缓存预热:

1)缓存中的数据是定期生成的,所以这是不会涉及到预热的 

2)实时生成:redis服务器首次接入以后,redis服务器是没有数据的,此时所有的数据都会打给mysql,随着时间的推移,redis上面的数据越积累越多,MYSQL承担的压力也越来越少了,客户端先会去查询redis,如果没有查询到,那么就会查询mysql,查询到了以后,就会把数据写入到redis里面,本身缓存预热就是用来解决上述问题的,把定期生成和实时生成结合一下,先通过离线的方式,通过一些统计的途径,先把热点数据找到一批,导入到redis里面,此时导入的这些热点数据就可以帮助MYSQL承担很大压力了,但是随着时间的推移,要主键是新的热点数据淘汰掉旧的热点数据

二)缓存穿透:查询的某一个key在redis中没有,在MYSQL中也没有,那么这个key肯定也不会更新到redis中,这一次查询,没有,下一次查询,仍然没有,像这样的数据存在很多,并且还反复查询,一样会给MYSQL带来巨大的压力

缓存穿透主要是三个原因:

2.1)业务设计不合理,比如说缺少必要的参数校验环节,导致非法的key也被进行查询了

2.2)开发/运维误操作,不小心把部分数据从数据库上面误删除了,这种场景没有那么经典,表现也为缓存穿透,误删操作,不一定可以及时的发现

解决方案:

2.1)通过改进业务,加强监控报警

2.2)如果发现这个key,在redis上和MYSQL上面都不存在,仍然是可以写入redis中,value设置成一个非法的空值

2.3)还可以引入布隆过滤器,每一次查询redis/mysql之前,都先进行判定一下key是否在布隆过滤器上面存在,可以把所有的key都插入到布隆过滤器中,布隆过滤器本身是结合了hash和bitmap的思想,以比较小的时间开销,比较快的时间速度,针对于key是否存在的判定

三)缓存雪崩:这个是由于在短时间内,redis上面大规模的key失效,导致缓存命中率突然下降,并且MYSQL的压力迅速上升,乃至于说直接宕机

3.1)redis直接挂了,redis宕机或者是redis集群模式下大量节点直接当宕机

3.2)redis好着呢,但是之前可能是说短时间内设置了很多key给redis,并且设置的过期时间是相同的,在给redis设置缓存的时候,有的时候为了考虑缓存的时效性,就会设置过期时间

可以加强监控报警,加强redis集群可用性的保证,或者是不给key设置过期时间,或者是在设置过期时间的时候添加随机的因子

四)缓存击穿:相当于是缓存雪崩的特殊情况,针对于热点key,突然过期了,导致大量的请求直接访问到数据库上面,甚至于说是引起数据库宕机,因为本身热点key访问频率高,影响很大的,可以基于统计的方式发现热点key,并且设置永不过期,或者是有必要进行服务降级,比如说使用数据库的时候使用分布式锁,同时限制请求时的并发数,比如说通过使用分布式锁,限制数据库的访问频率,本身服务器的功能有10个,但是在特殊情况下,适当的关闭一些不重要的功能,只是保留核心功能

二)分布式锁:

因为之前学习过的锁都是只能在同一个进程内部生效,但是在分布式系统中,是存在着很多进程的,每一个服务器都是单独的进程,因此之前的锁对于分布式系统中是无效的

所谓的加锁就是给redis上面设置一个key-value,所谓的解锁就是在redis上面把这个key-value删除掉

1)在服务器访问数据库之前,先去查询数据,再进行修改,当然在执行上述操作之前,先进行尝试在服务器上面设置键值对,如果这个键值对不存在,那么设置成功就是加锁操作,如果这个键值对存在设置失败,说明这个键值对之前已经存在了,那么是加锁失败,如果加锁成功,才可以执行操作,执行完成操作以后,才可以释放锁,也就是删除这个键值对,key随便设置,value设置成啥都行;

2)不存在就设置成功,存在就设置失败

客户端挂了,之前这个客户端设置的key仍然存在,所以会给其他客户端加锁带来影响

比如说在一个买票服务器中,在进行买票的过程中,就需要先加锁,可以在redis上面设置一个特殊的key-value,当完成上述买票操作,再把这个key-value直接删除掉,其他服务器要是也想要买票的话,就也去redis上面尝试去设置key-value键值对,如果发现了key-value键值对已经存在了,那么就加锁失败,是放弃还是阻塞,就看具体的实现策略了,这样就可以保证在第一个服务器执行查询-更新的过程中,第二个服务器不会执行查询,也就解决了上述的超卖问题

1)setnx:不存在就设置,存在就出错,本身使用setnx确实可以得到加锁的效果,针对于解锁,确实可以使用del命令来完成,某一个服务器加锁成功了,执行后续某一个逻辑过程中,程序崩溃了,没有执行到解锁,之前说过,为了可以保证解锁操作可以执行到,就可以把解锁放到finally里面,但是这种做法,只是针对于进程内部的锁有用,针对于分布式锁是无效的,比如说服务器直接掉电,进程异常终止,这样的情况就会导致redis上面设置的key无法被删除,这样也就导致其他服务器上面无法获取到锁了

2)可以通过set nx ex这样的命令来进行设置,比如说设置了key的过期时间,是1000ms,那么意味着即使出现了极端情况,某一个服务器挂了,没有正确的释放锁,这个锁最多也就是保存1000ms,也就会自动释放锁了

3)setnx,expire注意这种方式设置是不行的,请务必要使用set nx ex这样的方式进行设置,redis上面的多个命令之间,是无法保证原子性的,此时就很有可能出现一个成功,一个失败的情况

4)但是是否可能会出现服务器1执行了加锁,但是服务器2执行了解锁?这是有可能的,比如说现在服务器1进行加锁以后,由于本身的业务执行时间过长,服务器1加的锁超时释放了,但是假设此时在进行超时释放之后,服务器1执行业务未完成之前,服务器2直接加了一把锁,那么当服务器2进行判断发现可以加锁成功,此时就可以加锁了,这时服务器1执行业务完成,直接把服务器2加的锁删除了,此时还会出现线程1和线程2同时执行一段业务逻辑,很有可能造成超卖,就是服务器1进行了加锁,然后服务器2进行了解锁

为了解决上述问题,就需要引入一些校验机制:

1)给服务器编号,每一个服务器都有着自己的唯一一个身份标识

2)进行加锁的时候,设置key-value,key要对应着哪一个资源进行加锁,比如说一些车次,value就可以根据刚才服务器的编号标识出当前这个锁是给哪个服务器加上的,后续再进行解锁的时候,就可以进行校验了,解锁的时候,先进行判断一下这个锁对应的服务器编号,然后进行判定一下这个编号是否就是当前执行解锁的服务器编号,如果是,才可以真正执行del,如果不是就失败,只有说服务器完成这两步的校验,就可以有效地避免误解锁

引入lua脚本:

1)在进行解锁的时候,可能会出现一些问题,先进行查询判定,然后再进行del,一个服务器内部,也可能是多线程程序,此时就可能同一个服务器两个线程都在执行上述解锁操作,服务器1,线程A,线程B解锁操作重复执行了两次

2)从上述情况来看,看起来好像是说重复执行del好像问题不大,但是其实来说不然,主要是引入一个新的服务器,执行加锁,就有可能出现问题了,在线程A执行完成DEL之后,B执行DEL之前,服务器2的线程C刚好要执行加锁set,因为此时的服务器A已经把锁释放掉了,C的加锁是可以执行成功的,但是紧接着,线程Bdel就到来了,就把刚才服务器2的枷锁操作给解锁了,本身服务器1和服务器2进行加锁,key是资源的编号,比如说车次,但是服务器的id是value,确实可以使用事务来解决这个问题,因为redis可以避免插队;

3)但是实践中往往使用更好的方案,就是lua脚本,始于redis内嵌的脚本,lua脚本内可以编写一部分逻辑,然后把这个脚本上传到redis服务器上面,然后就可以通过控制客户端来运行脚本了,redis执行lua脚本的过程中,也是原子的,就相当于是只是执行一条命令,实际上那lua脚本可以写多个命令,lua也是属于一个事务的代替方案

引入看门狗:引入过期时间的续约问题 

本来是要在加锁的时候,要给key设置过期时间,但是这个过期时间要设置成多少合适呢?

1)如果设置的短,就有可能导致业务逻辑执行不完,就把锁释放了

2)如果本身设置的时间太长,也会导致锁释放不及时的问题

3.1)更好的方式,是进行动态续约,往往需要有一个专门的服务器进程来做这个事情,把这个负责的线程叫做看门狗,这本身也是一个非常广义的概念很多场景中也会涉及到这个针对于过期时间的操作,很多时候就会引入看门狗

3.2)在初始情况下设置一个过期时间,比如说设置1s,就提前还剩下300ms的时候,也不一定是300ms,数值本身都灵活调整,如果当前任务还没有执行完成,就把过期时间在进行续上1s,等到时间又快到了,任务还没有执行完,就在进行续杯,如果说本身服务器中途崩溃了,自然也就是说没有人再进行自动续约了,此时锁就能在本身较短的时间内被自动释放

1)使用redis服务器作为分布式锁,redis本身有没有可能直接挂了呢?太有可能了,要像保证高可用就需要通过一些列的预案演习,主从复制,哨兵集群,分片集群(更多的是解决存储空间不足的问题),但是在分布式锁场景中,涉及到的数据量不大,涉及到的加锁的服务器不算太多;

2)set nx ex写入到主节点,进行加锁,就是将set nx ex,如果主节点挂了,就会有哨兵节点自动地把从节点升级成主节点,进一步的保证刚才的锁可用,但是主节点和从节点的数据同步是存在延时的,主节点收到了set请求,执行成功了,但是还没有来得及同步给从节点呢,主节点就先挂了,即时从节点升级成了主节点了,但是对于刚才加锁的数据,是不存在的,作为一个分布式系统中,就需要随时考虑某一个节点挂了的情况,需要保证某一个节点挂了不会影响到大局

3)redlock算法:引入冗余节点来提升redis作为分布式锁的一个可用性问题

1)此时加锁,就是按照一定的顺序,针对于这些组redis都进行加锁操作,如果某一个节点挂了,某一个节点加不上锁,没关系,可能是redis挂了,可以给继续给下一个节点加锁即可,如果真正的key节点个数超过了总数的一半,那么此时就是为加锁成功

2)同时解锁的时候,也会给上述节点都设置一遍解锁

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis可以用来作为分布式缓存分布式锁和消息队列的原因有以下几点。 首先,Redis具有高性能的内存数据库特性,在缓存场景下,能够实现快速的读写操作,提高数据的访问速度。通过将常用的数据存储在Redis中,可以避免频繁地查询数据库,减少响应时间,提高系统的吞吐量。 其次,Redis支持数据过期机制和LRU(Least Recently Used,最近最少使用)算法等缓存策略,可以根据需求定时清理过期数据或者根据缓存数据的使用频率进行淘汰,保证缓存的有效性和一定的容量。 此外,Redis还提供了常见的数据结构,例如字符串、列表、哈希、集合和有序集合等,这些数据结构的操作都是原子性的,可以支持多个客户端同时访问和修改数据,利于实现分布式锁的功能。通过Redis的SETNX(SET if Not eXists)指令可以实现简单的互斥锁机制,通过设定过期时间和唯一标识可以防止死锁的发生。 此外,通过Redis的订阅与发布机制,可以轻松地实现消息队列的功能。发布者可以发布消息,订阅者可以实时地接收到消息并进行相应处理。同时,Redis还支持发布与订阅模式的消息持久化,即使在消息发布者和订阅者之间存在断开连接的情况下,消息也不会丢失。 综上所述,Redis的高性能、灵活的缓存策略、原子性的数据操作和消息持久化机制,使其成为一个强大的工具,可以用来实现分布式缓存分布式锁和消息队列的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值