分布式缓存redis方案(分布式缓存原理架构go实现)
本篇文章给大家谈谈分布式缓存redis方案,以及分布式缓存原理架构go实现对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、Redis缓存之(一)分布式锁
- 2、面试中问到Redis持久化的原理,本篇在做详细解答
- 3、怎么实现redis的数据库的缓存(redis实现缓存的流程)
- 4、Redis分布式缓存搭建
- 5、缓存击穿、穿透、雪崩及Redis分布式锁
Redis缓存之(一)分布式锁
Redisson是架设在陪团Redis基础上的一个Java驻毁竖内存数芦余橘据网格(In-Memory Data Grid)
Ridisson文档
[img]面试中问到Redis持久化的原理,本篇在做详细解答
我们知道redis是一个 高效的分布式内存数据库 ,由于是操作内存所以性能非常之快,通常用它来做分布式缓存,用来提高微服务的高性能,但是因为是内存操作,所以当出现服务器故障,断电等情况就会造成 内存数据丢失 ,不可恢复,因此redis 引入了持久化机制来将内存数据写入圆带磁盘,从而保障了Redis的数据不被丢失。
Redis有两种持久化的方式,一种是RDB,另外种是AOF。
RDB是将Redis内存中数据的快照存储在磁盘内,是Redis的默认持久化方案。
RDB持久化默认有三种策略
可在redis.conf中配置,会以一段时间内达到指定修改的次数为规则来触发快照操作,快照文件名为dump.rdb。每当Redis服务重启的时候都会从该文件中把数据加载到内存中。
在60秒内有10000次操作即触发RDB持久化。
没有满足第一种条件时,在900秒内有1次操作即触发RDB持久化。
没有满足第二种条件时,在300秒内有10次操作即触发RDB持久化。
RDB持久化除了可以根据配置中的策略来触发外,还可以使用save和bgsave命令手动来触发。这两个命令的区别在于save会阻塞服务器进程。在执行save命令的过程中,服务器不能处理任何请求,但是bgsave(background save,后台保存)命令会通过一个子进程在后台处理数据RDB持久化。本质上save和bgsave调用的都是rdbSave函数,所以Redis不允许save和bgsave命令同时执行,当然这也是为了避免RDB文件数据出现不一致性的问题。
每次都是一个大文件,备份写入IO操作笔记大,很容易耗时,影响进程资源使用。
如果最近一次进程崩溃,那么最近一次数据备份后的数据就被丢失。
文件直接就可以当冷备使用
AOF(Append Only File)以独立日志的方式记录每次的写命令,可以很好地解决了数据持久化的实时性。系统重启时可以重新执行AOF文件中的命令来恢复数据。AOF会先把命令追加亩饥在AOF缓冲区,然后根据对应策略写入硬盘。
AOF的实现流程有三个步骤
步骤一
把命令追加到AOF缓冲区,
步骤二
将缓冲区的内容写入程序缓冲区
步骤三
将程序缓冲区的内容写入文件
当AOF持久化功能处于开启状态时,服务器每执行完一个命令就会将命令以迅腔返协议格式追加写入redisServer结构体的aof_buf缓冲区。而在服务重启的时候会把AOF文件加载到缓冲区中。
AOF有 三种触发机制
·always:每次发生数据变更都会被立即记录到磁盘,性能较差,但数据完整性比较好。
·everysec:每秒钟将aof_buf缓冲区的内容写入AOF文件,如果宕机,就会有1秒内的数据丢失。
·no:将数据同步操作交给操作系统来处理,性能最好,但是数据可靠性最差。在配置文件中设置appendonly=yes后,若没有指定apendfsync,默认会使用everysec选项。
写入指令随着时间的推移,记录了很多重复的指令,导致数据量非常大。
RDB优先级高于AOF
RDB小,AOF较大
RDB慢,AOF快
RDB快,AOF慢
怎么实现redis的数据库的缓存(redis实现缓存的流程)
大致为两种措施:
一、脚本同步:
1、自己写脚本将数据库数据写入到redis/memcached。
2、这就涉及到实时数据变更的问题(mysqlrowbinlog的实时分析),binlog增量订阅Alibaba的canal,以及缓存层数据丢失/失效后的数据同步恢复问题。
二、纯贺业务层实现:
1、先读取nosql缓存层,没有数据再读取mysql层,并写入数据到nosql。
2、nosql层做好多节点分布式(一致性hash),以及节点失效后替代方案(多层hash寻找相邻替代节点),和数据震荡恢复了。
redis实现数据库缓存的分析:
对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached、FileSystem等)展示数据,可能在缓存的存取上会有很大的开销则裤差,并不能很好的满足需要,而Redis这样基于内存的NoSQL数据库,就非常适合担任实时数据的容器。
但是往往又有数据可靠性的需求,采用MySQL作为数据存储,不会因为内存问题而引起数据丢失,同时也可以利用关系数据库的特性实现很多功能。所以就会很自然的想到是否可以采用MySQL作为数据存孙皮储引擎,Redis则作为Cache。
MySQL到Redis数据复制方案,无论MySQL还是Redis,自身都带有数据同步的机制,比较常用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog来实现的,这样的数据复制其实还是一个异步过程,只不过当服务器都在同一内网时,异步的延迟几乎可以忽略。那么理论上也可用同样方式,分析MySQL的binlog文件并将数据插入Redis。
因此这里选择了一种开发成本更加低廉的方式,借用已经比较成熟的MySQLUDF,将MySQL数据首先放入Gearman中,然后通过一个自己编写的PHPGearmanWorker,将数据同步到Redis。比分析binlog的方式增加了不少流程,但是实现成本更低,更容易操作。
Redis分布式缓存搭建
花了两天时间整理了之前记录的Redis单体与哨兵模式的搭建与使用,又补齐了集群模式的使用和搭建经验,并对集群的一些个原理做了理解。
笔者安装中遇到的一些问题:
如果make报错,可能是没装gcc或者gcc++编辑器,安装之 yum -y install gcc gcc-c++ kernel-devel ,有可能还是提示一些个c文件编译不过,gcc -v查看下版本,如果不到5.3那么升级一下gcc:
在 /etc/profile 追加一行 source /opt/rh/devtoolset-9/enable
scl enable devtoolset-9 bash
重新make clean, make
这回编译通态纳哪过了,提示让你最好make test一下/
执行make test ,如果提示 You need tcl 8.5 or newer in order to run the Redis test
那就升级tcl, yum install tcl
重新make test,如果还有error就删了目录,重新tar包解压重新make , make test
\o/ All tests passed without errors! ,表示编译成功。
然后make install即可。
直接运行命令: ./redis-server /usr/redis-6.0.3/redis.conf
redis.conf 配置文件里帆码 bind 0.0.0.0 设置外部访问, requirepass xxxx 设置密码。
redis高可用方案有两种:
常用搭建方案为1主1从或1主2从+3哨兵监控主节点, 以及3主3从6节点集群。
(1)sentinel哨兵
/usr/redis-6.0.3/src/redis-sentinel /usr/redis-6.0.3/sentinel2.conf
sentinel2.conf配置:
坑1:master节点也会在故障转移后成为从节点,也需要配置masterauth
当kill master进程之后,经过sentinel选举,slave成为了新的master,再次启动原master,提示如下错误:
原因是此时的master再次启动已经是slave了,需要向现在的新master输入密码,所以需要在master.conf
中配置:
坑2:哨兵配置文件要暴露客户端可以访问到的master地址
在 sentinel.conf 配置文件的 sentinel monitor mymaster 122.xx.xxx.xxx 6379 2 中,配置该哨兵对应的master名字、master地址和端口,以及达到多少个哨兵选举通过认为master挂掉。其中master地址要站在redis访问者(也就是客户端)的角度、配置茄稿访问者能访问的地址,例如sentinel与master在一台服务器(122.xx.xxx.xxx)上,那么相对sentinel其master在本机也就是127.0.0.1上,这样 sentinel monitor mymaster 127.0.0.1 6379 2 逻辑上没有问题,但是如果另外服务器上的springboot通过lettuce访问这个redis哨兵,则得到的master地址为127.0.0.1,也就是springboot所在服务器本机,这显然就有问题了。
附springboot2.1 redis哨兵配置:
坑3:要注意配置文件.conf会被哨兵修改
redis-cli -h localhost -p 26379 ,可以登到sentinel上用info命令查看一下哨兵的信息。
曾经遇到过这样一个问题,大致的信息如下
slaves莫名其妙多了一个,master的地址也明明改了真实对外的地址,这里又变成127.0.0.1 !
最后,把5个redis进程都停掉,逐个检查配置文件,发现redis的配置文件在主从哨兵模式会被修改,master的配置文件最后边莫名其妙多了一行replicaof 127.0.0.1 7001, 怀疑应该是之前配置错误的时候(见坑2)被哨兵动态加上去的! 总之,实践中一定要多注意配置文件的变化。
(2)集群
当数据量大到一定程度,比如几十上百G,哨兵模式不够用了需要做水平拆分,早些年是使用codis,twemproxy这些第三方中间件来做分片的,即 客户端 - 中间件 - Redis server 这样的模式,中间件使用一致性Hash算法来确定key在哪个分片上。后来Redis官方提供了方案,大家就都采用官方的Redis Cluster方案了。
Redis Cluster从逻辑上分16384个hash slot,分片算法是 CRC16(key) mod 16384 得到key应该对应哪个slot,据此判断这个slot属于哪个节点。
每个节点可以设置1或多个从节点,常用的是3主节点3从节点的方案。
reshard,重新分片,可以指定从哪几个节点移动一些hash槽到另一个节点去。重新分片的过程对客户端透明,不影响线上业务。
搭建Redis cluster
redis.conf文件关键的几个配置:
启动6个集群节点
[root@VM_0_11_centos redis-6.0.3]# ps -ef|grep redis
root 5508 1 0 21:25 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7001 [cluster]
root 6903 1 0 21:32 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7002 [cluster]
root 6939 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7003 [cluster]
root 6966 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7004 [cluster]
root 6993 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7005 [cluster]
root 7015 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7006 [cluster]
这时候这6个节点还是独立的,要把他们配置成集群:
说明: -a xxxx 是因为笔者在redis.conf中配置了requirepass xxxx密码,然后 --cluster-replicas 1 中的1表示每个master节点有1个从节点。
上述命令执行完以后会有一个询问: Can I set the above configuration? yes同意自动做好的分片即可。
最后 All 16384 slots covered. 表示集群中16384个slot中的每一个都有至少有1个master节点在处理,集群启动成功。
查看集群状态:
坑1:暴露给客户端的节点地址不对
使用lettuce连接发现连不上,查看日志 Connection refused: no further information: /127.0.0.1:7002 ,跟之前哨兵配置文件sentinel.conf里边配置master地址犯的错误一样,集群启动的时候带的地址应该是提供给客户端访问的地址。
我们要重建集群:先把6个redis进程停掉,然后删除 nodes-7001.conf 这些节点配置文件,删除持久化文件 dump.rdb 、 appendonly.aof ,重新启动6个进程,在重新建立集群:
然后,还是连不上,这次报错 connection timed out: /172.xx.0.xx:7004 ,发现连到企鹅云服务器的内网地址上了!
解决办法,修改每个节点的redis.conf配置文件,找到如下说明:
所以增加配置:
然后再重新构建集群,停进程、改配置、删除节点文件和持久化文件、启动进程、配置集群。。。再来一套(累死了)
重新使用Lettuce测试,这次终于连上了!
坑2:Lettuce客户端在master节点故障时没有自动切换到从节点
name这个key在7002上,kill这个进程模拟master下线,然后Lettuce一直重连。我们期望的是应该能自动切换到其slave 7006上去,如下图:
重新启动7002进程,
7006已成为新master,7002成为它的slave,然后Lettuce也能连接上了。
解决办法,修改Lettuce的配置:
笔者用的是springboot 2.1 spring-boot-starter-data-redis 默认的Lettuce客户端,当使用Redis cluster集群模式时,需要配置一下 RedisConnectionFactory 开启自适应刷新来做故障转移时的自动切换从节点进行连接。
重新测试:停掉master 7006,这次Lettuce可以正常切换连到7002slave上去了。(仍然会不断的在日志里报连接错误,因为需要一直尝试重连7006,但因为有7002从节点顶上了、所以应用是可以正常使用的)
Redis不保证数据的强一致性
Redis并不保证数据的强一致性,也就是取CAP定理中的AP
关于一致性Hash算法,可以参考 一致性Hash算法 - (jianshu.com)
Redis cluster使用的是hash slot算法,跟一致性Hash算法不太一样,固定16384个hash槽,然后计算key落在哪个slot里边(计算key的CRC16值再对16384取模),key找的是slot而不是节点,而slot与节点的对应关系可以通过reshard改变并通过gossip协议扩散到集群中的每一个节点、进而可以为客户端获知,这样key的节点寻址就跟具体的节点个数没关系了。也同样解决了普通hash取模算法当节点个数发生变化时,大量key对应的寻址都发生改动导致缓存失效的问题。
比如集群增加了1个节点,这时候如果不做任何操作,那么新增加的这个节点上是没有slot的,所有slot都在原来的节点上且对应关系不变、所以没有因为节点个数变动而缓存失效,当reshard一部分slot到新节点后,客户端获取到新迁移的这部分slot与新节点的对应关系、寻址到新节点,而没迁移的slot仍然寻址到原来的节点。
关于热迁移,猜想,内部应该是先做复制迁移,等迁移完了,再切换slot与节点的对应关系,复制没有完成之前仍按照原来的slot与节点对应关系去原节点访问。复制结束之后,再删除原节点上已经迁移的slot所对应的key。
与哨兵模式比较类似,当1个节点发现某个master节点故障了、会对这个故障节点进行pfail主观宕机,然后会通过gossip协议通知到集群中的其他节点、其他节点也执行判断pfail并gossip扩散广播这一过程,当超过半数节点pfail时那么故障节点就是fail客观宕机。接下来所有的master节点会在故障节点的从节点中选出一个新的主节点,此时所有的master节点中超过半数的都投票选举了故障节点的某个从节点,那么这个从节点当选新的master节点。
所有节点都持有元数据,节点之间通过gossip这种二进制协议进行通信、发送自己的元数据信息给其他节点、故障检测、集群配置更新、故障转移授权等等。
这种去中心化的分布式节点之间内部协调,包括故障识别、故障转移、选主等等,核心在于gossip扩散协议,能够支撑这样的广播协议在于所有的节点都持有一份完整的集群元数据,即所有的节点都知悉当前集群全局的情况。
Redis高可用方案 - (jianshu.com)
面试题:Redis 集群模式的工作原理能说一下么 - 云+社区 - 腾讯云 (tencent.com)
深度图解Redis Cluster原理 - detectiveHLH - 博客园 (cnblogs.com)
Redis学习笔记之集群重启和遇到的坑-阿里云开发者社区 (aliyun.com)
云服务器Redis集群部署及客户端通过公网IP连接问题
缓存击穿、穿透、雪崩及Redis分布式锁
分布式锁: setnx ,redisson 并发问题
幂等问题: 落表状态,Redis
缓存击穿: 指缓存中无,db中有
原因: 一个key高并发恰好失效导致大量请求到db
方案: 加锁,自旋锁,或一个线程查芹乎db,一个线程监控(直接用Redisson分布式锁)
缓存穿透:指缓存和db中均无
原因: 一般是恶意请求
方案: 加布隆过滤,或查db无时,也设置缓存,value为某些特殊表示或"null"
雪崩:指缓存同时大量失效
原因: 大量的key同时失效,db压力加大
方案: 设置失效时间是增加随机数
问题方案文献:
(图例洞首简分析)
Redis分布式锁:
事务未执行完锁已到期释放问题:使用Redissoin解决续租问题,内部已解决
分布式锁文献:
(setnx + expire同时操作)
====================================
Redis原理与应纳裤用
关于分布式缓存redis方案和分布式缓存原理架构go实现的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。