Day25:内存式NoSQL数据库Redis

Posted 保护胖仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day25:内存式NoSQL数据库Redis相关的知识,希望对你有一定的参考价值。

知识点01:回顾

  1. RDBMS与NoSQL的特点分别是什么?

    • RDBMS:事务特性,稳定性和数据安全性,小数据量性能较好
    • NoSQL:高并发、高性能
  2. Redis的功能与应用场景是什么?

    • 功能:提供高性能和高并发的数据存储
    • 应用
      • 数据库:提供高性能的读写
      • 缓存:解决高并发问题
      • 消息队列:实现高性能和高并发的数据传递,一般不用
    • 特点
      • 基于C语言开发,底层兼容性更好
      • 基于内存的数据存储
      • 分布式
      • 支持多种数据类型
  3. Redis中的数据是什么结构?Value的常用类型有哪几种?

    • 数据结构:KV结构
    • 数据类型
      • K:String
      • V:String、Hash、List、Set、Zset、BitMap、HyperLogLog
  4. 每种类型的常用命令有哪些?

    • String:set、get、mset、mget、strlen、setnx
    • Hash:hset、hget、hgetall、hdel
    • List:lpush/rpush、lrange、lpop/rpop、llen
    • Set:sadd、srem、smembers、scard
    • Zset:zadd、zrange、zrevrange、zrem、zcard
  5. Jedis中有哪些类和方法?

      • Jedis:客户端连接对象
      • JedisPool:连接池对象
        • getResource:获取连接对象
      • JedisPoolConfig:连接池配置对象
    • 方法:所有操作都对应命令名称同名的方法

知识点02:目标

  1. 如何保证Redis的数据安全?
    • 内存中的数据如何保证不丢失?
    • 默认机制:RDB
    • 可选机制:AOF
  2. Redis中的理论点
    • Redis事务:单线程
    • Redis实现消息队列
    • 数据过期策略和内存淘汰机制
      • expire
      • setex
  3. Redis集群架构:分布式:实现即可
    • 主从复制
    • 哨兵集群
    • 分片集群
  4. 常见一些面试题
    • 缓存雪崩
    • 缓存穿透
    • 缓存击穿
    • 为什么Redis是单线程还很快?

知识点03:Redis持久化:数据存储设计

  • 目标掌握常见数据存储的设计

  • 实施

    • 问题
      • 数据存储如何保证数据安全?
    • 解决
      • 磁盘存储:数据直接存储在硬盘上
        • 特点:容量大、安全性高、读写速度上相对不如内存
        • 软件备份:在软件层次备份多份
          • HDFS数据存储:每个文件实际存储了3份
          • Hadoop3.0开始:HDFS通过纠错码来实现安全机制
        • 硬件备份:磁盘冗余阵列:RAID
          • RAID0:两块硬盘,每块1TB,操作系统中只能看到1块硬盘,2TB
          • RAID1:两块硬盘,每块1TB,操作系统中只能看到1块硬盘,1TB
      • 内存存储:数据直接放在内存中
        • 特点:容量小、安全性低、读写性能高
        • 内存备份:解决内存数据容易丢失的问题
        • 持久化方案:persist
          • 将内存的数据同步到磁盘
          • 常见实现:只追加记录变化:日志记录方式
          • HDFS的NameNode管理元数据:edits
          • mysql:binlog
  • 小结

    • 掌握常见数据存储的设计

知识点04:Redis持久化:RDB设计

  • 目标:掌握Redis的RDB持久化机制

  • 路径

    • step1:问题
    • step2:RDB方案
    • step3:优缺点
  • 实施

    • 问题

      Redis中的数据都存储在内存中,由内存对外提供读写,Redis一旦断电重启,内存中的数据就会丢失,Redis如何实现持久化?
      
    • RDB方案

      • Redis**默认的持久化方案**

      • 思想

        • 按照一定的时间内,如果Redis内存中的数据产生了一定次数的更新,就将整个Redis内存中的所有数据拍摄一个快照文件存储在硬盘上
        • 新的快照会覆盖老的快照文件,快照是全量快照,包含了内存中所有的内容,基本与内存一致
        • 如果Redis故障重启,从硬盘的快照文件进行恢复
      • 举例

        • 配置:save 30 2
        • 解释:如果30s内,redis内存中的数据发生了2条更新,就将整个Redis内存数据保存到磁盘文件中,作为快照
      • 过程

      • 触发

        • 手动触发:当执行某些命令时,会自动拍摄快照【一般不用】

          • save:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
            • 前端运行
            • 阻塞所有的客户端请求,等待快照拍摄完成后,再继续处理客户端请求
            • 特点:快照与内存是一致的,数据不会丢失,用户的请求会被阻塞
        • bgsave:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照

          • 后台运行
          • 主进程会fork一个子进程负责拍摄快照,客户端可以正常请求,不会被阻塞
            • 特点:用户请求继续执行,用户的新增的更新数据不在快照中
        • shutdown:执行关闭服务端命令

          • flushall:清空,没有意义
        • 自动触发:按照一定的时间内发生的更新的次数,拍摄快照

          • 配置文件中有对应的配置,决定什么时候做快照
            #Redis可以设置多组rdb条件,默认设置了三组,这三组共同交叉作用,满足任何一个都会拍摄快照
            save 900 1
            save 300 10
            save 60 10000
          
          • 为什么默认设置3组?
          • 原因:如果只有一组策略,面向不同的写的场景,会导致数据丢失
          • 针对不同读写速度,设置不同策略,进行交叉保存快照,满足各种情况下数据的保存策略
    • 优缺点

      • 优点

        • rdb方式实现的是全量快照,快照文件中的数据与内存中的数据是一致的
        • 快照是二进制文件,生成快照加载快照都比较快,体积更小
        • Fork进程实现,性能更好
        • 总结:更快、更小、性能更好
      • 缺点

      • 存在一定概率导致部分数据丢失

      • 应用:希望有一个高性能的读写,不影响业务,允许一部分的数据存在一定概率的丢失,大规模的数据备份和恢复

  • 小结

    • 什么是RDB机制,优缺点分别是什么?
      • 思想:在一定时间内如果发生一定次数的更新,就拍摄全量快照记录在磁盘上
      • 优点:更快、更小、性能更好
      • 缺点:有一定的概率存在数据丢失的情况
      • 应用:大量数据的备份和恢复

知识点05:Redis持久化:RDB测试

  • 目标实现RDB持久化的测试

  • 实施

    • 查看当前快照

      ll /export/server/redis/datas/
      

  • 配置修改

    cd /export/server/redis
    vim redis.conf
    #202行
    save 900 1
    save 300 10
    save 60 10000
    save 20 2
    
  • 重启redis服务,配置才会生效

    ps -ef | grep redis
    kill -9 pid
    redis-start.sh
    
  • 插入数据

    set s1 "laoda"
    set s2 "laoliu"
    
  • 查看dump的rdb快照

    ll /export/server/redis/datas/
    

  • 小结

    • 实现RDB持久化的测试

知识点06:Redis持久化:AOF设计

  • 目标:掌握Redis的AOF持久化机制

  • 路径

    • step1:问题
    • step2:AOF方案
    • step3:优缺点
    • step4:持久化方案
  • 实施

    • 问题

      RDB存在一定概率的数据丢失,如何解决?
      
    • AOF方案

      • 思想

        • 按照一定的规则,将内存数据的操作日志追加写入一个文件中
        • 当Redis发生故障,重启,从文件中进行读取所有的操作
        • 重新对Redis进行执行,用于恢复内存中的数据
      • 过程

      • 实现

        • appendfsync always
          • 每更新一条数据就同步将这个更新操作追加到文件中
          • 优点:数据会相对安全,几乎不会出现数据丢失的情况
          • 缺点:频繁的进行数据的追加,增大磁盘的IO,导致性能较差
        • appendfsync everysec
          • 每秒将一秒内Redis内存中数据的操作异步追加写入文件
          • 优点:在安全性和性能之间做了权衡,性能要比always高
          • 缺点:有数据丢失风险 ,但最多丢失1秒
        • appendfsync no
          • 交给操作系统来做,不由Redis控制
    • 优缺点

      • 优点:安全性和性能做了折中方案,如果性能要求不高,安全性可以达到最高

      • 缺点

        • 这个文件是普通文本文件,相比于二进制文件来说,每次追加和加载比较慢

        • 数据的变化以追加的方式写入AOF文件

          • 问题:文件会不断变大,文件中会包含不必要的操作【过期的数据】
      • 解决:模拟类似于RDB做全量的方式,定期生成一次全量的AOF文件

      • 应用:数据持久化安全方案

    • 持久化方案

      • 两种方案怎么选?

        • 两种方案可以共存
        • 问题:既有RDB全量快照又有AOF操作日志,下次Redis重启读谁?
        • 解决:AOF优先级高于RDB
  • 小结

    • 什么是AOF机制?
      • 按照一定规则将Redis内存的变化操作日志追加写入文件
      • 下次重启Redis,从操作日志中解析恢复数据

知识点07:Redis持久化:AOF实现

  • 目标:实现AOF持久化

  • 实施

    • 开启并配置

      vim redis.conf
      #594行:开启aof
      appendonly yes
      #624行:默认每s刷写一次
      appendfsync everysec
      #665,666
      #增幅100%就重新覆盖一次
      auto-aof-rewrite-percentage 100
      #文件至少要大于64MB,一般建议更改为GB大小
      auto-aof-rewrite-min-size 64mb
      
    • 重启Redis

      ps -ef | grep redis
      kill -9 pid
      redis-start.sh
      
    • 查看数据

      keys *
      

- 从AOF文件恢复数据
  • 查看aof文件

    ll /export/server/redis/datas
    

  • 小结

    • 实现AOF持久化

知识点08:Redis的事务机制

  • 目标:了解Redis的事务机制

  • 实施

    • 事务定义:事务是数据库操作的最小工作单元,包含原子性、一致性、隔离性、持久性

    • Redis事务:Redis一般不用事务

      • Redis本身是单线程的,所以本身没有事务等概念
      • Redis 支持事务的本质是一组命令的集合,事务支持一次执行多个命令,串行执行每个命令
      • 一旦Redis开启了事务,将所有命令放入一个队列中,提交事务时,对整个队列中的命令进行执行
      • redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
      • 没有隔离性:批量的事务命令执行前在缓存队列中,没有事务交叉,不存在脏读幻读等问题
      • 不能保证原子性:单条命令是原子性执行的,但事务不保证原子性,且没有回滚机制,事务中任意命令执行失败,其余的命令仍会被执行。
    • 过程

      • 开启事务
      • 提交命令
      • 执行事务
    • 命令

      • multi:开启事务
      • exec:执行事务
      • discard:取消事务
      • watch:监听机制,类似于乐观锁
      • unwatch:取消监听
    • 测试1:正常使用

      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      multi
      set user1 hadoop3
      set user2 hadoop4
      exec
      get user1
      get user2
      
    • 测试2:语法【编译】错误

      flushdb
      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      multi
      set user1 hadoop3
      sets user2 hadoop4
      exec
      get user1
      get user2
      
    • 测试3:类型【运行】错误

      flushdb
      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      multi
      set user1 hadoop3
      lpush user2 hadoop4
      exec
      get user1
      get user2
      
    • 测试4:取消事务

      flushdb
      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      multi
      set user1 hadoop3
      set user2 hadoop4
      discard
      get user1
      get user2
      
    • 测试5:监听

      flushdb
      watch user2
      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      multi
      set user1 hadoop3
      lpush user2 hadoop4
      exec
      get user1
      get user2
      
    • 测试6:取消监听

      flushdb
      watch user2
      set user1 hadoop1
      set user2 hadoop2
      get user1
      get user2
      unwatch
      multi
      set user1 hadoop3
      lpush user2 hadoop4
      exec
      get user1
      get user2
      
  • 小结

    • 了解Redis的事务机制

知识点09:Redis实现消息队列

  • 目标了解Redis如何实现消息队列

  • 实施

    • 消息队列是什么?

- 问题

  - A的并发高,B的并发量低,导致B无法接受所有数据,会有数据丢失
  - A的数据只给了B,如果C也要A的数据,必须重修修改代码,让A给C再发一份

- 解决:引入消息队列

  • Redis如何实现消息队列?

    • 命令

      • PUBLISH:广播生产消息
      • subscribe:订阅消息
      • unsubscribe:取消订阅
    • 测试

      • 启动多个客户端,先订阅后发布

      • 客户端1

        PUBLISH "cctv.5" "this is China"
        
      • 客户端2

        SUBSCRIBE "cctv.5"
        
      • 客户端3

        SUBSCRIBE "cctv.5"
        
  • 小结

    • 了解Redis如何实现消息队列

知识点10:Redis过期策略与内存淘汰机制

  • 目标了解Redis的过期策略与内存淘汰机制

  • 路径

    • step1:内存问题
    • step2:过期策略
    • step3:淘汰机制
  • 实施

    • 内存问题

      Redis使用的是内存,内存如果满了,怎么解决?
      
    • 过期策略

      • 设计思想:避免内存满,指定Key的存活时间,到达存活时间以后自动删除

        • 命令:expire/setex
      • 定时过期:指定Key的存活时间,一直监听这个存活时间,一旦达到存活时间,自动删除

        • 需要CPU一直做监听,如果Key比较多,CPU的消耗比较严重
      • 惰性过期:指定Key的存活时间,当使用这个Key的时候,判断是否过期,如果过期就删除

        • 如果某个Key设置了过期时间,但是一直没有使用,不会被发现过期了,就会导致资源浪费
      • 定期过期:每隔一段时间就检查数据是否过期,如果过期就进行删除

        • 中和的策略机制
      • Redis中使用了惰性过期和定期过期两种策略公共作用

    • 淘汰机制

      • 设计思想:内存满了,怎么淘汰

      • Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据, Redis 源码中的默认配置

        # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
        # is reached. You can select among five behaviors:
        #最大内存策略:当到达最大使用内存时,你可以在下面5种行为中选择,Redis如何选择淘汰数据库键
        
        #当内存不足以容纳新写入数据时
        
        # volatile-lru -> remove the key with an expire set using an LRU algorithm
        # volatile-lru :在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把 redis 既当缓存,又做持久化存储的时候才用。
        
        # allkeys-lru -> remove any key according to the LRU algorithm
        # allkeys-lru : 移除最近最少使用的key (推荐)
        
        # volatile-random -> remove a random key with an expire set
        # volatile-random : 在设置了过期时间的键空间中,随机移除一个键,不推荐
        
        # allkeys-random -> remove a random key, any key
        # allkeys-random : 直接在键空间中随机移除一个键,弄啥叻
        
        # volatile-ttl -> remove the key with the nearest expire time (minor TTL)
        # volatile-ttl : 在设置了过期时间的键空间中,有更早过期时间的key优先移除 不推荐
        
        # noeviction -> don't expire at all, just return an error on write operations
        # noeviction : 不做过键处理,只返回一个写操作错误。 不推荐
        
        # Note: with any of the above policies, Redis will return an error on write
        #       operations, when there are no suitable keys for eviction.
        # 上面所有的策略下,在没有合适的淘汰删除的键时,执行写操作时,Redis 会返回一个错误。下面是写入命令:
        #       At the date of writing these commands are: set setnx setex append
        #       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
        #       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
        #       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
        #       getset mset msetnx exec sort
        
        # 过期策略默认是:
        # The default is:
        # maxmemory-policy noeviction
        
      • 实际项目中设置内存淘汰策略:maxmemory-policy allkeys-lru,移除最近最少使用的key。

  • 小结

    • 了解Redis的过期策略与内存淘汰机制

知识点11:Redis架构:问题与主从复制集群设计

  • 目标:掌握Redis架构中的问题及主从复制集群的设计

  • 路径

    • step1:架构问题
    • step2:主从复制集群设计
  • 实施

    • 架构问题

      • Redis的服务只有单台机器
      • 问题1:单点故障问题,如果Redis服务故障,整个Redis服务将不可用
      • 问题2:单台机器的内存比较小,数据存储的容量不足,会导致redis无法满足需求
    • 主从复制集群设计

      • 架构

- **设计**

  - 分布式主从架构
  - Master:主节点
    - 负责对外提供数据的读写
  - Slave:从节点
    - 负责对外提供读的请求
    - 负责与主节点同步数据
  - 特点:主从节点上的数据都是一致的,连接任何一个节点实现读,==写操作只能连接主节点==

- **优缺点**

  - 优点:实现了读写分离,分摊了读写的压力负载,如果一台Redis故障,其他的Redis服务节点照常对外提供服务
  - 缺点:如果Master故障,整个集群不能对外提供写的操作,==Master没有HA机制==
  • 小结

    • 掌握Redis架构中的问题及主从复制集群的设计

知识点12:Redis架构:主从复制集群的实现

  • 目标:实现Redis主从复制集群的搭建测试

  • 实施

    • 配置三台Redis

      • 复制到第二台和第三台

        cd /export/server/
        scp -r redis-3.2.8-bin node2:$PWD
        scp -r redis-3.2.8-bin node3:$PWD
        
      • 配置node2和node3

      • 创建链接

        cd /export/server/
        ln -s redis-3.2.8-bin redis
        
      • 配置环境变量

        vim /etc/profile
        
        # REDIS HOME
        export REDIS_HOME=/export/server/redis
        export PATH=:$PATH:$REDIS_HOME/bin
        
        source /etc/profile
        
      • 修改配置文件

        cd /export/server/redis
        vim redis.conf
        
        • node2

          #61行
          bind node2
          #266行
          slaveof node1 6379
          
        • node3

          #61行
          bind node3
          #266行
          slaveof node1 6379
          
      • 三台机器都执行

        cd /export/server/redis
        rm -rf ./datas/* ./logs/*
        
    • 启动测试

      • 依次启动三台Redis

        redis-start.sh
        
      • 连接服务端

        redis-cli -h node1
        info replication
        
      • 读写测试

        • 主节点

  • 小结

    • 实现Redis主从复制集群的搭建测试

知识点13:Redis架构:主从复制同步与配置

  • 目标:了解主从复制的同步策略及配置

  • 路径

    • step1:同步策略
    • step2:常见配置
  • 实施

    • 同步策略

      • 全量同步
        • 新的Slave节点添加到集群中或者Slave与Master断开很长时间,超出了数据积压缓冲大小
        • Master会通过bgsave构建一份完整的内存快照,发送给Slave,Slave加载快照的数据
        • Master也会维护一个缓存队列,将快照中没有的数据,放在缓存队列中,Slave会同步缓存中的数据
        • 全量同步比较消耗性能
      • 增量同步
        • Master会将当前Slave没有同步的数据放在积压缓存区中
        • Slave请求同步数据时,Master如果判断同步的数据在积压缓冲区中进行进行返回同步
    • 常见配置

      ################################# REPLICATION #################################
      # 复制选项,slave复制对应的master。
      slaveof <masterip> <masterport>
      
      # 如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。
      # masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
      # masterauth <master-password>
      
      # 当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:
      # 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。
      # 2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
      slave-serve-stale-data yes
      
      # 作为从服务器,默认情况下是只读的(yes)
      slave-read-only yes
      
      # 是否使用socket方式复制数据。
      # 目前redis复制提供两种方式,disk和socket。
      # 如果新的slave连上来或者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。
      # 有2种方式:
      # 1.disk方式是master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。
      # 2.socket是master创建一个新的进程,直接把rdb文件以socket的方式发给slave。
      # disk方式的时候,当一个rdb保存的过程中,多个slave都能共享这个rdb文件。
      # socket的方式就的一个个slave顺序复制。
      # 在磁盘速度缓慢,网速快的情况下推荐用socket方式。
      repl-diskless-sync no
      
      # diskless复制的延迟时间,防止设置为0。
      # 一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。
      # 所以最好等待一段时间,等更多的slave连上来。
      repl-diskless-sync-delay 5
      
      # slave根据指定的时间间隔向服务器发送ping请求。
      # 时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
      # repl-ping-slave-period 10
      
      # 复制连接超时时间。
      # master和slave都有超时时间的设置。
      # master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。
      # slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。
      # 需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时。
      # repl-timeout 60
      
      # 是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。
      # 默认是no,即使用tcp nodelay。
      # 如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。
      # 但是这也可能带来数据的延迟。
      # 默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
      repl-disable-tcp-nodelay no
      
      # 复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。
      # 这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。
      # 缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。
      # 没有slave的一段时间,内存会被释放出来,默认1m。
      # repl-backlog-size 5mb
      
      # master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。
      # 单位为秒。
      # repl-backlog-ttl 3600
      
      # 当master不可用,Sentinel会根据slave的优先级选举一个master。
      # 最低的优先级的slave,当选master。
      # 而配置成0,永远不会被选举。
      # 注意:要实现Sentinel自动选举,至少需要2台slave。
      slave-priority 100
      
      # redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。
      # master最少得有多少个健康的slave存活才能执行写命令。
      # 这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。
      # 设置为0是关闭该功能,默认也是0。
      # min-slaves-to-write 3
      
      # 延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
      # min-slaves-max-lag 10
      
  • 小结

    • 了解主从复制的同步策略及配置

知识点14:Redis架构:哨兵集群的设计

  • 目标:掌握哨兵集群的设计

  • 路径

    • step1:问题
    • step2:哨兵设计
  • 实施

    • 问题

      主从复制集群的Master存在单点故障问题,怎么解决?
      
      • 类似于ZK的设计
        • 每台节点存储的数据都是一样的
        • 如果Leader故障,允许Follower选举成为Leader
    • 哨兵设计

      • 思想基于主从复制模式之上封装了哨兵模式,如果Master出现故障,让Slave选举成为新的Master

      • 实现哨兵进程实现

        • 必须能发现Master的故障
        • 必须负责重新选举新的Master
      • 架构

  - Redis主从架构
  - 哨兵进程
    - 每个哨兵负责监听所有Redis节点和其他哨兵
    - 为什么要监听所有Redis的节点:发现所有节点是否会出现故障
      - 如果Master出现故障,会进行投票选择一个Slave来成为新的Master
    - 为什么要监听别的哨兵:如果哨兵故障,不能让这个哨兵参与投票选举等
- 哨兵功能
    - 集群监控:监控节点状态
  - 消息通知:汇报节点状态
  - 故障转移:实现Master重新选举
  - 配置中心:实现配置同步

- **流程**

  - step1:如果Master突然故障,有一个哨兵会发现这个问题,这个哨兵会立即通告给所有哨兵
    - 主观性故障【sdown】
  - step2:当有一定的个数的哨兵都通告Master故障了,整体认为Master故障了
    - 客观性故障【odown】
  - step3:所有哨兵根据每台Slave通信的健康状况以及Slave权重选举一个新的Master
  - step4:将其他所有的Slave的配置文件中的Master切换为当前最新的Master
  • 小结

    • 掌握哨兵集群的设计

知识点15:Redis架构:哨兵集群的实现

  • 目标:实现哨兵模式的搭建测试

  • 实施

    • 配置哨兵服务

      • 第一台机器复制哨兵配置文件:sentinel.conf

        cp /export/server/redis-3.2.8/sentinel.conf /export/server/redis/
        
      • 修改配置文件

        vim sentinel.conf
        
        #18行
        bind 0.0.0.0
        protected-mode no
        daemonize yes
        logfile "/export/server/redis-3.2.8-bin/logs/sentinel.log"
        #73行
        sentinel monitor mymaster node1 6379 2
        
      • 分发给第二台和第三台

        cd /export/server/redis
        scp -r sentinel.conf root@node2:$PWD
        scp -r sentinel.conf root@node3:$PWD
        
    • 启动

      • 启动三台Redis服务

        redis-start.sh
        
      • 启动三台哨兵服务

        redis-sentinel /export/server/redis/sentinel.conf
        
    • 测试

      • 连接Redis

        redis-cli -h node1
        

- 连接哨兵

  ```
  redis-cli -h node3 -p 26379
  info sentinel
  ```

  • Jedis中哨兵的连接

    //方案三:构建哨兵连接池:第一个参数是master的逻辑名称,第二个参数是哨兵列表,第三个是连接池的配置
    HashSet<String> sets = new HashSet<>();
    sets.add("node1:26379");
    sets.add("node2:26379");
    sets.add("node3:26379");
    JedisSentinelPool mymaster = new JedisSentinelPool("mymaster", sets, jedisPoolConfig);
    //从连接池中获取连接
    jedis = mymaster.getResource();
    
  • 小结

    • 实现哨兵模式的搭建测试

知识点16:Redis架构:分片集群的设计

  • 目标掌握分片集群的设计

  • 路径

    • step1:问题
    • step2:分片集群设计
  • 实施

    • 问题

      Redis哨兵集群中的存储容量只有单台机器,如何解决大量数据使用Redis存储问题?
      
    • 分片集群设计

      • 分片集群模式

      • 思想:将多个Redis小集群从逻辑上合并为一个大集群,每个小集群分摊一部分槽位,对每一条Redis的数据进行槽位计算,这条数据属于哪个槽位,就存储对应槽位的小集群中

        • 分片的规则:根据Key进行运算:CRC16【K】 & 16383 = 0 ~ 16383

  • 架构

  • 小结

    • 掌握分片集群的设计

知识点17:Redis架构:分片集群的实现

  • 目标实现分片集群的搭建

  • 实施

    • 关闭所有机器的redis服务与哨兵服务

      redis-cli -h node1 -p 6379 SHUTDOWN
      redis-cli -h node2 -p 6379 SHUTDOWN
      redis-cli -h node3 -p 6379 SHUTDOWN
      
      redis-cli -h node1 -p 26379 SHUTDOWN
      redis-cli -h node2 -p 26379 SHUTDOWN
      redis-cli -h node3 -p 26379 SHUTDOWN
      
    • 删除三台机器的软连接

      rm -rf /export/server/redis
      
    • 三台机器都保证有C语言环境

      yum -y install gcc-c++ tcl
      

  • 上传redis5到第一台机器

    cd /export/software/
    rz
    
  • 解压编译安装

    tar -zxf redis-5.0.8.tar.gz -C /export/server/
    cd /export/server/redis-5.0.8
    # 编译
    make
    # 安装至指定目录
    make PREFIX=/export/server/redis-5.0.8-bin install
    

  • 创建软连接

    cd /export/server
    ln -s redis-5.0.8-bin redis
    
  • 拷贝配置文件

    cp /export/server/redis-5.0.8/redis.conf /export/server/redis
    
  • 创建两个服务目录

    # 创建目录:7001和7002
    cd /export/server/redis
    mkdir -p 7001 7002
    
  • 配置7001

    cd /export/server/redis
    cp redis.conf 7001/redis_7001.conf
    
    vim 7001/redis_7001.conf
    
    #69行
    bind 0.0.0.0
    #88行
    protected-mode no
    #92行
    port 7001
    #136行
    daemonize yes
    #158行
    pidfile /var/run/redis_7001.pid
    #171行
    logfile "/export/server/redis-5.0.8-bin/7001/logs/redis.log"
    #263行
    dir /export/server/redis-5.0.8-bin/7001/datas/
    #293行
    masterauth 123456
    #507行
    requirepass 123456
    #699行
    appendonly yes
    #832行
    cluster-enabled yes
    #840行
    cluster-config-file nodes-7001.conf
    #846行
    cluster-node-timeout 15000
    
    mkdir -p /export/server/redis/7001/logs
    mkdir -p /export/server/redis/7001/datas
    
  • 配置7002

    • 拷贝

      cd /export/server/redis
      cp 7001/redis_7001.conf 7002/redis_7002.conf
      
    • 替换

      所有7001换成7002
      
    • 创建目录

      mkdir -p /export/server/redis/7002/logs
      mkdir -p /export/server/redis/7002/datas
      
  • 发送给node2和node3

    cd /export/server
    scp -r redis-5.0.8-bin root@node2:$PWD
    scp -r redis-5.0.8-bin root@node3:$PWD
    
  • 创建软连接

    cd /export/server
    ln -s redis-5.0.8-bin redis
    
  • 启动服务

    • 三台机器启动所有redis进程

      redis-server /export/server/redis/7001/redis_7001.conf
      redis-server /export/server/redis/7002/redis_7002.conf
      

- 初始化配置集群

  ```
  redis-cli -a 123456 --cluster create \\
  192.168.88.130:7001 192.168.88.130:7002 \\
  192.168.88.131:7001 192.168.88.131:7002 \\
  192.168.88.132:7001 192.168.88.132:7002 \\
  --cluster-replicas 1
  ```

  - --cluster-replicas :每个小的集群有几个副本,不包含master

- 结果

  ```
  >>> Performing hash slots allocation on 6 nodes...
  Master[0] -> Slots 0 - 5460
  Master[1] -> Slots 5461 - 10922
  Master[2] -> Slots 10923 - 16383
  Adding replica 192.168.88.131:7002 to 192.168.88.130:7001
  Adding replica 192.168.88.132:7002 to 192.168.88.131:7001
  Adding replica 192.168.88.130:7002 to 192.168.88.132:7001
  M: 2564bea6bdc5a367ae8098f5674957a69a929659 192.168.88.130:7001
     slots:[0-5460] (5461 slots) master
  S: a20f85e538e538ca97d1bf052d55719409648854 192.168.88.130:7002
     replicates de952c29bda3a33321d9e5ab756975a5c3cf015a
  M: 9db82f4d39d40b0c486e7d0f319bc2a5fb82cac7 192.168.88.131:7001
     slots:[5461-10922] (5462 slots) master
  S: b20bb439d99d4fb0e861578ef3ca45d4d29f6afd 192.168.88.131:7002
     replicates 2564bea6bdc5a367ae8098f5674957a69a929659
  M: de952c29bda3a33321d9e5ab756975a5c3cf015a 192.168.88.132:7001
     slots:[10923-16383] (5461 slots) master
  S: 25839625c79a49274875b5b28887d4e3e4a775fd 192.168.88.132:7002
     replicates 9db82f4d39d40b0c486e7d0f319bc2a5fb82cac7
  Can I set the above configuration? (type 'yes' to accept): yes
  >>> Nodes configuration updated
  >>> Assign a different config epoch to each node
  >>> Sending CLUSTER MEET messages to join the cluster
  Waiting for the cluster to join
  ....
  >>> Performing Cluster Check (using node 192.168.88.130:7001)
  M: 2564bea6bdc5a367ae8098f5674957a69a929659 192.168.88.130:7001
     slots:[0-5460] (5461 slots) master
     1 additional replica(s)
  M: 9db82f4d39d40b0c486e7d0f319bc2a5fb82cac7 192.168.88.131:7001
     slots:[5461-10922] (5462 slots) master
     1 additional replica(s)
  M: de952c29bda3a33321d9e5ab756975a5c3cf015a 192.168.88.132:7001
     slots:[10923-16383] (5461 slots) master
     1 additional replica(s)
  S: b20bb439d99d4fb0e861578ef3ca45d4d29f6afd 192.168.88.131:7002
     slots: (0 slots) slave
     replicates 2564bea6bdc5a367ae8098f5674957a69a929659
  S: 25839625c79a49274875b5b28887d4e3e4a775fd 192.168.88.132:7002
     slots: (0 slots) slave
     replicates 9db82f4d39d40b0c486e7d0f319bc2a5fb82cac7
  S: a20f85e538e538ca97d1bf052d55719409648854 192.168.88.130:7002
     slots: (0 slots) slave
     replicates de952c29bda3a33321d9e5ab756975a5c3cf015a
  [OK] All nodes agree about slots configuration.
  >>> Check for open slots...
  >>> Check slots coverage...
  [OK] All 16384 slots covered.
  ```
  • 连接集群

    redis-cli -c -h node1 -p 7001 -a 123456 
    
  • 测试:cluster nodes

  • 其他操作

  • 启动脚本

    cd /export/server/redis
    vim bin/redis-cluster-start.sh
    
    #!/bin/bash
    
    REDIS_HOME=/export/server/redis
    # Start Server
    ## node1
    ssh node1 "$REDIS_HOME/bin/redis-server /export/server/redis/7001/redis_7001.conf"
    ssh node1 "$REDIS_HOME/bin/redis-server /export/server/redis/7002/redis_7002.conf"
    ## node2
    ssh node2 "$REDIS_HOME/bin/redis-server /export/server/redis/7001/redis_7001.conf"
    ssh node2 "$REDIS_HOME/bin/redis-server /export/server/redis/7002/redis_7002.conf"
    ## node3
    ssh node3 "$REDIS_HOME/bin/redis-server /export/server/redis/7001/redis_7001.conf"
    ssh node3 "$REDIS_HOME/bin/redis-server /export/server/redis/7002/redis_7002.conf"
    
    chmod u+x bin/redis-cluster-start.sh
    
  • 关闭脚本

    vim bin/redis-cluster-stop.sh
    
    #!/bin/bash
    
    REDIS_HOME=/export/server/redis
    # Stop Server
    ## node1
    $REDIS_HOME/bin/redis-cli -h node1 -p 7001 -a 123456 SHUTDOWN
    $REDIS_HOME/bin/redis-cli -h node1 -p 7002 -a 123456 SHUTDOWN
    ## node2
    $REDIS_HOME/bin/redis-cli -h node2 -p 7001 -a 123456 SHUTDOWN
    $REDIS_HOME/bin/redis-cli -h node2 -p 7002 -a 123456 SHUTDOWN
    ## node3
    $REDIS_HOME/bin/redis-cli -h node3 -p 7001 -a 123456 SHUTDOWN
    $REDIS_HOME/bin/redis-cli -h node3 -p 7002 -a 123456 SHUTDOWN
    
    chmod u+x bin/redis-cluster-stop.sh
    
  • Jedis中连接

    • 注意
    • 单节点模式/哨兵模式:Jedis
      • set/get
      • 分片集群模式:JedisCluster
      • set/get
        @Before
        public void getJedisCluster()
            //构建连接池的配置对象
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxTotal(30);//总连接数
            jedisPoolConfig.setMaxIdle(20);//最大空闲连接
            jedisPoolConfig.setMaxWaitMillis(1500);//等待时间
            //构建集群模式的额连接池
            HashSet<HostAndPort> sets = new HashSet<HostAndPort>();
            sets.add(new HostAndPort("node1",7001));
            sets.add(new HostAndPort("node1",7002));
            sets.add(new HostAndPort("node2",7001));
          sets.add(new HostAndPort("node2",7002));
            sets.add(new HostAndPort("node3",7001));
            sets.add(new HostAndPort("node3",7002));
            jedisCluster = new JedisCluster(sets,2000,2000,5,"123456",jedisPoolConfig);
        
    
  • 小结

    • 实现分片集群的搭建

知识点18:Redis常见面试题

  • 分片集群问题

  • 常见面试题

    • 什么是缓存穿透,怎么解决?

      • 现象:客户端高并发不断向Redis请求一个不存在的Key,MySQL中也没有

        • 由于Redis没有,导致这个并发全部落在MySQL上
      • 解决

        • step1:对于那些每秒访问频次过高的IP进行限制,拒绝访问

        • step2:如果第一次redis中没有,读MYSQL,MySQL也没有,在Redis中设置一个临时默认值

        • step3:利用BitMap类型构建布隆过滤器

          • MySQL
            • 1 hadoop
            • 2 hive
            • 3 hive
            • 4 spark
            • 5 hue
            • 10 oozie
          111110000100000000000000000000000000000000000000000000000000
          
          • 如果用户请求一个Key,对Key进行计算取余一个数字:10

          • 将MySQL中如果存在的数据都设置为1

          • 如果用户请求1:MySQL中有,可以请求

          • 如果用户请求8:MySQL中没有这个值,不可以请求MySQL

    • 以上是关于Day25:内存式NoSQL数据库Redis的主要内容,如果未能解决你的问题,请参考以下文章

      Day25:内存式NoSQL数据库Redis

      Day25:内存式NoSQL数据库Redis

      Day24:内存式NoSQL数据库Redis

      Day24:内存式NoSQL数据库Redis

      Day24:内存式NoSQL数据库Redis

      day25——NoSQL的字符串操作list操作set操作