redis单点、redis主从、redis哨兵sentinel,redis集群cluster配置搭建与使用

前端之家收集整理的这篇文章主要介绍了redis单点、redis主从、redis哨兵sentinel,redis集群cluster配置搭建与使用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

redis单点、redis主从、redis哨兵 sentinel,redis集群cluster配置搭建与使用

redis是如今被互联网公司使用最广泛的一个中间件,我们打开GitHub搜索redis,边可以看到,该项目的介绍是这样的:

@H_301_59@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

  1. 下载一个最新版本的安装包,如 redis-version.tar.gz
  2. 解压 @H_301_59@tar zxvf redis-version.tar.gz
  3. 执行 make (执行此命令可能会报错,例如确实gcc,一个个解决即可)

如果是 mac 电脑,安装redis将十分简单执行@H_301_59@brew install redis即可。

安装好redis之后,我们先不慌使用,先进行一些配置。打开@H_301_59@redis.conf文件,我们主要关注以下配置:

@H_301_59@port 6379             # 指定端口为 6379,也可自行修改 
daemonize yes         # 指定后台运行

1.1 redis 单点

安装好redis之后,我们来运行一下。启动redis的命令为 :

@H_301_59@redishome/bin/redis-server path/to/redis.config

假设我们没有配置后台运行(即:daemonize no),那么我们会看到如下启动日志:

@H_301_59@93825:C 20 Jan 2019 11:43:22.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
93825:C 20 Jan 2019 11:43:22.640 # Redis version=5.0.3,bits=64,commit=00000000,modified=0,pid=93825,just started
93825:C 20 Jan 2019 11:43:22.640 # Configuration loaded
93825: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

使用命令:@H_301_59@telnet localhost 6379 来连接redis,或者你可以直接使用代码来连接测试。连接之后,看到如下信息:

@H_301_59@Connected to localhost.
Escape character is '^]'.

我们输入几个命令试试:

@H_301_59@set hello world     设置key-value
get hello           获取key值
expire hello 10     设置10秒过期
ttl hello           查看过期时间
del hello           删除key

如此,我们便体验了一把redis,可以说是非常简单了。刚才我们是使用命令行来操作redis的,下面我们来使用代码操作一下redis,以@H_301_59@Java为例,我们使用一个开源的 java - redis客户端。

1.1.3 使用jedis客户端操作redis

打开GitHub,搜索redis,进入到项目主页之后,我们可以看到使用方法

  1. 加入jedis依赖

    @H_301_59@<dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.0.0</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    
  2. 编写代码如下

    Jedis jedis = new Jedis("localhost",6379);
    
    jedis.set("hello","world");
    
    String value = jedis.get("hello");
    
    System.out.println(value); // get world
    
    jedis.del("hello");
    
    System.out.println(jedis.get("hello"));// get null
    

1.1.4 使用spring-redis操作

上面jedis操作redis的例子很简单,除了使用jedis之外,还可以使用spring-redis。步骤如下

  1. 配置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"/>
    
  2. 编写代码

    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 演示情况下主从配置在一台电脑上):

  1. 复制两个redis配置文件(启动两个redis,只需要一份redis程序,两个不同的redis配置文件即可)
@H_301_59@mkdir redis-master-slave
cp path/to/redis/conf/redis.conf path/to/redis-master-slave master.conf
cp path/to/redis/conf/redis.conf path/to/redis-master-slave slave.conf
  1. 修改配置
@H_301_59@## master.conf
port 6379

## master.conf
port 6380
slaveof 127.0.0.1 6379
  1. 分别启动两个redis
@H_301_59@redis-server path/to/redis-master-slave/master.conf
redis-server path/to/redis-master-slave/slave.conf

启动之后,打开两个命令行窗口,分别执行telnet localhost 6379 telnet localhost 6380

然后分别在两个窗口中执行 @H_301_59@info 命令,可以看到

@H_301_59@# Replication
role:master

# Replication
role:slave
master_host:127.0.0.1
master_port:6379

主从配置没问题。

然后在master 窗口执行 set 之后,到slave窗口执行get,可以get到,说明主从同步成功。

这时,我们如果在slave窗口执行 set ,会报错:

@H_301_59@-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个哨兵。步骤如下:

@H_301_59@mkdir redis-sentinel
cd redis-sentinel
cp redis/path/conf/redis.conf path/to/redis-sentinel/redis01.conf
cp redis/path/conf/redis.conf path/to/redis-sentinel/redis02.conf
cp redis/path/conf/redis.conf path/to/redis-sentinel/redis03.conf
touch sentinel.conf

上我们创建了 3个redis配置文件,1个哨兵配置文件。我们将 redis01设置为master,将redis02,redis03设置为slave。

@H_301_59@vim redis01.conf
port 63791

vim redis02.conf
port 63792
slaveof 127.0.0.1 63791

vim redis03.conf
port 63793
slaveof 127.0.0.1 63791

vim sentinel.conf
daemonize yes
port 26379
sentinel monitor mymaster 127.0.0.1 63793 1   # 下面解释含义

上面的主从配置都熟悉,只有哨兵配置 sentinel.conf,需要解释一下:

@H_301_59@mymaster        为主节点名字,可以随便取,后面程序里边连接的时候要用到
127.0.0.1 63793 为主节点的 ip,port
1               后面的数字 1 表示选举主节点的时候,投票数。1表示有一个sentinel同意即可升级为master

1.3.3 启动哨兵,使用jedis连接哨兵操作redis

上面我们配置好了redis主从,1主2从,以及1个哨兵。下面我们分别启动redis,并启动哨兵

@H_301_59@redis-server path/to/redis-sentinel/redis01.conf
redis-server path/to/redis-sentinel/redis02.conf
redis-server path/to/redis-sentinel/redis03.conf

redis-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

@H_301_59@redis-cli -p 63791 shutdown

这个时候如果sentinel没有设置后台运行,可以在命令行窗口看到 master切换的情况日志。

@H_301_59@# 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

上面的日志较多,仔细找找可以看到下面几行主要的:

@H_301_59@初始情况下,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

这个日志比较晦涩,从代码运行效果看,如下:

@H_301_59@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:26379
14:45:25.770 [main] DEBUG redis.clients.jedis.JedisSentinelPool - Found Redis master at 127.0.0.1:63792
14: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:63792
ejahaeegig-->true
deeeadejjf-->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:63793
haiihiihbb-->true
ifgebdcicd-->true
aajhbjagag-->true

Process finished with exit code 0

从结果看出

  1. 开始正常操作redis,并设置了两次。
  2. 主redis挂了,jedis得不到连接,报错了JedisConnectionException:Could not get a resource from the pool
  3. 主redis没选好之前,程序持续报错。
  4. 主redis选好了,程序正常运行,最后结束。

我们看到最后一次运行设置的值是@H_301_59@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的官方文档中找到如下一句话:

@H_301_59@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节点。因此我们需要准备及配置的节点如下:

@H_301_59@主:redis01  从 redis02    slaveof redis01
主:redis03  从 redis04    slaveof redis03
主:redis05  从 redis06    slaveof redis05
@H_301_59@mkdir redis-cluster
cd redis-cluster
mkdir 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 之类的写法执行的时候碰到错误

@H_301_59@ERR Invalid node address specified: localhost:6371

执行成功之后,连接一台redis,执行 cluster info 会看到类似如下信息:

@H_301_59@cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:1515
cluster_stats_messages_pong_sent:1506
cluster_stats_messages_sent:3021
cluster_stats_messages_ping_received:1501
cluster_stats_messages_pong_received:1515
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:3021

我们可以看到@H_301_59@cluster_state:ok,@H_301_59@cluster_slots_ok:16384,@H_301_59@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);
    }

上面我们设置了信息@H_301_59@set foo bar,但是不知道被设置到那一台redis上去了。请读者思考一下,我们是集群模式,所以数据被分散放到不同的槽中了,Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:

  • 节点 A 包含 0 到 5500号哈希槽.
  • 节点 B 包含5501 到 11000 号哈希槽.
  • 节点 C 包含11001 到 16384号哈希槽.

看到这里你应该还是不知道@H_301_59@set foo bar 放到哪台redis上去了,不妨尝试连接任意一台redis探索一下,你会知道的。

总结

至此,我们了解并动手实践了redis的安装,redis单点,redis主从,redis 哨兵 sentinel,redis 集群cluster。
我们来梳理一下redis主从,redis哨兵,redis机器的区别和关系。

redis主从:是备份关系, 我们操作主库,数据也会同步到从库。 如果主库机器坏了,从库可以上。就好比你 D盘的片丢了,但是你移动硬盘里边备份有。
redis哨兵:哨兵保证的是HA,保证特殊情况故障自动切换,哨兵盯着你的“redis主从集群”,如果主库死了,它会告诉你新的老大是谁。
redis集群:集群保证的是高并发,因为多了一些兄弟帮忙一起扛。同时集群会导致数据的分散,整个redis集群会分成一堆数据槽,即不同的key会放到不不同的槽中。

主从保证了数据备份,哨兵保证了HA 即故障时切换,集群保证了高并发性。

一切动手做了才会熟悉。

Lua脚本在redis分布式锁场景的运用

Netty开发redis客户端,Netty发送redis命令,netty解析redis消息
Docker & k8s 系列一:快速上手docker
Docker & k8s 系列二:本机k8s环境搭建
Docker & k8s 系列三:在k8s中部署单个服务实例
Docker & Kubenetes 系列四:集群,扩容,升级,回滚

alt 逃离沙漠公众号

猜你在找的Redis相关文章