700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > redis进阶持久化 事务 主从复制 集群高可用

redis进阶持久化 事务 主从复制 集群高可用

时间:2024-04-18 14:26:43

相关推荐

redis进阶持久化 事务 主从复制 集群高可用

一、redis持久化

1 Rdb方式

Rdb方式一般为redis的默认数据持久化方式.默认开启,是通过手动(save-阻塞式,bgsave-异步)或周期性方式保存redis中key/value的一种机制。在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储 。

1.1 rdb方式配置

可通过修改redis.conf文件,按照自己的方式配置

# 这里表示每隔60s,如果有超过1000个key发生了变更,那么就生成一个新的dump.rdb文件,就是当前redis内存中完整的数据快照,这个操作也被称之为snapshotting(快照)。save 60 1000# 持久化 rdb文件遇到问题时,主进程是否接受写入,yes 表示停止写入,如果是no 表示redis继续提供服务。stop-writes-on-bgsave-error yes # 在进行快照镜像时,是否进行压缩。yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间。rdbcompression yes# 一个CRC64的校验就被放在了文件末尾,当存储或者加载rbd文件的时候会有一个10%左右的性能下降,为了达到性能的最大化,你可以关掉这个配置项。rdbchecksum yes# 快照的文件名dbfilename dump.rdb# 存放快照的目录dir /var/lib/redis

1.2 rdb方式持久化实践

试验一

在redis中保存几条数据,然后执行shutdown关闭redis,然后再重启redis,看看刚才插入的数据是否还在?假如数据还在,为什么?

因为,通过redis-cli shutdown这种方式去停掉redis,其实是一种安全退出的模式,redis在退出的时候会将内存中的数据立即生成一份完整的rdb快照

127.0.0.1:6379> set name zhangsanOK127.0.0.1:6379> shutdown[root@localhost ~]# docker start redis01redis01[root@localhost ~]# docker exec -it redis01 redis-cli127.0.0.1:6379> get name"zhangsan"

试验二

在redis中再保存几条新的数据,用kill -9粗暴杀死redis进程,模拟redis故障异常退出,导致内存数据丢失的场景?

这次就发现,redis进程异常被杀掉,几条最新的数据就丢失了

打开一个客户端存入数据

127.0.0.1:6379> keys *(empty array)127.0.0.1:6379> set name zhangsanOK

打开二个客户端杀毒redis进程,然后启动redis,登录看上面设置的name属性是否还存在

[root@localhost ~]# ps -ef | grep redispolkitd 1983 1965 0 12:33 ? 00:00:00 redis-server *:6379root 2033 1965 0 12:33 pts/0 00:00:00 redis-cliroot 2135 1601 0 12:40 pts/0 00:00:00 docker exec -it redis01 redis-cliroot 2151 1965 0 12:40 ? 00:00:00 redis-cliroot 2163 2050 0 12:41 pts/1 00:00:00 grep --color=auto redis[root@localhost ~]# kill -9 1983[root@localhost ~]# docker psCONTAINER ID IMAGECOMMAND CREATED STATUS PORTSNAMES[root@localhost ~]# docker start redis01redis01[root@localhost ~]# docker exec -it redis01 redis-cli127.0.0.1:6379> keys *(empty array)

试验三

手动调用save(同步保存)或者bgsave(异步保存)执行rdb快照生成.然后杀掉redis进程,再重启检测是否还有刚刚保存的数据

127.0.0.1:6379> keys *(empty array)127.0.0.1:6379> set name zhangsanOK127.0.0.1:6379> saveOK127.0.0.1:6379> set age 10OK127.0.0.1:6379> bgsaveBackground saving started

1.3 rdb持久化总结

Redis中的save和bgsave有什么不同?

Redis Save 命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘BGSAVE 命令执行之后立即返回 OK ,然后 Redisfork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

RDB持久化机制优点

第一:RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程云服务上去,在国内可以是阿里云的ODPS分布式存储上,以预定好的备份策略来定期备份redis中的数据.

第二:RDB对redis对外提供的读写服务,影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。

第三:相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复redis进程,更加快速。

RDB持久化机制缺点

假如redis故障时,要尽可能少的丢失数据,那么RDB方式不太好,它都是每隔5分钟或更长时间做一次快照,这个时候一旦redis进程宕机,那么会丢失最近几分钟的数据。

2 Aof方式

AOF 机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,默认关闭

2.1 AOF方式配置

# 是否开启AOF,默认关闭appendonly yes# 指定 AOF 文件名appendfilename appendonly.aof# Redis支持三种刷写模式:# appendfsync always #每次收到写命令就立即强制写入磁盘,类似MySQL的sync_binlog=1,是最安全的。但该模式下速度也是最慢的,一般不推荐使用。appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做平衡,推荐该方式。# appendfsync no#完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不推荐。#在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。#设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yesno-appendfsync-on-rewrite yes#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。auto-aof-rewrite-percentage 100#当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。auto-aof-rewrite-min-size 64mb

2.2 AOF方式持久化实践

第一:打开AOF的开关,启用AOF持久化

第二:写入一些数据,观察AOF文件(appendonly.aof)中的日志内容

第三:kill -9杀掉redis进程,重新启动redis进程,发现数据被恢复回来了,就是从AOF文件中恢复回来的,redis进程启动的时候,直接就会从appendonly.aof中加载所有的日志,把内存中的数据恢复回来。

2.3 AOF持久化总结

如何理解AOF方式中的rewrite操作

edis中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存清除的算法清理掉。也就是说redis中的数据会不断淘汰掉旧的,只有一部分常用的数据会被自动保留在redis内存中,所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在AOF中,AOF日志文件就一个,会不断的膨胀,到很大很大。

所以,AOF会自动在后台每隔一定时间做rewrite操作,比如日志里已经存放了针对100w数据的写日志了; redis内存只剩下10万; 基于内存中当前的10万数据构建一套最新的日志,到AOF中; 覆盖之前的老日志; 确保AOF日志文件不会过大,保持跟redis内存数据量一致.

AOF持久化机制优点

AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据.AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。 因此在进行rewrite切换时可以更好的保证数据安全性AOF日志文件的命令通过易读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据.

AOF持久化机制缺点

对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的AOF这种基于命令日志方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。

二、redis事务

1 简介

Redis采用了乐观锁方式进行事务控制,它使用watch命令监视给定的key,当**exec(提交事务)**时候如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key。注意watch的key是对整个连接有效的,如果连接断开,监视和事务都会被自动清除。当然exec,discard,unwatch命令都会清除连接中的所有监视。

2 基本指令

redis进行事务控制时,通常是基于如下指令进行实现

multi 开启事务exec 提交事务discard 取消事务watch 监控,如果监控的值发生变化,则提交事务时会失败unwatch 去掉监控

说明:Redis保证一个事务中的所有命令要么都执行,要么都不执行(原子性)。如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行。而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令。

3 redis事务控制实践

3.1 exec提交事务

模拟转账

127.0.0.1:6379> mset zhangsan 200 lisi 300OK127.0.0.1:6379> mget zhangsan lisi1) "200"2) "300"127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379(TX)> incrby zhangsan 100 #所有指令操作都会进入队列QUEUED127.0.0.1:6379(TX)> decrby lisi 100QUEUED127.0.0.1:6379(TX)> mget zhangsan lisiQUEUED127.0.0.1:6379(TX)> exec #提交事务1) (integer) 3002) (integer) ) 1) "300"2) "200"127.0.0.1:6379> mget zhangsan lisi1) "300"2) "200"

3.2 discard取消事务

注意redis事务太简单,没有回滚,而只有取消。取消事物可以理解为回滚

127.0.0.1:6379> mget zhangsan lisi1) "300"2) "200"127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> incrby zhangsan 500QUEUED127.0.0.1:6379(TX)> discardOK127.0.0.1:6379> mget zhangsan1) "300"127.0.0.1:6379> exec(error) ERR EXEC without MULTI

当出现错误指令时,事务也会自动取消

127.0.0.1:6379> mget zhangsan lisi1) "300"2) "200"127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> incrby zhangsan 500QUEUED127.0.0.1:6379(TX)> aaaa(error) ERR unknown command `aaaa`, with args beginning with:127.0.0.1:6379(TX)> exec(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get zhangsan"300"

3.3 秒杀抢票事务处理

打开客户端1,执行如下操作

127.0.0.1:6379> set ticket 1OK127.0.0.1:6379> watch ticket #乐观锁,对值进行观察,改变则事务失败OK127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379(TX)> decr ticketQUEUED

打开客户端2,执行如下操作,演示还没等客户端1提交事务,此时客户端2把票买到了

127.0.0.1:6379> get ticket"1"127.0.0.1:6379> decr ticket(integer) 0

回到客户端1:提交事务,检查ticket的值

127.0.0.1:6379(TX)> exec(nil) #执行事务失败127.0.0.1:6379> get ticket"0"127.0.0.1:6379> unwatch #取消监控OK

3.4 Jedis 客户端事务操作

基于Jedis进行事务测试

@Testpublic void testTransaction() {Jedis jedis = new Jedis("192.168.64.3", 6379);//jedis.auth("123456");jedis.set("tony", "300");jedis.set("jack", "500");//实现操作,tony转账100给jack//开启事务Transaction multi = jedis.multi();//执行业务操作try {multi.decrBy("tony", 100);multi.incrBy("jack", 100);int n = 100 / 0;//提交事务multi.exec();} catch (Exception e) {e.printStackTrace();multi.discard();//取消事务}System.out.println(jedis.mget("tony", "jack"));jedis.close();}

Jedis 客户端秒杀操作实践

/*** 秒杀:两个线程都去抢购同一张票(考虑乐观锁)*/public class JedisSecKillTests {public static void secKill() {Jedis jedis = new Jedis("192.168.58.129", 6379);jedis.auth("123456");jedis.watch("ticket", "money");String ticket = jedis.get("ticket");System.out.println(Thread.currentThread().getName() + "=》抢票前票数" + ticket + " money=" + jedis.get("money"));if (ticket == null || "0".equals(ticket)) throw new RuntimeException("已无库存");Transaction multi = jedis.multi();try {multi.decr("ticket");multi.incrBy("money", 100);multi.exec();} catch (Exception e) {e.printStackTrace();multi.discard();} finally {jedis.unwatch();System.out.println(Thread.currentThread().getName() + "-》抢票后票数" + jedis.get("ticket") + " money=" + jedis.get("money"));jedis.close();}}public static void main(String[] args) {Jedis jedis = new Jedis("192.168.58.129", 6379);jedis.auth("123456");jedis.set("ticket", "1");jedis.set("money", "0");Thread t1 = new Thread(() -> secKill());Thread t2 = new Thread(() -> secKill());Thread t3 = new Thread(() -> secKill());Thread t4 = new Thread(() -> secKill());t1.start();t2.start();t3.start();t4.start();}}

三、redis主从复制

单个Redis支持的读写能力还是有限的,此时我们可以使用多个redis,主从(Master/Slave)架构来提高redis的并发处理能力,其中,master负责读写,并将数据同步到salve,从节点负责读操作.

1 快速入门

基于redis,设计一主从架构,一个Master,两个Slave,其中Master负责Redis读写操作,并将数据同步到Slave,Slave只负责读

说明:假如已有redis服务,可先将原先所有redis服务停止,并启动新的redis容器

docker run -p 6379:6379 --name redis6379 \-v /usr/local/docker/redis01/data:/data \-v /usr/local/docker/redis01/conf/redis.conf:/etc/redis/redis.conf \-d redis redis-server /etc/redis/redis.conf \--appendonly yes

docker run -p 6380:6379 --name redis6380 \-v /usr/local/docker/redis02/data:/data \-v /usr/local/docker/redis02/conf/redis.conf:/etc/redis/redis.conf \-d redis redis-server /etc/redis/redis.conf \--appendonly yes

docker run -p 6381:6379 --name redis6381 \-v /usr/local/docker/redis03/data:/data \-v /usr/local/docker/redis03/conf/redis.conf:/etc/redis/redis.conf \-d redis redis-server /etc/redis/redis.conf \--appendonly yes

检测redis服务角色,启动三个客户端,分别登陆三台redis容器服务,通过info指令进行角色查看

127.0.0.1:6379> info replication# Replicationrole:master #默认新启动的三个redis服务角色都为master.connected_slaves:0master_failover_state:no-failovermaster_replid:37e4ace8110b77f7a6efaf4db56d2570e6442b13master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0

检测redis6379的ip设置docker inspect redis6379

"Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "06c371111354b9dd2b704724c9ff487a529727f4fa29010f3174acb8859f6f3e","EndpointID": "fa246f9b4937db5ea39c1a4df2a73aee1f132a790ad5a40fa5779ce7e58f814a","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}

设置Master/Slave架构,分别登陆redis6380/redis6381,执行以下命令:slaveof 172.17.0.2 6379

说明,假如master有密码,需要在slave的redis.conf配置文件中添加"masterauth 你的密码"这条语句,然后重启redis再执行slaveof 指令操作

再次登陆redis6379

127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:2

登陆redis6379测试,master读写都可以

登陆redis6380测试,slave只能读

127.0.0.1:6379> get a"100"127.0.0.1:6379> set num 100(error) READONLY You can't write against a read only replica.

Java中的读写测试分析

@Autowiredprivate RedisTemplate redisTemplate;@Testvoid testMasterReadWrite(){//配置文件端口为6379ValueOperations valueOperations = redisTemplate.opsForValue();valueOperations.set("role", "master6379");Object role = valueOperations.get("role");System.out.println(role);}@Testvoid testSlaveRead(){//配置文件端口为6380ValueOperations valueOperations = redisTemplate.opsForValue();Object role = valueOperations.get("role");System.out.println(role);}

2 主从同步原理分析

Redis的主从结构可以采用一主多从结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步

Redis全量同步

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:

1)从服务器连接主服务器,发送SYNC命令;

2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;

3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;

4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;

5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;

6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

Redis增量同步

Redis增量复制是指Slave初始化后,开始正常工作时主服务器发生的写操作同步到从服务器的过程。 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

3 Redis的replication机制

redis采用异步方式复制数据到slave节点。一个master node是可以配置多个slave node的。slave node也可以连接其他的slave node。slave node做复制的时候,是不会block master node的正常工作的。slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了。slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量。

4 Redis哨兵模式

哨兵(Sentinel)是Redis的主从架构模式下,实现高可用性(high availability)的一种机制。

由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

4.1 快速入门

分别进入3台redis容器内部进行配置,在容器指定目录/etc/redis创建sentinel.conf文件,文件内容为:

sentinel monitor redis6379 172.17.0.2 6379 1

其中, 如上指令表示要的监控的master, redis6379为服务名, 172.17.0.2和6379为master的ip和端口,1表示多少个sentinel认为一个master失效时,master才算真正失效

在每个redis容器内部的/etc/redis目录下执行如下指令(最好是在多个客户端窗口执行),启动哨兵服务

redis-sentinel sentinel.conf

关闭redis6379 master服务,其它客户端窗口,检测日志输出

32:X 19 Aug 05:55:44.130 # +switch-master redis6379 172.17.0.2 6379 172.17.0.4 637932:X 19 Aug 05:55:44.131 * +slave slave 172.17.0.3:6379 172.17.0.3 6379 @ redis6379 172.17.0.4 637932:X 19 Aug 05:55:44.131 * +slave slave 172.17.0.2:6379 172.17.0.2 6379 @ redis6379 172.17.0.4 6379

登陆ip为172.17.0.4对应的服务查看

127.0.0.1:6379> info replication# Replicationrole:master #redis6381服务现在已经变为masterconnected_slaves:1slave0:ip=172.17.0.3,port=6379,state=online,offset=47869,lag=0master_failover_state:no-failovermaster_replid:e510eb26c0448660da8175dcb7f1eccf271b4d84master_replid2:c004f969ed05b145fc851e63e19fab7a6a62d8e5master_repl_offset:48005second_repl_offset:30951

4.2 Sentinel 配置进阶

对于sentinel.conf文件中的内容,我们还可以基于实际需求,进行增强配置,例如:

sentinel monitor redis6379 172.17.0.2 6379 1 daemonize yes #后台运行logfile "/var/log/sentinel_log.log" #运行日志sentinel down-after-milliseconds redis6379 30000 #master失效了多长时间才认为失效,默认30秒

4.3 哨兵工作原理分析

每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值(这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒), 则这个实例会被 Sentinel 标记为主观下线如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

四、Redis集群高可用

Redis单机模式可靠性保证不是很好,容易出现单点故障,同时其性能也受限于CPU的处理能力,实际开发中Redis必然是高可用的,所以单机模式并不是我们的终点,我们需要对目前redis的架构模式进行升级。

Sentinel模式做到了高可用,但是实质还是只有一个master在提供服务(读写分离的情况本质也是master在提供服务),当master节点所在的机器内存不足以支撑系统的数据时,就需要考虑集群了。

Redis集群架构实现了对redis的水平扩容,即启动N个redis节点,将整个数据分布存储在这N个redis节点中,每个节点存储总数据的1/N。redis集群通过分区提供一定程度的可用性,即使集群中有一部分节点失效或无法进行通讯,集群也可以继续处理命令请求。

对于redis集群(Cluster),一般最少设置为6个节点,3个master,3个slave,其简易架构如下:

1 创建集群

准备网络环境,创建虚拟网卡,主要是用于redis-cluster能与外界进行网络通信,一般常用桥接模式

docker network create redis-net

查看docker的网卡信息docker network ls

查看docker网络详细信息,可使用命令docker network inspect redis-net

准备redis配置模板

mkdir -p /usr/local/docker/redis-cluster

cd /usr/local/docker/redis-cluster

vim redis-cluster.tmpl

在redis-cluster.tmpl中输入以下内容

port ${PORT}#节点端口,即对外提供通信的端口cluster-enabled yes #是否启用集群cluster-config-file nodes.conf #集群配置文件cluster-node-timeout 5000 #连接超时时间cluster-announce-ip 192.168.64.3 #宿主机ipcluster-announce-port ${PORT} #集群节点映射端口cluster-announce-bus-port 1${PORT} #集群总线端口appendonly yes #持久化模式

创建节点配置文件,在redis-cluser中执行以下命令

for port in $(seq 8010 8015); \do \mkdir -p ./${port}/conf \&& PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf \&& mkdir -p ./${port}/data; \done

for 变量 in $(seq var1 var2);do …; done为linux中的一种shell 循环脚本, 例如:

[root@centos7964 ~]# for i in $(seq 1 3);> do echo $i;> done;123[root@centos7964 ~]#

其中,指令envsubst <源文件>目标文件,用于将源文件内容更新到目标文件中.

查看配置文件内容指令:cat /usr/local/docker/redis-cluster/801{0..5}/conf/redis.conf

创建集群中的redis节点容器

for port in $(seq 8010 8015); \do \docker run -it -d -p ${port}:${port} -p 1${port}:1${port} \--privileged=true -v /usr/local/docker/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \--privileged=true -v /usr/local/docker/redis-cluster/${port}/data:/data \--restart always --name redis-${port} --net redis-net \--sysctl net.core.somaxconn=1024 redis redis-server /usr/local/etc/redis/redis.conf; \done

其中, --privileged=true表示让启动的容器用户具备真正root权限, --sysctl net.core.somaxconn=1024 这是一个linux的内核参数,用于设置请求队列大小,默认为128,后续启动redis的启动指令需要先放到这个请求队列中,然后依次启动.

创建成功以后,通过docker ps指令查看节点内容。

创建redis-cluster集群配置

docker exec -it redis-8010 bash

redis-cli --cluster create 192.168.64.3:8010 192.168.64.3:8011 192.168.64.3:8012 192.168.64.3:8013 192.168.64.3:8014 192.168.64.3:8015 --cluster-replicas 1

如上指令要尽量放在一行执行,其中最后的1表示主从比例,当出现选择提示信息时,输入yes即可。

连接redis-cluster,并添加数据到redisredis-cli -c -h 192.168.64.3 -p 8010

2 创建集群出错解决方案

在搭建过程,可能在出现问题后,需要停止或直接删除docker容器,可以使用以下参考命令

批量停止docker 容器:

docker ps -a | grep -i "redis-801*" | awk '{print $1}' | xargs docker stop

批量删除docker 容器:

docker ps -a | grep -i "redis-801*" | awk '{print $1}' | xargs docker rm -f

批量删除文件

rm -rf 801{0..5}/conf/redis.conf

3 Jedis读写数据测试

@Testpublic void testJedisCluster() {//1.构建集群对象Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("192.168.64.3", 8010));nodes.add(new HostAndPort("192.168.64.3", 8011));nodes.add(new HostAndPort("192.168.64.3", 8012));nodes.add(new HostAndPort("192.168.64.3", 8013));nodes.add(new HostAndPort("192.168.64.3", 8014));nodes.add(new HostAndPort("192.168.64.3", 8015));JedisCluster jedisCluster = new JedisCluster(nodes);jedisCluster.set("redis", "6.2.5");System.out.println(jedisCluster.get("redis"));jedisCluster.close();}

4 RedisTemplate读写数据测试

spring:redis:#host: 192.168.64.3#password: 123456#port: 6380cluster: #集群配置nodes: 192.168.64.3:8010,192.168.64.3:8011,192.168.64.3:8012,192.168.64.3:8013,192.168.64.3:8014,192.168.64.3:8015max-redirects: 3timeout: 5000 #超时时间database: 0jedis: #连接池pool:max-idle: 8max-wait: 0

@SpringBootTestpublic class RedisClusterTests {@Autowiredprivate StringRedisTemplate redisTemplate;//RedisTemplate底层采用JDK方式的序列化@Testvoid testRedisCluster() {ValueOperations valueOperations = redisTemplate.opsForValue();//redisTemplate.setKeySerializer(new StringRedisSerializer());//redisTemplate.setValueSerializer(new StringRedisSerializer());valueOperations.set("key1", "100");valueOperations.increment("key1", 100);System.out.println(valueOperations.get("key1"));}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。