其余还分享Linux,Nginx,MySQL,Redis,流媒体,P2P,K8S,Docker,TCP/IP,协程等等视频。须要的朋友可往后台私信【架构】获取
redis单点、redis主从、redis哨兵 sentinel,redis集群cluster配置搭建与利用
1 .redis 安装及配置 1.1 redis 单点 1.1.2 在命令窗口操作redis 1.1.3 利用jedis客户端操作redis 1.1.4 利用spring-redis操作 1.1.5 利用Lettuce操作redis 1.2 redis 主从 1.3 哨兵sentinel 1.3.2 哨兵sentinel配置 1.3.3 启动哨兵,利用jedis连接哨兵操作redis 1.3.4 编写程序&运行 1.3.5仿照主节点宕机情形 1.4 redis cluster 1.4.1 配置 redis cluster 集群 1.4.2启动redis集群 1.4.3 利用jedis连接redis cluster 集群 1.5 总结
redis单点、redis主从、redis哨兵 sentinel,redis集群cluster配置搭建与利用redis是如今被互联网公司利用最广泛的一个中间件,我们打开GitHub搜索redis,边可以看到,该项目的先容是这样的:

Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.
从这句话中,我们可以提取其特性的关键字:in-memory database ,内存数据库support:Strings , lists, sets ,hashes ,hyperloglogs, bitmaps也便是高性能,支持数据类型多。本文假设你已经理解redis的基本利用,进而谈论redis的单点,高可用,集群。
1 .redis 安装及配置redis的安装十分大略,打开redis的官网 http://redis.io 。
下载一个最新版本的安装包,如 redis-version.tar.gz解压 tar zxvf redis-version.tar.gz实行 make (实行此命令可能会报错,例如确实gcc,一个个办理即可)如果是 mac 电脑,安装redis将十分大略实行brew install redis即可。
安装好redis之后,我们先不慌利用,前辈行一些配置。打开redis.conf文件,我们紧张关注以下配置:
port 6379 # 指定端口为 6379,也可自行修正 daemonize yes # 指定后台运行
1.1 redis 单点
安装好redis之后,我们来运行一下。启动redis的命令为 :
redishome/bin/redis-server path/to/redis.config
假设我们没有配置后台运行(即:daemonize no),那么我们会看到如下启动日志:
93825:C 20 Jan 2019 11:43:22.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo93825:C 20 Jan 2019 11:43:22.640 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=93825, just started93825:C 20 Jan 2019 11:43:22.640 # Configuration loaded93825:S 20 Jan 2019 11:43:22.641 Increased maximum number of open files to 10032 (it was originally set to 256). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 5.0.3 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6380 | `-._ `._ / _.-' | PID: 93825 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
无论是否配置了后台运行,启动成功之后,我们可以新开一个命令行窗口来操作试试。
1.1.2 在命令窗口操作redis利用命令:telnet localhost 6379 来连接redis,或者你可以直策应用代码来连接测试。连接之后,看到如下信息:
Connected to localhost.Escape character is '^]'.
我们输入几个命令试试:
set hello world 设置key-valueget hello 获取key值expire hello 10 设置10秒过期ttl hello 查看过期韶光del hello 删除key
如此,我们便体验了一把redis,可以说是非常大略了。刚才我们是利用命令行来操作redis的,下面我们来利用代码操作一下redis,以Java为例,我们利用一个开源的 java - redis客户端。
1.1.3 利用jedis客户端操作redis打开GitHub,搜索redis,进入到项目主页之后,我们可以看到利用方法:
加入jedis依赖<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.0</version> <type>jar</type> <scope>compile</scope></dependency>
编写代码如下
Jedis jedis = new Jedis("localhost",6379);jedis.set("hello", "world");String value = jedis.get("hello");System.out.println(value); // get worldjedis.del("hello");System.out.println(jedis.get("hello"));// get null
1.1.4 利用spring-redis操作
上面jedis操作redis的例子很大略,除了利用jedis之外,还可以利用spring-redis。步骤如下
配置redis<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/><!-- redis template definition --><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnFactory"/>
编写代码
public class Example { // inject the actual template @Autowired private RedisTemplate<String, String> template; // inject the template as ListOperations // can also inject as Value, Set, ZSet, and HashOperations @Resource(name="redisTemplate") private ListOperations<String, String> listOps; public void addLink(String userId, URL url) { listOps.leftPush(userId, url.toExternalForm()); // or use template directly redisTemplate.boundListOps(userId).leftPush(url.toExternalForm()); }}
1.1.5 利用Lettuce操作redis
Lettuce是一个基于netty的 非壅塞的 redis客户端。支持Java8以及相应式。其官网为 https://lettuce.io/。Lettuce也可以和spring搭配利用。利用Lettuce须要加入如下maven依赖:
<dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>5.1.5.RELEASE</version></dependency>
基本的 get,set示例代码如下:
public class LettuceTest { public static void main(String[] args) { RedisURI uri = new RedisURI(); uri.setHost("myredishost"); uri.setPort(6379); uri.setDatabase(0); RedisClient redisClient = RedisClient.create(uri); StatefulRedisConnection<String, String> connection = redisClient.connect(); RedisCommands<String, String> syncCommands = connection.sync(); syncCommands.set("testKey", "Hello, Redis!"); System.out.println(syncCommands.get("testKey")); connection.close(); redisClient.shutdown(); }}
1.2 redis 主从
上面我们启动了一台redis,并对其进行操作。当然这只是实验性的玩玩。假设我们生产环境利用了一台redis,redis挂了怎么办?如果等到运维重启redis,并规复好数据,可能须要花费很永劫光。那么在这期间,我们的做事是不可用的,这该当是不能容忍的。假设我们做了主从,主库挂了之后,运维让从库接管,那么做事可以连续运行,正所谓防患未然。
redis主从配置非常大略,过程如下(ps 演示情形下主从配置在一台电脑上):
复制两个redis配置文件(启动两个redis,只须要一份redis程序,两个不同的redis配置文件即可)mkdir redis-master-slavecp path/to/redis/conf/redis.conf path/to/redis-master-slave master.confcp path/to/redis/conf/redis.conf path/to/redis-master-slave slave.conf
修正配置
## master.confport 6379## master.confport 6380slaveof 127.0.0.1 6379
分别启动两个redis
redis-server path/to/redis-master-slave/master.confredis-server path/to/redis-master-slave/slave.conf
启动之后,打开两个命令行窗口,分别实行telnet localhost 6379 telnet localhost 6380然后分别在两个窗口中实行 info 命令,可以看到
# Replicationrole:master# Replicationrole:slavemaster_host:127.0.0.1master_port:6379
主从配置没问题。
然后在master 窗口实行 set 之后,到slave窗口实行get,可以get到,解释主从同步成功。
这时,我们如果在slave窗口实行 set ,会报错:
-READONLY You can't write against a read only replica.
由于从节点是只读的。
1.3 哨兵sentinel上面我们先容了主从,从库作为一个“傀儡”,可以在须要的时候“顶上来”,”接盘“。我们配置的主从是为了”防患未然“,在主redis挂了之后,可以立马切换到从redis上,可能只须要花几分钟的韶光,但是仍旧是须要人为操作。假设主redis在晚上23点挂了,10分钟之后你接到电话,老板让你赶紧修复,于是你从被窝爬起来整,岂不是很头疼。如果你关机了,又其他人知道做事器密码,那系统岂不是要停机一晚上?太恐怖了。
这个时候redis sentinel 就派上用场了。sentinel 常日翻译成哨兵,便是放哨的,这里它便是用来监控主从节点的康健情形。客户端连接redis主从的时候,先连接 sentinel,sentinel会见告客户端主redis的地址是多少,然后客户端连接上redis并进行后续的操作。当主节点挂掉的时候,客户端就得不到连接了因而报错了,客户端重新想sentinel讯问主master的地址,然后客户端得到了[新选举出来的主redis],然后又可以愉快的操作了。
1.3.2 哨兵sentinel配置为相识释sentinel的用途,我们做个试验。配置3个redis(1主2从),1个哨兵。步骤如下:
mkdir redis-sentinelcd redis-sentinelcp redis/path/conf/redis.conf path/to/redis-sentinel/redis01.confcp redis/path/conf/redis.conf path/to/redis-sentinel/redis02.confcp redis/path/conf/redis.conf path/to/redis-sentinel/redis03.conftouch sentinel.conf
上我们创建了 3个redis配置文件,1个哨兵配置文件。我们将 redis01设置为master,将redis02,redis03设置为slave。
vim redis01.confport 63791vim redis02.confport 63792slaveof 127.0.0.1 63791vim redis03.confport 63793slaveof 127.0.0.1 63791vim sentinel.confdaemonize yesport 26379sentinel monitor mymaster 127.0.0.1 63793 1 # 下面阐明含义
上面的主从配置都熟习,只有哨兵配置 sentinel.conf,须要阐明一下:
mymaster 为主节点名字,可以随便取,后面程序里边连接的时候要用到127.0.0.1 63793 为主节点的 ip,port1 后面的数字 1 表示选举主节点的时候,投票数。1表示有一个sentinel赞许即可升级为master
1.3.3 启动哨兵,利用jedis连接哨兵操作redis
上面我们配置好了redis主从,1主2从,以及1个哨兵。下面我们分别启动redis,并启动哨兵
redis-server path/to/redis-sentinel/redis01.confredis-server path/to/redis-sentinel/redis02.confredis-server path/to/redis-sentinel/redis03.confredis-server path/to/redis-sentinel/sentinel.conf --sentinel
启动之后,可以分别连接到 3个redis上,实行info查看主从信息。
1.3.4 编写程序&运行下面利用程序来连接哨兵,并操作redis。
public static void main(String[] args) throws Exception{ Set<String> hosts = new HashSet<>(); hosts.add("127.0.0.1:26379"); //hosts.add("127.0.0.1:36379"); 配置多个哨兵 JedisSentinelPool pool = new JedisSentinelPool("mymaster",hosts); Jedis jedis = null; for(int i=0 ;i<20;i++){ Thread.sleep(2000); try{ jedis = pool.getResource(); String v = randomString(); jedis.set("hello",v); System.out.println(v+"-->"+jedis.get("hello").equals(v)); }catch (Exception e){ System.out.println(" [ exception happened]" + e); } } }
程序非常大略,循环运行20次,连接哨兵,将随机字符串 set到redis,get结果。打印信息,非常捕获。
1.3.5仿照主节点宕机情形运行上面的程序(把稳,在实验这个效果的时候,可以将sleep韶光加长或者for循环增多,以防程序提前停滞,不便看整体效果),然后将主redis关掉,仿照redis挂掉的情形。现在主redis为redis01,端口为63791
redis-cli -p 63791 shutdown
这个时候如果sentinel没有设置后台运行,可以在命令行窗口看到 master切换的情形日志。
# Sentinel ID is fd0634dc9876ec60da65db5ff1e50ebbeefdf5ce# +monitor master mymaster 127.0.0.1 63791 quorum 1 +slave slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63791 +slave slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791# +sdown master mymaster 127.0.0.1 63791# +odown master mymaster 127.0.0.1 63791 #quorum 1/1# +new-epoch 1# +try-failover master mymaster 127.0.0.1 63791# +vote-for-leader fd0634dc9876ec60da65db5ff1e50ebbeefdf5ce 1# +elected-leader master mymaster 127.0.0.1 63791# +failover-state-select-slave master mymaster 127.0.0.1 63791# +selected-slave slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791 +failover-state-send-slaveof-noone slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791 +failover-state-wait-promotion slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791# +promoted-slave slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791# +failover-state-reconf-slaves master mymaster 127.0.0.1 63791 +slave-reconf-sent slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63791 +slave-reconf-inprog slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63791 +slave-reconf-done slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63791# +failover-end master mymaster 127.0.0.1 63791# +switch-master mymaster 127.0.0.1 63791 127.0.0.1 63793 +slave slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63793 +slave slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 63793# +sdown slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 63793# -sdown slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 63793 +convert-to-slave slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 63793
上面的日志较多,仔细找找可以看到下面几行紧张的:
初始情形下,1主2从# +monitor master mymaster 127.0.0.1 63791 quorum 1 +slave slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 63791 +slave slave 127.0.0.1:63793 127.0.0.1 63793 @ mymaster 127.0.0.1 63791创造主挂了,准备 故障转移# +try-failover master mymaster 127.0.0.1 63791将主切换到了 63793 即redis03 # +switch-master mymaster 127.0.0.1 63791 127.0.0.1 63793
这个日志比较晦涩,从代码运行效果看,如下:
14:45:20.675 [main] INFO redis.clients.jedis.JedisSentinelPool - Trying to find master from available Sentinels...14:45:25.731 [main] DEBUG redis.clients.jedis.JedisSentinelPool - Connecting to Sentinel 192.168.1.106:2637914:45:25.770 [main] DEBUG redis.clients.jedis.JedisSentinelPool - Found Redis master at 127.0.0.1:6379214:45:25.771 [main] INFO redis.clients.jedis.JedisSentinelPool - Redis master running at 127.0.0.1:63792, starting Sentinel listeners...14:45:25.871 [main] INFO redis.clients.jedis.JedisSentinelPool - Created JedisPool to master at 127.0.0.1:63792ejahaeegig-->truedeeeadejjf-->true [ exception happened]redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ [ exception happened]........ 14:46:02.737 [MasterListener-mymaster-[192.168.1.106:26379]] DEBUG redis.clients.jedis.JedisSentinelPool - Sentinel 192.168.1.106:26379 published: mymaster 127.0.0.1 63792 127.0.0.1 63793.14:46:02.738 [MasterListener-mymaster-[192.168.1.106:26379]] INFO redis.clients.jedis.JedisSentinelPool - Created JedisPool to master at 127.0.0.1:63793haiihiihbb-->trueifgebdcicd-->trueaajhbjagag-->trueProcess finished with exit code 0
从结果看出
开始正常操作redis,并设置了两次。主redis挂了,jedis得不到连接,报错了JedisConnectionException:Could not get a resource from the pool主redis没选好之前,程序持续报错。主redis选好了,程序正常运行,末了结束。我们看到末了一次运行设置的值是aajhbjagag,我们可以连接剩下的2台redis中的任意一台,get hello,结果肯定是同等的。
1.4 redis cluster上面的章节中,我们分别学习了redis 单点,redis主从,并增加了高可用的 sentinel 哨兵模式。我们所做的这些事情只是担保了数据备份以及高可用,目前为止我们的程序一贯都是向1台redis写数据,其他的redis只是备份而已。实际场景中,单个redis节点可能不知足哀求,由于:
单个redis并发有限单个redis吸收所有的数据,终极回导致内存太大,内存太大回导致rdb文件过大,从很大的rdb文件中同步规复数据会很慢。所有,我们须要redis cluster 即redis集群。
Redis 集群是一个供应在多个Redis间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,由于这须要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情形下可能会导致不可预见的缺点.
Redis 集群通过分区来供应一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情形下连续处理命令. Redis 集群的上风:
自动分割数据到不同的节点上。全体集群的部分节点失落败或者不可达的情形下能够连续处理命令。为了配置一个redis cluster,我们须要准备至少6台redis,为啥至少6台呢?我们可以在redis的官方文档中找到如下一句话:
Note that the minimal cluster that works as expected requires to contain at least three master nodes.
由于最小的redis集群,须要至少3个主节点,既然有3个主节点,而一个主节点搭配至少一个从节点,因此至少得6台redis。然而对我来说,便是复制6个redis配置文件。本实验的redis集群搭建依然在一台电脑上仿照。
1.4.1 配置 redis cluster 集群上面提到,配置redis集群须要至少6个redis节点。因此我们须要准备及配置的节点如下:
主:redis01 从 redis02 slaveof redis01主:redis03 从 redis04 slaveof redis03主:redis05 从 redis06 slaveof redis05
mkdir redis-clustercd redis-clustermkdir redis01 到 redis06 6个文件夹cp redis.conf 到 redis01 ... redis06修正端口分别配置3组主从关系
1.4.2启动redis集群
上面的配置完成之后,分别启动6个redis实例。配置精确的情形下,都可以启动成功。然后运行如下命令创建集群:
redis-5.0.3/src/redis-cli --cluster create 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375 127.0.0.1:6376 --cluster-replicas 1
把稳,这里利用的是ip:port,而不是 domain:port ,由于我在利用 localhost:6371 之类的写法实行的时候碰到缺点:
ERR Invalid node address specified: localhost:6371
实行成功之后,连接一台redis,实行 cluster info 会看到类似如下信息:
cluster_state:okcluster_slots_assigned:16384cluster_slots_ok:16384cluster_slots_pfail:0cluster_slots_fail:0cluster_known_nodes:6cluster_size:3cluster_current_epoch:6cluster_my_epoch:1cluster_stats_messages_ping_sent:1515cluster_stats_messages_pong_sent:1506cluster_stats_messages_sent:3021cluster_stats_messages_ping_received:1501cluster_stats_messages_pong_received:1515cluster_stats_messages_meet_received:5cluster_stats_messages_received:3021
我们可以看到cluster_state:ok,cluster_slots_ok:16384,cluster_size:3。
1.4.3 利用jedis连接redis cluster 集群上面我们配置了一个redis集群,包含6个redis节点,3主3从。下面我们来利用jedis来连接redis集群。代码如下:
public static void main(String[] args) { Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); //Jedis Cluster will attempt to discover cluster nodes automatically jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6371)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6372)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6373)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6374)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6375)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 6376)); JedisCluster jc = new JedisCluster(jedisClusterNodes); jc.set("foo", "bar"); String value = jc.get("foo"); System.out.println(" ===> " + value); }
上面我们设置了信息set foo bar,但是不知道被设置到那一台redis上去了。请读者思考一下,我们是集群模式,以是数据被分散放到不同的槽中了,Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点卖力一部分hash槽,举个例子,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽.节点 B 包含5501 到 11000 号哈希槽.节点 C 包含11001 到 16384号哈希槽.看到这里你该当还是不知道set foo bar 放到哪台redis上去了,不妨考试测验连接任意一台redis探索一下,你会知道的。
总结至此,我们理解并动手实践了redis的安装,redis单点,redis主从,redis 哨兵 sentinel,redis 集群cluster。我们来梳理一下redis主从,redis哨兵,redis机器的差异和关系。
redis主从:是备份关系, 我们操作主库,数据也会同步到从库。 如果主库机器坏了,从库可以上。就好比你 D盘的片丢了,但是你移动硬盘里边备份有。redis哨兵:哨兵担保的是HA,担保分外情形故障自动切换,哨兵盯着你的“redis主从集群”,如果主库去世了,它会见告你新的老大是谁。redis集群:集群担保的是高并发,由于多了一些兄弟帮忙一起扛。同时集群会导致数据的分散,全体redis集群会分成一堆数据槽,即不同的key会放到不不同的槽中。
主从担保了数据备份,哨兵担保了HA 即故障时切换,集群担保了高并发性。
统统动手做了才会熟习。