redis技术知识大全

瘦欲@ 提交于 2020-02-27 03:33:21

1.前言

        本文主要介绍redis的安装、基础知识、内存淘汰策略、缓存持久化、集群搭建和使用等知识点

2.redis的安装

    1.安装gcc:
    redis是c语言开发,安装redis需要先将官网下载的源码进行编译,编译依赖gcc环境。
    [root@aliyun package]# yum install gcc-c++
    2.安装redis.
    下载redis压缩文件,上传至centos7,并解压
    [root@aliyun package]# tar -zxvf redis-5.0.0.tar.gz
    3.进入到解压的文件夹进行编译(需要依赖我们第一步安装的gcc环境,不然会报错)
    进入后,输入make 进行编译
    [root@aliyun package]# make 
    编译完成后我们进行指定位置安装:
    [root@aliyun package]#  make PREFIX=/usr/local/redis install
    (这里安装,我们指定的安装目录是:/usr/local/redis。会看到我们目录下会生成一个redis文件夹)
    4,进入到之前解压的文件夹里,找到redis.conf这个文件,复制到安装的目录下: 
    [root@aliyun package]# cp redis.conf /usr/local/redis
    到这里,我们的redis安装结束,可以正常使用。
    后面还需要做一些针对开发的调整。
    5,启动redis
    进入bin目录,进行redis的启动
    [root@aliyun package]# ./redis-server ../redis.conf
    我们需要使用:control+c停止
    6,后端启动方式:
    修改配置文件:redis.conf
    daemonize yes
    将no改成yes
    7,修改配置文件后,后端启动
    进入bin目录,重新启动,需要加上redis.conf文件:
    [root@aliyun package]# ./redis-server ../redis.conf
    这样就可以进行后端启动了。
    如何检查后端启动是否成功 :
    [root@aliyun package]# ps -ef | grep -i redis
    我们能正常查看到redis的端口即可。
    8,停止redis的方法
    [root@aliyun package]# kill -9  6121
    [root@aliyun package]# ./redis-cli shutdown
    9,进行使用
    执行客户端操作:
    [root@aliyun package]# ./redis-cli
    后面就可以正常存值和取值了

3.redis基础知识

 2.1 缓存中间件 Memcache 和 Redis 的区别

   1. Memcache 的代码层类似 Hash,特点如下:
    支持简单数据类型
    不支持数据持久化存储
    不支持主从
    不支持分片
    2. Redis 特点如下:
    数据类型丰富
    支持数据磁盘持久化存储
    支持主从
    支持分片
    3. 为什么 Redis 能这么快
    Redis 的效率很高,官方给出的数据是 100000+QPS,这是因为:
    1)Redis 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。
    2)Redis 使用单进程单线程模型的(K,V)数据库,将数据存储在内存中,存取均不会受到硬盘 IO 的限制,因此其执行速度极快。
    另外单线程也能处理高并发请求,还可以避免频繁上下文切换和锁的竞争,如果想要多核运行也可以启动多个实例。
    3)数据结构简单,对数据操作也简单,Redis不使用表,不会强制用户对各个关系进行关联,不会有复杂的关系限制,其存储结构就是键值对,类似于 HashMap,HashMap 最大的优点就是存取的时间复杂度为 O(1)。
    4)Redis 使用多路 I/O 复用模型,为非阻塞 IO。

  2.2 Redis 的数据类型

   1. String
    string是redis最基本的类型,一个key对应一个value。value不仅是string,也可以是数字。string类型是二进制安全的,意思是redis的string类型可以包含任何数据,比如jpg图片或者序列化的对象。string类型的值最大能存储512M。如下图:

     
    2. Hash
    redis的hash是一个string的key和value的映射表,Hash特别适合存储对象。常用命令:hget,hset,hgetall等。

      
    3. List
    可以添加一个元素到列表的头部(左边)或者尾部(右边) 
    常用命令:lpush、rpush、lpop、rpop、lrange(获取列表片段)等。
    应用场景:list应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表都可以用list结构来实现。
    数据结构:list就是链表,可以用来当消息队列用。redis提供了List的push和pop操作,还提供了操作某一段的api,可以直接查询或者删除某一段的元素。
    实现方式:redis list的是实现是一个双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了额外的内存开销。

      
    4. Set
    String 元素组成的无序集合,通过哈希表实现(增删改查时间复杂度为 O(1)),不允许重复。
    另外,当我们使用 Smembers 遍历 Set 中的元素时,其顺序也是不确定的,是通过 Hash 运算过后的结果。
    Redis 还对集合提供了求交集、并集、差集等操作,可以实现如同共同关注,共同好友等功能。
    常用命令:sadd、spop、smembers、sunion等。

    
    5. zset
    set一样是string类型元素的集合,且不允许重复的元素。
    常用命令:zadd、zrange、zrem、zcard等。
    使用场景:sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set结构。和set相比,sorted set关联了一个double类型权重的参数score,使得集合中的元素能够按照score进行有序排列,redis正是通过分数来为集合中的成员进行从小到大的排序。
    实现方式:Redis sorted set的内部使用HashMap和跳跃表(skipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

    

    数据类型应用场景总结

     

3 缓存穿透,缓存击穿,缓存雪崩解决方案分析

3.1 缓存穿透

       缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
        解决方案
        有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

3.2 缓存雪崩

        缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
        解决方案
        缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效。或者设置热点数据永不过期,有更新操作就更新缓存就好了.

3.3 缓存击穿

        对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
        缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
    解决方案
        1. 缓存设置成永不过期,在更新或删除 DB 中的数据时,也主动地把缓存中的数据更新或删除掉。
        2.缓存依然保持设置过期时间,每次get缓存的时候,都和数据的过期时间和当前时间进行一下对比,当间隔时间小于一个阈值的时候,主动更新缓存。
        3.在缓存失效后,通过互斥锁或者队列,控制读数据库和写缓存的线程数量。
            public static String getData(String key) throws InterruptedException {
                //从Redis查询数据
                String result = getDataFromRedis(key);
                //参数校验
                if (StringUtils.isBlank(result)) {
                    try {
                        //获得锁
                        if (reenLock.tryLock()) {
                            //去数据库查询
                            result = getDataFromMysql(key);
                            //校验
                            if (StringUtils.isNotBlank(result)) {
                                //插进缓存
                                setDataToRedis(key, result);
                            }
                        } else {
                            //睡一会再拿
                            Thread.sleep(1000L);
                            result = getData(key);
                        }
                    } finally {
                        //释放锁
                        reenLock.unlock();
                    }
                }
                return result;
            }
        4.设置一级缓存和二级缓存,一级缓存过期时间短,二级缓存过期时间长或者不过期,一级缓存失效后访问二级缓存,同时刷新一级缓存和二级缓存。

  4 redis内存淘汰策略

    Redis是基于内存的key-value数据库,因为系统的内存大小有限,所以我们在使用Redis的时候可以配置Redis能使用的最大的内存大小。

4.1 通过配置文件配置

     通过在Redis安装目录下面的redis.conf配置文件中添加以下配置设置内存大小
            //设置Redis最大占用内存大小为500M
            maxmemory 500mb

4.2 Redis的内存淘汰

        既然可以设置Redis最大占用内存大小,那么配置的内存就有用完的时候。那在内存用完的时候,还继续往Redis里面添加数据不就没内存可用了吗?
        实际上Redis定义了几种策略用来处理这种情况:
        noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
        allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。
        allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。
        volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。
        volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。
        volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。

4.3 LRU算法

        上面说到了Redis可使用最大内存使用完了,是可以使用LRU算法进行内存淘汰的,那么什么是LRU算法呢?
        LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法。在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU算法了。其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。
        使用java实现一个简单的LRU算法
        public class LRUCache<k, v> {
            //容量
            private int capacity;
            //当前有多少节点的统计
            private int count;
            //缓存节点
            private Map<k, Node<k, v>> nodeMap;
            private Node<k, v> head;
            private Node<k, v> tail;
            public LRUCache(int capacity) {
                if (capacity < 1) {
                    throw new IllegalArgumentException(String.valueOf(capacity));
                }
                this.capacity = capacity;
                this.nodeMap = new HashMap<>();
                //初始化头节点和尾节点,利用哨兵模式减少判断头结点和尾节点为空的代码
                Node headNode = new Node(null, null);
                Node tailNode = new Node(null, null);
                headNode.next = tailNode;
                tailNode.pre = headNode;
                this.head = headNode;
                this.tail = tailNode;
            }

            public void put(k key, v value) {
                Node<k, v> node = nodeMap.get(key);
                if (node == null) {
                    if (count >= capacity) {
                        //先移除一个节点
                        removeNode();
                    }
                    node = new Node<>(key, value);
                    //添加节点
                    addNode(node);
                } else {
                    //移动节点到头节点
                    moveNodeToHead(node);
                }
            }

            public Node<k, v> get(k key) {
                Node<k, v> node = nodeMap.get(key);
                if (node != null) {
                    moveNodeToHead(node);
                }
                return node;
            }

            private void removeNode() {
                Node node = tail.pre;
                //从链表里面移除
                removeFromList(node);
                nodeMap.remove(node.key);
                count--;
            }

            private void removeFromList(Node<k, v> node) {
                Node pre = node.pre;
                Node next = node.next;

                pre.next = next;
                next.pre = pre;
                node.next = null;
                node.pre = null;
            }

            private void addNode(Node<k, v> node) {
                //添加节点到头部
                addToHead(node);
                nodeMap.put(node.key, node);
                count++;
            }

            private void addToHead(Node<k, v> node) {
                Node next = head.next;
                next.pre = node;
                node.next = next;
                node.pre = head;
                head.next = node;
            }

            public void moveNodeToHead(Node<k, v> node) {
                //从链表里面移除
                removeFromList(node);
                //添加节点到头部
                addToHead(node);
            }

            class Node<k, v> {
                k key;
                v value;
                Node pre;
                Node next;
                public Node(k key, v value) {
                    this.key = key;
                    this.value = value;
                }
            }
        }

4.4 LRU在Redis中的实现

    1) 近似LRU算法
        Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key。
            可以通过maxmemory-samples参数修改采样数量:例:maxmemory-samples 10 maxmenory-samples配置的越大,淘汰的结果越接近于严格的LRU算法
        Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。
    2)Redis3.0对近似LRU的优化
        Redis3.0对近似LRU算法进行了一些优化。新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。
        当需要淘汰的时候,则直接从池中选取最近访问时间最小(最久没被访问)的key淘汰掉就行。
    3)LRU算法的对比
    我们可以通过一个实验对比各LRU算法的准确率,先往Redis里面添加一定数量的数据n,使Redis可用内存用完,再往Redis里面添加n/2的新数据,这个时候就需要淘汰掉一部分的数据,如果按照严格的LRU算法,应该淘汰掉的是最先加入的n/2的数据。

4.5 LFU算法

        LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。
        LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。
        LFU一共有两种策略:
            volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key
            allkeys-lfu:在所有的key中使用LFU算法淘汰数据
            设置使用这两种淘汰策略跟前面讲的一样,不过要注意的一点是这两周策略只能在Redis4.0及以上设置,如果在Redis4.0以下设置会报错

  5 redis持久化

    5.1 什么是持久化

        持久化,即将数据持久存储,而不因断电或其他各种复杂外部环境影响数据的完整性。
        由于 Redis 将数据存储在内存而不是磁盘中,所以内存一旦断电,Redis 中存储的数据也随即消失,这往往是用户不期望的,所以 Redis 有持久化机制来保证数据的安全性。

    5.2 Redis 如何做持久化

     1) RDB(快照)持久化
        RDB(快照)形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。
        RDB 配置文件,redis.conf:
          save 900 1 #在900s内如果有1条数据被写入,则产生一次快照。
          save 300 10 #在300s内如果有10条数据被写入,则产生一次快照
          save 60 10000 #在60s内如果有10000条数据被写入,则产生一次快照
          stop-writes-on-bgsave-error yes 
          #stop-writes-on-bgsave-error :
          如果为yes则表示,当备份进程出错的时候,
          主进程就停止进行接受新的写入操作,这样是为了保护持久化的数据一致性的问题。
    2) RDB 的创建与载入
        SAVE:阻塞 Redis 的服务器进程,直到 RDB 文件被创建完毕。SAVE 命令很少被使用,因为其会阻塞主线程来保证快照的写入,由于 Redis 是使用一个主线程来接收所有客户端请求,这样会阻塞所有客户端请求。
        BGSAVE:该指令会 Fork 出一个子进程来创建 RDB 文件,不阻塞服务器进程,子进程接收请求并创建 RDB 快照,父进程继续接收客户端的请求。子进程在完成文件的创建时会向父进程发送信号,父进程在接收客户端请求的过程中,在一定的时间间隔通过轮询来接收子进程的信号。
    3) 自动化触发RDB持久化的方式如下:
        根据 redis.conf 配置里的 SAVE m n 定时触发(实际上使用的是 BGSAVE)。
        主从复制时,主节点自动触发。
        执行 Debug Reload。
        执行 Shutdown 且没有开启 AOF 持久化。
    4) RDB 持久化方式的缺点
        RDB 持久化方式的缺点如下:
        内存数据全量同步,数据量大的状况下,会由于 I/O 而严重影响性能。
        可能会因为 Redis 宕机而丢失从当前至最近一次快照期间的数据。
    5) AOF 持久化
        AOF 持久化是通过保存 Redis 的写状态来记录数据库的。
        相对 RDB 来说,RDB 持久化是通过备份数据库的状态来记录数据库,而 AOF 持久化是备份数据库接收到的指令:
        把所有的对Redis的服务器进行修改的命令都存到一个文件里,以增量的形式追加保存到 AOF 文件中。
    6) 开启 AOF 持久化
       打开 redis.conf 配置文件,将 appendonly 属性改为 yes。
       修改 appendfsync 属性,该属性可以接收三种参数,分别是 always,everysec,no。
        always 表示总是即时将缓冲区内容写入 AOF 文件当中,everysec 表示每隔一秒将缓冲区内容写入 AOF 文件,no 表示将写入文件操作交由操作系统决定。
        一般来说,操作系统考虑效率问题,会等待缓冲区被填满再将缓冲区数据写入 AOF 文件中。
          appendonly yes
          #appendsync always
          appendfsync everysec
          # appendfsync no
        日志重写解决 AOF 文件不断增大
        随着写操作的不断增加,AOF 文件会越来越大。假设递增一个计数器 100 次,如果使用 RDB 持久化方式,我们只要保存最终结果 100 即可。 而 AOF 持久化方式需要记录下这100次递增操作的指令,而事实上要恢复这条记录,只需要执行一条命令就行,所以那一百条命令实际可以精简为一条。
        Redis 支持这样的功能,在不中断前台服务的情况下,可以重写 AOF 文件,同样使用到了 COW(写时拷贝)。
        重写过程如下:
        调用 fork(),创建一个子进程。
        子进程把新的 AOF 写到一个临时文件里,不依赖原来的 AOF 文件。
        主进程持续将新的变动同时写到内存和原来的 AOF 里。
        主进程获取子进程重写 AOF 的完成信号,往新 AOF 同步增量变动。
        使用新的 AOF 文件替换掉旧的 AOF 文件。
    7) AOF 和 RDB 的优缺点如下:
        RDB 优点:全量数据快照,文件小,恢复快。
        RDB 缺点:无法保存最近一次快照之后的数据。
        AOF 优点:可读性高,适合保存增量数据,数据不易丢失。
        AOF 缺点:文件体积大,恢复时间长。
    8) RDB-AOF 混合持久化方式
        Redis 4.0 之后推出了此种持久化方式,RDB 作为全量备份,AOF 作为增量备份,并且将此种方式作为默认方式使用。
        在上述两种方式中,RDB 方式是将全量数据写入 RDB 文件,这样写入的特点是文件小,恢复快,但无法保存最近一次快照之后的数据,AOF 则将 Redis 指令存入文件中,这样又会造成文件体积大,恢复时间长等弱点。
        在 RDB-AOF 方式下,持久化策略首先将缓存中数据以 RDB 方式全量写入文件,再将写入后新增的数据以 AOF 的方式追加在 RDB 数据的后面,在下一次做 RDB 持久化的时候将 AOF 的数据重新以 RDB 的形式写入文件。
        这种方式既可以提高读写和恢复效率,也可以减少文件大小,同时可以保证数据的完整性。
        在此种策略的持久化过程中,子进程会通过管道从父进程读取增量数据,在以 RDB 格式保存全量数据时,也会通过管道读取数据,同时不会造成管道阻塞。
        可以说,在此种方式下的持久化文件,前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。此种方式是目前较为推荐的一种持久化方式。

6 RedisCluster会设计成16384个槽

       Redis Cluster 是Redis的集群实现,内置数据自动分片机制,集群内部将所有的key映射到16384个Slot中。为什么RedisCluster会设计成16384个槽呢?
        1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
        当槽位为65536时,这块的大小是: 65536÷8=8kb因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。 
        2.redis的集群主节点数量基本不可能超过1000个。
        集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。 
        3.槽位越小,节点少的情况下,压缩率高。
        Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。而16384÷8÷1024=2kb。

7.redis集群三种方式

    7.1 主从模式

       Redis 一般是使用一个 Master节点来进行写操作,而若干个Slave节点进行读操作,Master和Slave分别代表了一个个不同的另外定期的数据备份操作也是单独选择一个 Slave 去完成,这样可以最大程度发挥 Redis 的性能,为的是保证数据的弱一致性和最终一致性。
        另外,Master 和 Slave 的数据不是一定要即时同步的,但是在一段时间后 Master 和 Slave 的数据是趋于同步的,这就是最终一致性。
    1)全同步过程如下:
        Slave 发送 Sync 命令到 Master。
        Master 启动一个后台进程,将 Redis 中的数据快照保存到文件中。
        Master 将保存数据快照期间接收到的写命令缓存起来。
        Master 完成写文件操作后,将该文件发送给 Slave。
        使用新的 AOF 文件替换掉旧的 AOF 文件。
        Master 将这期间收集的增量写命令发送给 Slave 端。
    2)增量同步过程如下:
        Master 接收到用户的操作指令,判断是否需要传播到 Slave。
        将操作记录追加到 AOF 文件。
        将操作传播到其他 Slave:对齐主从库;往响应缓存写入指令。
        将缓存中的数据发送给 Slave。
    3)主从复制存在问题:
        1. 一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。
        2. 主节点的写能力受到单机的限制。
        3. 主节点的存储能力受到单机的限制。
        4. 原生复制的弊端在早期的版本中也会比较突出,比如:redis复制中断后,从节点会发起psync。此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或秒级的卡顿。

7.2 Redis Sentinel(哨兵)

    主从模式弊端:当 Master 宕机后,Redis 集群将不能对外提供写入操作。Redis Sentinel 可解决这一问题。
    1)解决主从同步 Master 宕机后的主从切换问题:
        监控:不断检查主服务器和从服务器是否正常运行。
        通知:当被监控的某个redis服务器出现问题,Sentinel通过API脚本向管理员或者其他应用程序发出通知。
        自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点,这样人工干预就可以免了。
        配置提供者:在Redis Sentinel模式下,客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点的信息。
    2)哨兵的工作原理
        1. 每个Sentinel节点都需要定期执行以下任务:每个Sentinel以每秒一次的频率,向它所知的主服务器、从服务器以及其他的Sentinel实例发送一个PING命令
        2. 如果一个实例距离最后一次有效回复PING命令的时间超过down-after-milliseconds所指定的值,那么这个实例会被Sentinel标记为主观下线。
        3. 如果一个主服务器被标记为主观下线,那么正在监视这个服务器的所有Sentinel节点,要以每秒一次的频率确认主服务器的确进入了主观下线状态。
        4. 如果一个主服务器被标记为主观下线,并且有足够数量的Sentinel(至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断,那么这个主服务器被标记为客观下线。
        5. 一般情况下,每个Sentinel会以每10秒一次的频率向它已知的所有主服务器和从服务器发送INFO命令,当一个主服务器被标记为客观下线时,Sentinel向下线主服务器的所有从服务器发送INFO命令的频率,会从10秒一次改为每秒一次。
        6. Sentinel和其他Sentinel协商客观下线的主节点的状态,如果处于SDOWN状态,则投票自动选出新的主节点,将剩余从节点指向新的主节点进行数据复制。
        7. 当没有足够数量的Sentinel同意主服务器下线时,主服务器的客观下线状态就会被移除。当主服务器重新向Sentinel的PING命令返回有效回复时,主服务器的主观下线状态就会被移除。

7.3 cluster模式

1) 概念
cluster的出现是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。对cluster的一些理解:
cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。
因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容
  这种模式适合数据量巨大的缓存要求,当数据量不是很大使用sentinel即可。
2)集群环境准备

本文以centos7系统、redis5.0版本为例,演示集群环境在Linux下的搭建过程及使用。注意redis4.0及以下版本搭建集群需要安装ruby来实现。因为redis是多进程单线程的,所以准备了三台服务器,每台服务器上一个master一个salve。端口分别为7001,7002

IP地址 端口 系统
XXX.XXX.XXX.100 7001 centos7
XXX.XXX.XXX.100 7002 centos7
XXX.XXX.XXX.101 7001 centos7
XXX.XXX.XXX.101 7002 centos7
XXX.XXX.XXX.102 7001 centos7
XXX.XXX.XXX.102 7002 centos7

1. Redis群集配置参数
    我们即将创建一个示例集群部署。在继续之前,让我们介绍Redis Cluster在redis.conf文件中引入的配置参数。有些人会很明显,有些人会在你继续阅读时更清楚。
    cluster-enabled<yes/no>:如果是,则在特定Redis实例中启用Redis群集支持。否则,实例像往常一样作为独立实例启动。
    cluster-config-file<filename>:请注意,尽管有此选项的名称,但这不是用户可编辑的配置文件,而是每次发生更改时Redis群集节点自动保持群集配置(基本上是状态)的文件,为了能够在启动时重新阅读它。该文件列出了集群中其他节点,状态,持久变量等内容。由于某些消息接收,通常会将此文件重写并刷新到磁盘上。
    cluster-node-timeout<milliseconds>:Redis群集节点不可用的最长时间,不会被视为失败。如果主节点的可访问时间超过指定的时间,则其从属节点将进行故障转移。此参数控制Redis群集中的其他重要事项。值得注意的是,在指定时间内无法访问大多数主节点的每个节点都将停止接受查询。
    cluster-slave-validity-factor<factor>:如果设置为零,则从站将始终尝试对主站进行故障切换,而不管主站和从站之间的链路是否保持断开连接的时间长短。如果该值为正,则计算最大断开时间作为节点超时值乘以此选项提供的因子,如果节点是从属节点,则如果主链接断开连接的时间超过指定的时间,则不会尝试启动故障转移。例如,如果节点超时设置为5秒,并且有效性因子设置为10,则从主设备断开超过50秒的从设备将不会尝试故障转移其主设备。请注意,如果没有从站能够对其进行故障转移,则任何不同于零的值都可能导致Redis群集在主站发生故障后不可用。在这种情况下,只有当原始主服务器重新加入群集时,群集才会返回。
    cluster-migration-barrier<count>:主服务器将保持连接的最小从服务器数,以便另一个从服务器迁移到不再由任何从服务器覆盖的主服务器。有关详细信息,请参阅本教程中有关副本迁移的相应部分。
    cluster-require-full-coverage<yes/no>:如果设置为yes,则默认情况下,如果任何节点未覆盖某个百分比的密钥空间,则集群将停止接受写入。如果该选项设置为no,即使只能处理有关键子集的请求,群集仍将提供查询。
    3)搭建集群
    1.下载
        分别在三台机器上下载redis安装包,也可以在一台机器上下载复制到另外两台机器
      [root@server /usr/local]# wget http://download.redis.io/releases/redis-5.0.3.tar.gz
    2. 解压、编译
        在三台集群上都进行如下操作:
        # 创建redis-cluster集群目录
        [root@server /usr/local]# mkdir redis-cluster
        # 创建redis子目录
        [root@server /usr/local]# cd redis-cluster
        [root@server /usr/local/redis-cluster]# mkdir redis7001 redis7002
      #解押redis到 /usr/local/ 文件夹
        [root@server /usr/local/redis-cluster]# cd ..
      [root@server /usr/local]# tar -zxvf redis-5.0.3.tar.gz -C /usr/local/
      # 编译,并安装熬 /usr/local/redis 文件夹
      [root@server /usr/local]# make & make install PREFIX=/usr/local/redis-cluster
      #复制 redis.conf 到安装目录
       [root@server /usr/local]# cp -r  /usr/local/redis-5.0.3/redis.conf  /usr/local/redis-cluster/redis7001
       [root@server /usr/local]# cp -r  /usr/local/redis-5.0.3/redis.conf  /usr/local/redis-cluster/redis7002
    3. 修改配置文件
        三台机器的六个配置文件都需要修改,以其中一个台服务器的redis7001和redis7002的配置文件为例,做如下修改,剩余两台服务器类似。下面是列出了redis修改的配置参数:

      # 打开redis7001的 redis.conf 集群配置文件
      [root@server /usr/local]# vim /usr/local/redis-cluster/redis7001/redis.conf
      # 配置下面内容
      daemonize yes
      bind XXX.XXX.XXX.100 #这是服务器的IP地址
      port 7001
      cluster-enabled yes
      cluster-config-file nodes-7001.conf
      cluster-node-timeout 5000
      pidfile redis_7001.pid
      logfile "redis_7001.log"
      dir /usr/local/redis-cluster/redis7001


      # 打开redis7002的 redis.conf 集群配置文件
      [root@server /usr/local]# vim /usr/local/redis-cluster/redis7002/redis.conf
      # 配置下面内容
      daemonize yes
      bind XXX.XXX.XXX.100 #这是服务器的IP地址
      port 7002
      cluster-enabled yes
      cluster-config-file nodes-7002.conf
      cluster-node-timeout 5000
      pidfile redis_7002.pid
      logfile "redis_7002.log"
      dir /usr/local/redis-cluster/redis7002

    4. 启动节点
        分别启动三台服务上的redis
        # 进入redis-cluster的bin目录
        [root@server /usr/local]# cd redis-cluster/bin
        # 启动redis的7001和7002进程
        [root@server /usr/local/redis-cluster/bin]# ./redis-server /usr/local/redis-cluster/redis7001/redis.conf
     [root@server /usr/local/redis-cluster/bin]# ./redis-server /usr/local/redis-cluster/redis7002/redis.conf
    5.创建集群
        [root@server /usr/local/redis-cluster/bin]# ./redis-cli --cluster create XXX.XXX.XXX.100:7001 XXX.XXX.XXX.100:7002 XXX.XXX.XXX.101:7001 XXX.XXX.XXX.101:7002 XXX.XXX.XXX.102:7001 XXX.XXX.XXX.102:7002 --cluster-replicas 1

     
     输入yes,显示下图,表示集群启动成功

    

    可以看到16384个槽,被平均分配给了三个master。同时每台集群的7001是master,7002是slave,三主三从模式搭建完毕

6.查看集群信息
        查看节点信息
        [root@server /usr/local/redis-cluster/bin]# ./redis-cli -c -h XXX.XXX.XXX.100 -p 7001 

      输入 cluster nodes 查询集群节点信息,输入  cluster info查询集群完整性信息

        
        查看集群完整性
        [root@server /usr/local/redis-cluster/bin]#./redis-cli --cluster check XXX.XXX.XXX.100:7001

        

7.集群测试
    通过一台服务器7001 的master存储set一条数据,通过其他服务器get这条数据,验证集群的可用性
    [root@server /usr/local/redis-cluster/bin]# ./redis-cli -c -h XXX.XXX.XXX.100 -p 7001 

           

   [root@server /usr/local/redis-cluster/bin]# ./redis-cli -c -h XXX.XXX.XXX.101 -p 7002

       

   当然,登录其他几个节点的redis,也能get到这条数据,这里不再演示。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!