
地 址:北京市怀柔区66号
电 话:13323327978
网址:allin0.com
邮 箱:29240282@qq.com
一,网络Redis作缓存服务(wu)器

redis作为(wei)缓存服务器是缓存s缓众(zhong)多(duo)企业(ye)中的选择之(zhi)一,虽然(ran)该技术很成熟但也是服务存在一定的问题。就是软件缓存带来的缓存(cun)穿透,缓存击穿,存持缓存失效问题,久化继而(er)引用分布式锁。网络

1.1,缓存s缓缓存穿透

在如今的服务项目中大多采用垂直的MVC架构,由service层去(qu)调(diao)用DAO层,软件然后DAO层再去查询数据库。存持而redis作(zuo)为缓存服务器(qi)就是久化在service层去调用DAO层去查询时(shi)先去缓存服务器查询,如果存在则直接返回该数据,网络否则再去查询数据库。缓存s缓由此可知,服务这么做大量减少了对磁盘I/O的操作,减轻了(le)数据库的压力。
现在我们假设一种情况,在数据库中存在(zai)有id为1到1000的数据。现在如果(guo)有人手动去模拟一个id为(wei)1001的请求,那么该数据在缓存服务器中是不存在的,因而便会去查询数据库。那么问(wen)题来了,如果是一个大量无效的请求去查询数据库。则势必会对数据库造成难以承受的压力,这种情况就(jiu)是所谓的缓存穿透。
1,将查询到的null值直接保存到缓存服务器中(zhong),但是这种做法并不推荐,因为(wei)如果是大量不同的请求id同样会去查询数据库。
2,接口的限流,降级与熔断
在项目中对(dui)于重要的接(jie)口一定要做(zuo)限流,对于以(yi)上恶意攻击的请求除了要限流,还要做好降级准备,并且进行熔断,这种做法可(ke)以有效控制大量无效请求。
3,布隆过滤器
Bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其(qi)典型(xing)的应用场景就是快速判断一个(ge)key是否存在于某容器(qi),不存在就直(zhi)接返回。布隆过滤器的关键就在于hash算法和容器大小,该做(zuo)法是多数企业所选择的。
1.2,缓存击穿
在高并发下,对某些 热点的(de)值进行查询,但是这个时候缓存正好过期了,缓存没有命中,导致大(da)量请求直接落到数(shu)据库上,此时这种大量的请求可能会是数据库崩盘。
解决方案:
1,将热点key设置成永不过期。
2,使用互斥锁。
以上(shang)两种情况均是属于缓存失效,但里面还有小小的细节。那就是存在多(duo)个(ge)缓存同时失效的问题,尤其在高并(bing)发时间段。为避免这(zhe)种(zhong)多个缓存失效的问题,我们在设置超时时间的时候(hou),可(ke)以使用固定时间+随机时间。以最大限度避免当缓存失效时大量(liang)请求去查询数据库。
1.3,分布(bu)式锁
通常情况下分布(bu)式锁有三种实现方式,1. 数据库乐观锁;2. 基于ZooKeeper的分(fen)布式锁;3. 基于Redis的分布式锁;这里只记录基(ji)于redis的分(fen)布(bu)式锁。
作为分布式锁的要(yao)求:
先参看如下代码:
public List<Goods> goodsManager() { System.out.println("调用过了业务层的goodsManager方法"); return goodsDao.queryAllPage(); // 1,先去查询缓存服务器 List<Goods> goodsList = (List<Goods>) redisTemplate.opsForValue().get("goods"); if(goodsList == null){ // 2,申请分布式锁 RedisConnection conn = redisTemplate.getConnectionFactory().getConnection(); if(conn.setNX("lock".getBytes(), "1".getBytes())){ // 3,给分布式锁设置一个超时(shi)时间 conn.expire("lock".getBytes(), 60); System.out.println("去数据库中查询所有(you)的商品"); // 4,缓存中没有商品列(lie)表的数据 goodsList = goodsDao.queryAllPage(); // 5,将结果(guo)放入缓存中 redisTemplate.opsForValue().set("goods", goodsList); redisTemplate.expire("goods", 5, TimeUnit.MINUTES); // 6,释放分布式锁 conn.del("lock".getBytes()); } else { try { Thread.sleep(50); goodsManager(); } catch (InterruptedException e) { e.printStackTrace(); } } return goodsList; } else { //缓存服务器中有商品列表的数据 return goodsList; } } 代码设计思路:
1,请求到来调(diao)用方法。
2,先去redis缓存中查询是否存(cun)在,如果没有则查询数据库。
3,使用原生的连接(setNX)获得分布式锁,然后设置超时时间。
设置超时时间的原因在于,如果线(xian)程获得锁之后不下心崩溃,为(wei)防止发生死锁因而设置超时时间。
4,查询数据(ju)库获得数据,并保存在数据库中。
5,释放锁。
1.4,雪崩效应
解决方案:
1、可以使用分布式锁 ,单架构项目使用syn
2、永不过(guo)期
4、高可用,集群
1.5,redis缓存与springboot整合
在启动函数中先要开启缓存注解@Enablecaching
@Cacheable
被该注解(jie)标注的方法,会(hui)在执行前(qian)查询缓存服务器,如(ru)果缓存服务器有结果,则(ze)直接返回结果,当前方法就不(bu)会执行。如(ru)果(guo)没有结果,则在执行该方法的方法体,并(bing)且将该方法返回值放入缓存(cun)服务器中。
@CachePut
该注解和@Cacheable注解的功能(neng)差不多,唯一的区别在于不(bu)管缓存服务器有(you)没有对应(ying)的值,都会去(qu)调用相应的方法用于(yu)添加和更新的方法。
@CacheEvict
删除指(zhi)定的缓存,一般用于删除方法的使用 。
二(er),Redis持久化
### 2.1,redis提供两种持久化方式:
2.2,RDB原理分析
RDB持久化有两种操作方(fang)式(shi),手动操作进行持久化。
bgsave和save最大的(de)区别在于bgsave不会阻塞客户端的写操作,但是如果bgsave执行(xing)失败,Redis默认将停止接受接(jie)入操作,否则就没人会注意到灾难的发生,如果不希望这(zhe)样做,可以将
stop-writes-on-bgsave-error yes设置为no
另一种为自动触发持久化,首先我们可以在配置文件中配置快照的规则。
save 300 10 当300秒以内执行10个(ge)写命令,使用快照备份
save 60 10000 当60秒以内执行10000个写命令,使用(yong)快照备份
注意:redis执行备份命令时,将禁止写入命令
2.3,AOF原理分析
AOF的整个流程大(da)体来看可以分为两步,一步是命令的实时写入,第二步是(shi)对aof文件的重写,重写是为了减少aof文件的大小。
2.4,Redis内存回收策略
在redis.conf中的配置项maxmemory-policy用于配置redis的内存回收策略,当内存达到最大值时所采取的(de)内存处理方(fang)式。
redis提供六种(zhong)内存(cun)淘汰策略
volatile-lru: allkeys-lru: volatile-random: allkeys-random: volatile-ttl: noeviction(默认): 三,Redis高可用
在(zai)用(yong)户量非常庞大的时候,单台redis肯定(ding)是完全不够用(yong)的。因此更多的时(shi)候我(wo)们更(geng)希望可以读/写分离,读/写分离的前提就是读操作(zuo)比写操作频繁的多,将数据放在多台服务器上那么(me)久可以消除单台服务器的(de)压力。
因此(ci)对于服务器的搭建如图:
假设一台服务器负责写操作,其余三台为读操作,以此实现一个独写分离的缓存功能。但(dan)是很明显存在一种弊端,就是其余三台读取(qu)数据(ju)的服务器它们之间的数据是不能够进(jin)行(xing)同步的。这样便造成数据不一致的情况,此(ci)时就需要对它们之间进(jin)行一个数据上的(de)互通。
简单介绍一下主从复制的概念,如上图 Master为主,负责写入数据的(de)操作,其余 三台为从(Slave),负责读取数据操作。当有数据写入时,根据配置好的属性自动将更新的(de)数据(ju)复制到其余三台服务器(qi)中,这(zhe)样便实现了(le)服务器之间的数(shu)据一致性(xing)。
主从复制的大(da)致流程:
1、保证主服务器(qi)(Master)的启动。
2、当从服务器启动时,发送SYNC命令给主服务器(qi)。主服务器接受(shou)到同步命令时,就是执行bgsave命令备份数据(ju),但是主服务器并不会拒绝客户端的写操作,而是将来自客户端的写命令写入缓冲区(qu)。从服务器在(zai)未收到主服务器的备份快照文件之前,会根据配置决定(ding)使用现有数据响应(ying)客户端还(hai)是返回错误。
4、当主服务(wu)器发(fa)送完(wan)备份文件后,会将bgsave执行之后的缓存区内的写命令也发送给从服务器,从服务器完成备份文件解析后,就开始等待主服务器的后续命令。
5、同步完成以后,每次主服务器完成一次写入命令,都(dou)会(hui)同时往从服务器发送同步写入命令,主从同步就完成了。
从机配置:
slaveof server port 设置Master的ip和端口
masterauth root 设置Master的密码
到此为止,就完成了吗?
并不是,以上步骤只是完成(cheng)了主从复制,并没有(you)完成读写分离(li)。并且,如果主(Master)服务(wu)器宕机,那整(zheng)个缓存服务器就全部挂掉了。==而(er)且作为从(Slave)服务器(qi)时不可以进(jin)行写的操作,==那(na)又如何解决(jue)呢(哨兵模式)?
3.2,哨兵模式
1,什么时哨兵模式?
当Master宕机以后需要手(shou)动的把一台Slave切换为Master,这种(zhong)方式(shi)需要人工干预,费时费力。因此哨兵模式可(ke)以帮助我们解决这个问题。
3,哨兵模式配置
3.1,#配置哨兵配置文件:
redis/src/sentinel.conf
3.2,#禁止保护模式
protected-mode no
3.3,#配置监听的主服务, 最后的2表示当(dang)2个或2个以上的哨兵认为主服务不可用才会进行故障切换
3.4,#定义服务密码
sentinel auth-pass 服务器名称(和上面相同) 密码
3.5,#启动哨兵模式;
./redis-sentinel sentinel.conf
4,其他相关配置
sentinel down-after-milliseconds : 指定哨兵在(zai)检测redis服务(wu)时,当redis服务在一个毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30秒。
sentinel failover-timeout: 指定故障切换运行的毫秒数,当(dang)超过这个毫秒数时,就认为切换故障失败,默认3分钟。
sentinel notification-script: 指定(ding)哨兵检测到redis实例异常时,调用的报警脚本。
3.3,分(fen)片集(ji)群
分片集群原理(li)在于多个缓存服务器之间两(liang)两相互通信,每个复制集具有一个主实例和多个从实例。并且每个复制集朱保存一部分数据库中的键(jian)值对,解决了主从复制集中总数(shu)据存储量最(zui)小实例的限制,大(da)大扩大了缓存服务器的大小。
其结构图(tu)如下:
1,分片集群特点
1、Client与redis节点直接连接,不需(xu)要中间(jian)proxy层。
3、所(suo)有的redis节点彼此互联(PING-PONG机制),内部使用gossip二(er)进制协议优化传输数据(ju)。
4、 节点的失效检测是通过集群中超过半数的节点检测失效时才生效。
==问(wen)题:Redis 集群中内(nei)置了 16384 个(ge)哈希槽,那他是如何决定将(jiang)key放入哪个插槽的?==
当Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算(suan)出一个结果,然后把结果对 16384 求余数,这样每个(ge) key 都会对应一个编号在 0-16383 之间(jian)的哈希槽,redis 会根据节点数量大致均等的(de)将哈希槽映射到不同的节点。
2,集群搭建步骤
前置条件:
删除redis/src下的appendonly.aof,dump.rdb,nodes-6379.conf3个文件。
1、修改redis.conf,配置集群信息开启集群,
cluster-enabled yes 指定集群的配置文件,
cluster-config-file nodes- 端口.conf
2、用redis-trib.rb搭建集群因为redis-trib.rb是用Ruby实现(xian)的Redis集群管理工具,所(suo)以我们需要先安装ruby的环境.
2.1、安装ruby
yum -y install zlib ruby rubygems
2.2、安装rubygems的redis依赖
3、安装好依赖的(de)环境之后,我们就可(ke)以来使用脚本命令了
注意:此脚本文件在我们的解(jie)压缩目录src下(xia)。
./redis-trib.rb create --replicas 0 192.168.10.167:6379 192.168.10.167:6380 192.168.10.167:6381 开放16379 redis端口+1W--replicas 0:指定了从数据的数量(liang)为0。
4、查看集群状态
通过客户端输入以下命令:
cluster nodes:这个命令可以查看插槽的分配情(qing)况
整个(ge)Redis提供了16384个插槽,./redis-trib.rb 脚本实现了是将16384个插槽平均分配给了N个节点。
end:如果你觉得本文对你有帮助的话,记得关注点赞转发,你的支持就是我(wo)更新动(dong)力(li)。
版权声明:本文(wen)内容由互联网用户自发贡献,该文观点仅代(dai)表(biao)作者本人。本站仅提供信息存储空间服务,不拥有所(suo)有权,不(bu)承担相关(guan)法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容(rong), 请发送邮件至 1817475@qq.com 举报,一经查实,本站将立刻(ke)删除。