本文共 16101 字,大约阅读时间需要 53 分钟。
点关注,不迷路!如果本文对你有帮助的话不要忘记点赞支持哦!
上述面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2020收集的一些大厂的面试真题(都整理成文档,小部分截图),有需要的可以 。
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的单点,高可用,集群。redis的安装十分简单,打开redis的官网 。
tar zxvf redis-version.tar.gz
如果是 mac 电脑,安装redis将十分简单执行brew install redis
即可。
安装好redis之后,我们先不慌使用,先进行一些配置。打开redis.conf
文件,我们主要关注以下配置:
port 6379 # 指定端口为 6379,也可自行修改 daemonize yes # 指定后台运行
安装好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 `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
无论是否配置了后台运行,启动成功之后,我们可以新开一个命令行窗口来操作试试。
使用命令: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客户端。
打开GitHub,搜索redis,进入到项目主页之后,我们可以看到使用方法:
redis.clients jedis 3.0.0 jar compile
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
上面jedis操作redis的例子很简单,除了使用jedis之外,还可以使用spring-redis。步骤如下
public class Example { // inject the actual template @Autowired private RedisTemplatetemplate; // inject the template as ListOperations // can also inject as Value, Set, ZSet, and HashOperations @Resource(name="redisTemplate") private ListOperations listOps; public void addLink(String userId, URL url) { listOps.leftPush(userId, url.toExternalForm()); // or use template directly redisTemplate.boundListOps(userId).leftPush(url.toExternalForm()); }}
Lettuce是一个基于netty的 非阻塞的 redis客户端。支持Java8以及响应式。其官网为 。Lettuce也可以和spring搭配使用。
使用Lettuce需要加入如下maven依赖:
io.lettuce lettuce-core 5.1.5.RELEASE
基本的 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); StatefulRedisConnectionconnection = redisClient.connect(); RedisCommands syncCommands = connection.sync(); syncCommands.set("testKey", "Hello, Redis!"); System.out.println(syncCommands.get("testKey")); connection.close(); redisClient.shutdown(); }}
上面我们启动了一台redis,并对其进行操作。当然这只是实验性的玩玩。假设我们生产环境使用了一台redis,redis挂了怎么办?如果等到运维重启redis,并恢复好数据,可能需要花费很长时间。那么在这期间,我们的服务是不可用的,这应该是不能容忍的。假设我们做了主从,主库挂了之后,运维让从库接管,那么服务可以继续运行,正所谓有备无患。
redis主从配置非常简单,过程如下(ps 演示情况下主从配置在一台电脑上):
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-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.
因为从节点是只读的。
上面我们介绍了主从,从库作为一个“傀儡”,可以在需要的时候“顶上来”,”接盘“。我们配置的主从是为了”有备无患“,在主redis挂了之后,可以立马切换到从redis上,可能只需要花几分钟的时间,但是仍然是需要人为操作。假设主redis在晚上23点挂了,10分钟之后你接到电话,老板让你赶紧修复,于是你从被窝爬起来整,岂不是很头疼。假如你关机了,又其他人知道服务器密码,那系统岂不是要停机一晚上?太可怕了。
这个时候redis sentinel 就派上用场了。sentinel 通常翻译成哨兵,就是放哨的,这里它就是用来监控主从节点的健康情况。客户端连接redis主从的时候,先连接 sentinel,sentinel会告诉客户端主redis的地址是多少,然后客户端连接上redis并进行后续的操作。当主节点挂掉的时候,客户端就得不到连接了因而报错了,客户端重新想sentinel询问主master的地址,然后客户端得到了[新选举出来的主redis],然后又可以愉快的操作了。
为了说明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
上面我们配置好了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查看主从信息。
下面使用程序来连接哨兵,并操作redis。
public static void main(String[] args) throws Exception{ Sethosts = 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结果。打印信息,异常捕获。
运行上面的程序(注意,在实验这个效果的时候,可以将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,结果肯定是一致的。上面的章节中,我们分别学习了redis 单点,redis主从,并增加了高可用的 sentinel 哨兵模式。我们所做的这些工作只是保证了数据备份以及高可用,目前为止我们的程序一直都是向1台redis写数据,其他的redis只是备份而已。实际场景中,单个redis节点可能不满足要求,因为:
所有,我们需要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集群搭建依然在一台电脑上模拟。
上面提到,配置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组主从关系
上面的配置完成之后,分别启动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
。
上面我们配置了一个redis集群,包含6个redis节点,3主3从。下面我们来使用jedis来连接redis集群。代码如下:
public static void main(String[] args) { SetjedisClusterNodes = new HashSet (); //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个节点,那么:
看到这里你应该还是不知道set foo bar
放到哪台redis上去了,不妨尝试连接任意一台redis探索一下,你会知道的。
至此,我们了解并动手实践了redis的安装,redis单点,redis主从,redis 哨兵 sentinel,redis 集群cluster。
我们来梳理一下redis主从,redis哨兵,redis机器的区别和关系。redis主从:是备份关系, 我们操作主库,数据也会同步到从库。 如果主库机器坏了,从库可以上。就好比你 D盘的片丢了,但是你移动硬盘里边备份有。
redis哨兵:哨兵保证的是HA,保证特殊情况故障自动切换,哨兵盯着你的“redis主从集群”,如果主库死了,它会告诉你新的老大是谁。 redis集群:集群保证的是高并发,因为多了一些兄弟帮忙一起扛。同时集群会导致数据的分散,整个redis集群会分成一堆数据槽,即不同的key会放到不不同的槽中。到此这篇关于文章就结束了!
点关注,不迷路!如果本文对你有帮助的话不要忘记点赞支持哦!
上述面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2020收集的一些大厂的面试真题(都整理成文档,小部分截图),有需要的可以 。
希望对大家有所帮助,有用的话点赞给我支持!
转载地址:http://etfvi.baihongyu.com/