redis

本文详细介绍了Redis的使用,包括Nosql的发展、Redis的安装与配置、数据类型操作、shell命令实践。还探讨了Redis的持久化策略RDB和AOF,以及主从、哨兵和集群部署模式。最后讨论了Redis在缓存击穿、雪崩和穿透等问题的解决方案,以及面试中常见的Redis问题。
摘要由CSDN通过智能技术生成

hadoop实时day26–redis


一、今日内容大纲
1、nosql发展和介绍
2、redis介绍 安装
3、redis数据类型
4、redis操作
	shell(课堂上教学)
	jedis java api(自己练习)
5、redis持久化策略
	RDB
	AOF
6、redis集群部署模型
7、redis 缓存击穿 缓存雪崩  缓存穿透

二、Nosql数据库发展历史
1、Nosql介绍

https://www.runoob.com/mongodb/nosql.html

2、RDBMS vs NoSQL
#RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务

#NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
- 键值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性	
3、常见的nosql数据库
类型部分代表特点
列存储Hbase Cassandra Hypertable顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。
文档存储MongoDB CouchDB文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。
key-value存储Tokyo Cabinet / TyrantBerkeley DB Memcache Redis可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。
图存储Neo4J FlockDB图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。
对象存储db4o Versant通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。
xml数据库Berkeley DB XMLBaseX高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。

二、Redis基本介绍和使用场景

image-20210404181351956

1、redis基本概念
redis是REmote DIctionary Server(Redis) 。
是开源免费的,用C语言编写的软件。
是一个高性能(key/value)分布式内存数据库,基于内存运行并支持数据持久化的nosql数据库。
是当前最热门Nosql数据库之一。 
2、redis特点
redis和其他kv类型内存数据库相比:

1、redis支持数据持久化 重启的时候可以再次加重进行使用;
2、redis支持的数据类型丰富。支持string  hash  set  list  zset等数据结构存储。
3、redis支持数据备份。可分布式运行提高读写并发。
3、redis使用场景
#1、热点数据缓存  缓存服务器
	redis速度快、支持数据类型丰富
#2、限时业务的使用
	redis数据支持TTL 超时被自动删除。  优惠券活动信息、手机验证码等业务场景。
#3、取最新的N个数据、排行榜相关	
	配合list类型做数据最新的N个
	配合zset有序集合实现排行榜功能
#4、定时器、计数器
	redis支持原子性操作  incrby  decrby递增递减需求。
	比如:限制一个手机发多少短信  限制一个接口被调用多少次  验证码被发送几次。
	银行输入密码错误5次锁定账号

三、Redis安装
  • redis中英文官网

    https://redis.io/
    http://www.redis.cn/  #推荐把redis的中文官方作为文档使用
    
  • 安装包

    redis-4.0.2.tar.gz
    
  • 安装目录

    mkdir -p /export/servers
    mkdir -p /export/software
    mkdir -p /export/logs
    mkdir -p /export/data
    
  • 解压源码包

    cd /export/servers
    
    tar zxvf redis-4.0.2.tar.gz 
    
    mv redis-4.0.2 redis-src
    
  • 编译安装

    #yum在线安装依赖
    yum -y install gcc gcc-c++ libstdc++-devel tcl
    
    #编译安装
    cd /export/servers/redis-src/
    make MALLOC=libc
    make PREFIX=/export/servers/redis install
    
    #安装的路径是由上述PREFIX 指定的  注意和源码位置区分开
    
  • 配置文件

    mkdir -p /export/servers/redis/conf
    cd /export/servers/redis/conf
    vi redis_6379.conf
    
    #不要忘了创建redis存储数据本地文件夹路径
    mkdir -p /export/data/redis/6379/
    
    #redis 配置中需要修改的地方 其他保持默认
    bind node-1
    daemonize yes
    logfile "/export/data/redis/6379/log.log"
    dir /export/data/redis/6379/
    
  • redis启动和关闭

    #redis 启动
    cd /export/servers/redis
    
    bin/redis-server conf/redis_6379.conf
    
    #查看redis是否启动成功 进程
    ps -ef |grep redis
    
    #redis客户端
    [root@node-1 redis]# bin/redis-cli -h node-1
    node-1:6379> ping
    PONG
    
    
    #服务端和客户端关闭
    客户端退出:
    第一种:强制退出 Ctrl+c
    第二种: quit
    服务端退出:
    第一种: ./redis-cli  -h 192.168.72.141 shutdown
    第二种: kill -9
    
  • 测试redis读写性能

    在把redis服务启动成功之后  执行下面命令
    bin/redis-benchmark -h node-1
    
    #可以通过日志看出不同类型数据的读写请求速度
    103950.10 requests per second
    

四、Redis的数据类型

image-20210404184532008

  • redis数据类型很强大

    1、说redis的数据类型 准确来说讲的是redis中value的数据类型 redis是kv类型数据库
    2、五大常见类型
    	string  list  set  zset  hash
    
  • 类型数据操作–重点掌握实际中用的多的。

    redis命令手册:
    http://www.redis.cn/commands.html
    
    公益网站:
    https://www.runoob.com/redis/redis-keys.html
    

五、shell命令操作Redis
1、String类型
set k1 v1 #如果k1不存在 新增;如果k1已经存在,更新。
get k1    #获取k1所对应value

mset k1 v1 k2 v2 k3 v3...#一次设置多个kv对
mget k1 k2 k3....# 一次获取多个k所对应的值
node-1:6379> mget id name age
1) "10086"
2) "allenwoon"
3) "18"

#设置kv的时候 可以添加上TTL时间限制
SETEX key seconds value    #给指定key设置超时时间 单位是s
PSETEX key milliseconds value#给指定key设置超时时间 单位是ms 毫秒

#递增递减操作
incr key #对value进行++操作
decr key #对value进行--操作
incrby key 数字 #对value进行增加 增加指定数字的大小
decrby key 数字 #对value进行减少 减少指定数字的大小

	递增递减操作 如果value不是数字类型字符串 直接报错
    node-1:6379> get name
    "allenwoon"
    node-1:6379> incr name
    (error) ERR value is not an integer or out of range
    node-1:6379> 

#追加操作
APPEND key value #如果value是字符串 可以将内容追加到尾部
    node-1:6379> set name allen
    OK
    node-1:6379> get name
    "allen"
    node-1:6379> APPEND name woon
    (integer) 9
    node-1:6379> get name
    "allenwoon"
2、Hash类型
#hash类型可以理解为java中hashmap kv映射。
#但是redis本身已经有了key 为了避免重名 field value。
key    value
k1     field1 v1
       field2 v2 
       field3 v3
#hash类型特别适合存储对象数据。
node-1:6379> hset person name allen
(integer) 1
node-1:6379> hset person age 18
(integer) 1
node-1:6379> hset person city beijing
(integer) 1
node-1:6379> hset person hobby ball
(integer) 1

#根据key、field获取对应的值
HGET key field

#hkeys 根据指定key获取value中所有的field
node-1:6379> hkeys person
1) "name"
2) "age"
3) "city"
4) "hobby"

#获取所有的value的值
node-1:6379> HVALS person
1) "allen"
2) "18"
3) "beijing"
4) "ball"

#判断指定field是否存在
HEXISTS key field 
node-1:6379> HEXISTS person country
(integer) 0	#表示不存在
node-1:6379> HEXISTS person city
(integer) 1 #表示存在

#HLEN key 获取哈希表中有多少个kv对
node-1:6379> HLEN person
(integer) 4

#HDEL key field1 field2  删除一个或者多个field

3、List类型
#list类型可以看成是java中 Linklist 队列。最大的特点FIFO,先进先出。
#是一个双向都可以操作的队列  Left Right 

LPUSH key v1 v2 v3 #从左侧添加数据
RPUSH key v1 v2 v3 #从右侧添加数据

    node-1:6379> LPUSH list1 a b d c e
    (integer) 5
    node-1:6379> LRANGE list1 0 -1
    1) "e"
    2) "c"
    3) "d"
    4) "b"
    5) "a"
    node-1:6379> RPOP list1
    "a"
    node-1:6379> RPOP list1
    "b"
    node-1:6379> RPOP list1
    "d"
    node-1:6379> RPOP list1
    "c"
    node-1:6379> RPOP list1
    "e"
    node-1:6379> RPOP list1
    (nil)


--如果想使用list模拟队列的功能  可以从左侧添加数据 从右侧取出数据
LPOP #移除并且返回 key 对应的 list 的第一个元素。
	redis> RPUSH mylist "one"
    (integer) 1
    redis> RPUSH mylist "two"
    (integer) 2
    redis> RPUSH mylist "three"
    (integer) 3
    redis> LPOP mylist
    "one"
    redis> LRANGE mylist 0 -1
    1) "two"
    2) "three"
    redis> 

RPOP #从右侧弹出一个元素

#查看list的数据 LRANGE key start stop查看list当中所有的数据示例
LRANGE list1 0 -1  # 下标从0开始 -1表示倒数第一个元素下标 

#LINDEX key index 通过索引获取列表中的元素 从0开始
	redis> LPUSH mylist "World"
    (integer) 1
    redis> LPUSH mylist "Hello"
    (integer) 2
    redis> LINDEX mylist 0
    "Hello"
    redis> LINDEX mylist -1
    "World"
    redis> LINDEX mylist 3
    (nil)
    redis>
    
#LSET key index value 通过索引设置列表元素的值
        node-1:6379> LRANGE list2 0 -1
        1) "a"
        2) "b"
        3) "ww"
        4) "d"
        5) "e"
#LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
        redis> RPUSH mylist "Hello"
        (integer) 1
        redis> RPUSH mylist "World"
        (integer) 2
        redis> LINSERT mylist BEFORE "World" "There"
        (integer) 3
        redis> LRANGE mylist 0 -1
        1) "Hello"
        2) "There"
        3) "World"
        redis> 
#阻塞获取操作
BLPOP k1 timeout #从指定的列表取出左边第一个元素,如果该列表没有元素 客户端会阻塞 直到超时时间结束或者发现可用元素为止。
BRPOP k1 timeout

#发送一串字符串进行验证的时候,如果没有发送则一直被阻塞
4、set类型
#和java中set一样 特点:去重 无序
#redis中set集合是string类型的无序集合 集合成员是唯一的,
#使用业务场景:去重

SADD key member1 member2 #向集合添加一个或多个成员
SCARD key #返回集合存储的key的基数 (集合元素的数量)
		redis> SADD myset "Hello"
        (integer) 1
        redis> SADD myset "World"
        (integer) 1
        redis> SCARD myset
        (integer) 2
        redis> 
SMEMBERS key #显示set中所有元素
        redis> SADD myset "Hello"
        (integer) 1
        redis> SADD myset "World"
        (integer) 1
        redis> SMEMBERS myset
        1) "World"
        2) "Hello"
        redis> 
SISMEMBER key member #判断 member 元素是否是集合key的成员
        redis> SADD myset "one"
        (integer) 1  #如果member元素是集合key的成员,则返回1
        redis> SISMEMBER myset "one"
        (integer) 1
        redis> SISMEMBER myset "two"
        (integer) 0  #如果member元素不是key的成员,或者集合key不存在,则返回0
        redis> 
SPOP key # 随机的删除一个元素 并返回这个元素
		SADD myset "one"
        SADD myset "two"
        SADD myset "three"
        SPOP myset
        SMEMBERS myset
        SADD myset "four"
        SADD myset "five"
        SPOP myset 3
        SMEMBERS myset
SREM key member1 member2 #移除集合中一个或多个成员示例
5、zset类型(stored set)
#去重 有序(内部有自己的排序规则 根据double分数进行排序的)
stored set也是string类型的元素集合,且不允许重复。
不同于set的是,每一个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中成员进行排序的 从小到大排序。
集合元素一定不重复唯一,但是分数可以重复。

#业务场景:排行榜 排序去重的业务

ZADD key score1 member1 [score2 member2] #向有序集合添加一个或多个成员,或者更新已存在成员的分数
        node-1:6379> ZADD booktop 97 java
        (integer) 1
        node-1:6379> ZADD booktop 100 bigdta
        (integer) 1
        node-1:6379> ZADD booktop 95 python
        (integer) 1
        node-1:6379> ZADD booktop 60 php
        (integer) 1
ZRANGE key start stop [WITHSCORES] #通过索引区间返回有序集合指定区间内的成员
                           #返回的元素可以认为是按得分从最低到最高排列。 如果得分相同,将按字典排序。
         node-1:6379> ZRANGE booktop 0 -1 withscores
            1) "php"
            2) "60"
            3) "python"
            4) "95"
            5) "java"
            6) "97"
            7) "bigdta"
            8) "100"                      
                           
ZREVRANGE key start stop [WITHSCORES]  #通过索引区间返回有序集合指定区间内的成员
			#其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典序的反序排列。
		node-1:6379> ZREVRANGE booktop 0 -1 withscores
            1) "bigdta"
            2) "100"
            3) "java"
            4) "97"
            5) "python"
            6) "95"
            7) "php"
            8) "60"
  
ZREVRANK key member  
#返回有序集key中成员member的排名,其中有序集成员按score值从大到小排列。排名以0为底,也就是说,score值最大的成员排名为0。
		node-1:6379> ZREVRANK booktop java
		(integer) 1

ZRANK key  member 
#返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底,也就是说,score值最小的成员排名为0。
        node-1:6379> ZRANK booktop java
        (integer) 2
        
#有序集合中元素分数的修改操作
ZINCRBY key increment member #有序集合中对指定成员的分数加上增量 
        node-1:6379> ZINCRBY booktop 2 python
        "97"

#返回有序集key中,成员member的score值。
node-1:6379> ZSCORE booktop java
"101"

#移除元素
zrem key memvber .....
6、key通用操作命令
#redis中所有的索引都是从0开始的

#显示当前数据库所有的key
	keys * 
	key 支持查找所有符合给定模式pattern(正则表达式)的 key 。
#以秒为单位返回指定的key剩余过期时间
	ttl key  
        当key不存在的时候,返回-2;
        当key存在但是没有设置ttl,返回-1
        当key存在且设置了ttl,返回的是剩下的时间  以s为单位
#删除指定的key	
	DEL key 

#给指定的key设置ttl时间 以秒为单位
	expire key seconds
	
#移除key过期超时时间
	PERSIST key
	注意:要在ttl时间结束之前移除 否则到时间该key就被删除了

#查看指定key所对应的value数据类型
    node-1:6379> TYPE list1
    list
    node-1:6379> TYPE set1
    set
    node-1:6379> TYPE name
    string
    node-1:6379> TYPE myset:__rand_int__
    hash
#判断key是否存在
    node-1:6379> EXISTS hhahaah
    (integer) 0 #表示不存在
    node-1:6379> EXISTS name
    (integer) 1 #表示存在
    
#select 编号(0-15):切换数据库
	redis内部默认情况下有16张数据库 标号从0开始。默认情况下使用0号库。可以使用selet 选择不同的数据库。 数据库之间彼此都是隔离的。
	
node-1:6379[1]> SELECT 4   #可以通过【数字】来判断当前操作的是哪一个数据库
OK
node-1:6379[4]> SELECT 0
OK
node-1:6379> 

六、java操作Redis—jedis
  • 使用jedis操作redis

    redis官方针对java语言提供了多套api,这里推荐使用jedis。其最大的特点就是各种数据类型操作方法和shell命令行中是一样的  会极大减少学习的成本。
    
    https://github.com/xetorthio/jedis
    
  • 重点:jedis链接池 封装jedis链接池工具类

  • 创建工程 依赖问题

        <dependencies>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>2.9.0</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.9</version>
            </dependency>
        </dependencies>
    
  • jedis入门案例

         * todo jedis入门案例
         *  1、创建jedis对象  在对象中需要指定redis ip  端口
         *  2、执行相关操作
         *  3、是否资源 关闭连接
         */
    
        @Test
        public void JedisTestOne(){
            //1、创建jedis对象  在对象中需要指定redis ip  端口
            Jedis jedis = new Jedis("node-1", 6379);
            //2、执行相关操作
            String pong = jedis.ping();
            System.out.println(pong);
            //是否资源 关闭连接
            jedis.close();
        }
    //此种方式会频繁和redis进行连接的创建和关闭  不适合开发 开发中推荐使用jedis链接池
    
  • jedis链接池

        /**
         * todo jedis 链接池使用
         * 1、创建链接池对象  需要指定redis ip  端口
         * 2、从链接池中获取链接 Jedis对象
         * 3、执行相关操作
         * 4、是否资源 把链接返回池子中
         */
        @Test
        public  void JedisTestTwo(){
            //1、创建链接池对象  需要指定redis ip  端口
            //创建链接池配置对象 用于指定链接池相关属性
            JedisPoolConfig config = new JedisPoolConfig();
            JedisPool jedisPool = new JedisPool(config,"node-1", 6379);
    
            //2、从链接池中获取链接 Jedis对象
            Jedis jedis = jedisPool.getResource();
    
            //3、执行相关操作
            String pong = jedis.ping();
            System.out.println(pong);
            //4、是否资源 把链接返回池子中
            jedis.close();
        }
    //直接写链接池 同样不适合开发环境 通常要把这一段逻辑封装成一个工具类JedisUtil
    
  • jedisUtl工具类封装

    //要求 1、工具类所创建的链接池全局唯一 单例 static 随着类的加载被加载创建 只创建一次
    //2、能够在工具类中提供一个统一的方法 获取redis的链接
    /**
     * @author: Allen Woon
     */
    public class JedisUtils {
        //保证该成员变量无法被直接访问使用 私有的
        private static JedisPool jedisPool;
        //保证链接池对象全局唯一 单例 随着类的加载被创建 且只创建一次
        static {
            //创建链接池配置对象 用于指定链接池相关属性
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxIdle(5);//闲时最大的数量
            config.setMaxTotal(100);//最大的链接数量
            config.setMinIdle(3);//闲时最小的数量
            jedisPool = new JedisPool(config,"node-1", 6379);
        }
    
        //从链接池中获取链接的方法
        public static Jedis getJedis(){
            return jedisPool.getResource();
        }
    }
    

七、Redis持久化策略
作为内存中Nosql数据库 redis支持把数据内存的数据持久化到磁盘中 避免断点丢失问题。

redis提供了两种持久化方案:RDB、AOF
1、RDB(redis database)
#在进程RDB持久化的时候 redis会fork出一个子进程专门用于持久化。
#首先将数据写入一个临时文件 持久化结束之后 再用这个临时文件去替换上次持久化的文件。整个过程主进程进行任何操作,确保了极高的性能。

fork的作用是复制一个跟原进程完全一样的进程,新进程中数据状态、环境变量等跟原进程一样。但是是一个全新的进程,并作为原进程的子进程运行。

#缺点:最后一次RDB容易产生数据丢失。如果要求是大规模数据恢复 并且要求精度不高。可以选用rdb

#默认RDB是开启的
save 900 1  : 在900秒之内,如果有一个数据进行修改,就会执行一下保存
save 300 10  : 在300秒之内, 如果有10个以上的数据被修改, 就会执行一下保存
save 60 10000 : 在60秒之内, 如果有10000个以上的数据被修改. 就会执行一下保存

dbfilename dump.rdb #持久化文件名称
dir /export/data/redis/6379/#保存的路径

#如果需要关闭rdb 把save参数注释掉  给save命令设置一个空字符串
save ""

#相关的其他参数
stop-writes-on-bgsave-error yes #如果持久化save失败 前端停止写操作
rdbcompression yes #是否开启压缩 LZF
rdbchecksum yes #持久化完毕是否进行数据校验 
2、AOF(append only file)
#redis提供基于日志机制实现的持久化技术。会将用户的操作完整的记录下来保存追加到文件中。当服务器重启 的时候 根据日志记录重演操作 恢复数据。 还支持后端对AOF文件进行重新避免该文件体积过大。

#默认不开启aof
appendonly yes #是否开启 默认不开启
appendfilename "appendonly.aof" #文件的名称
appendfsync everysec # [always everysec no]

#优点:可以将用户所有操作记录下来 数据保存完整 不容易丢失
#缺点:持久化文件过大 不利于恢复
  • 如果两种策略都开启 默认先AOF 精度高。
  • 注意:如果把redis当成缓存数据库来使用。可以不开启持久化技术

image-20210404224429567

image-20210404233446057

image-20210404234546574

image-20210404234643668


八、Redis部署架构
1、单机架构
2、主从架构(master/slave
  • 解决:解决读写分离。

  • 概念

    俗称主从复制架构。主要职责就是为了实现读写分离。
    主机数据更新之后根据配置、策略,自动同步到从机上。
    
    master负责写,slave负责读。
    
  • 如何配置 配从不配置主

    #配从不配置主
    在从机上添加一行参数
    slaveof 主ip 端口
    
  • 安装部署

    #1、把安装包scp给其他两台机器
    scp -r redis/ root@node-2:$PWD
    scp -r redis/ root@node-3:$PWD
    
    #2、在node-2 node-3创建一些文件夹  日志 数据
    mkdir -p /export/logs
    mkdir -p /export/data/redis/6379/
    
    #3、修改node-2 node-3上redis配置文件
    bind 主机名		# 千万别忘了
    slaveof 主ip 端口  #复制哪台机器
    
    #4、启动3台redis
    
    #5、验证从机无法进行写操作
    node-2:6379> set hobby1 ball
    (error) READONLY You can't write against a read only slave.
    
3、sentinel架构(哨兵模式)
  • 背景

    主从复制架构中 解决读写分离 但是master是单点故障。如何产生master高可用?
    所谓的master挂掉之后 能够选举出来一个新的master。继续领导大家对外提供服务。
    
  • 概念

    哨兵模式是一种特殊的模式。
    哨兵是一个独立的进程。会独立运行。通过发送命令检测redis进程健康问题。
    哨兵系统通常由多个哨兵进程组成,可以去监视主机和各个从机。并在主机下线出现故障的时候,自动将从机中某个选举成为新的master。
    
  • 哨兵作用

    1、监视redis服务器健康状态。包括监视主机和从机。
    2、master切换选举问题。然后通过发布订阅功能把消息通知给各个从机,修改配置文件,切换主机。
    
  • 配置(3台机器都需要配置哨兵)

    cd /export/servers/redis/conf
    vim sentinel.conf
    
    # 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,node-1代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
    
    #修改bind配置,每台机器修改为自己对应的主机名
    bind node-1 
    #配置sentinel服务后台运行
    daemonize yes
    #修改三台机器监控的主节点,现在主节点是node01服务器
    sentinel monitor mymaster node-1 6379 2
    
  • 启动哨兵进程 3台机器都需要启动

    cd /export/servers/redis/bin/
    ./redis-sentinel ../conf/sentinel.conf
    
  • jedis中如何添加哨兵信息

     	// 哨兵信息
        Set<String> sentinels = new HashSet<>(Arrays.asList("node01:26379",
            "node02:26379","node03:26379"));
        // 创建连接池
        JedisSentinelPool pool = new JedisSentinelPool("mymaster",
    sentinels,jedisPoolConfig);
    
4、redis cluster集群模式
  • 背景

    当数据量过大的时候,大到一台服务器存储不下。不管是主从架构还是哨兵架构都无法解决数据存不下的问题。
    这时候就需要对存储数据进行拆分,将数据部署存储在多台机器上。
    
    cluster集群模式就应运而生。
    
  • 概念

    redis cluster集群模式是redis水平扩容,部署N台机器,将所有的数据分布存储在各个机器上。
    cluster集群模式像是主从架构和sentinel结合体。cluster内部可以实现主从复制和master重新选举机制。
    
  • 特点

    多个redis之间互联 数据共享。
    要求所有的节点必须是一主一从(也可以一主多从)。其中从不提供服务的,仅作为备用。
    不支持同时处理多个key  比如:mset  mget。因为redis需要把key分布式存储在不同机器上,并发量很高的情况下,同时操作key效率不高。
    支持在线增加节点  删除节点。
    客户端可以连接到任何一个节点上操作,内部自动跳转。
    
  • 内部细节

    • 存储数据的时候如何分配?

      redis集群中内置了16384个哈希槽(坑位),均匀分布在3台机器上。
      	node-1:0-5000
      	node-2:5001-10000
      	node-3:10001-16383
      当放置一个key的时候。首先对key通过一个算法CRC16算法计算一个值。
      	CRC16(key) % 16384 =余数  对应着哈希槽。
      
    • 如何实现容错?

      投票过程是整个集群中所有master实例参与。 如果半数master与某个节点通信失败 就任务那个节点出现故障了把他的备机切换成为主。
      
    • 什么整个集群不可用

      1、如果任意一个master挂掉 但是其没有slave 集群进行失败状态。
      2、如果集群中超过半数master挂掉 整个集群进入失败状态。
      
  • cluster安装部署(伪分布式 单机使用不同的端口模拟集群模式)

    redis集群搭建至少需要3主3从。需要6个redis实例。
    这里在一台机器上进行伪分布式安装,使用不同的端口来启动redis.
    跟着讲义 心细。
    
    在使用客户端连接的时候 一定要加上-c参数 可以自动在集群多台机器间跳转。
    

九、Redis面试必问问题

image-20210404235035746

Redis作为缓存服务器的使用流程

   前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
1、缓存穿透
 描述:
	缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。缓存不起作用,穿透缓存,直捣DB.

解决方案:
	布隆过滤器
	接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
	从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
	采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

image-20210404235218956

2、缓存雪崩
描述:
	 缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案:
	缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
	如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
	设置热点数据永远不过期。

image-20210404235435790

3、缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

(1)设置热点数据永远不过期。

雪压枝头低,虽低不着迷,一朝红日出,依旧与天齐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章鱼哥TuNan&Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值