Redis

1、Redis的认知

为什么要使用Reids?

1、是一个独立的数据缓存,存储数据库中的数据

2、提升用户获取数据的效率

是一个中间件技术:介于tomcat服务和数据库服务之间

数据库的分类:

1、sql数据库:mysql、oracle、sqlserver、db2等

通过sql语句进行操作

2、nosql数据库:redis

  • Redis就是一款NoSQL。

  • NoSQL -> 非关系型数据库 -> Not Only SQL。

  • Key-Value:Redis。。。

  • 文档型:ElasticSearch,Solr,Mongodb。。。

  • 面向列:Hbase,Cassandra。。。

  • 图形化:Neo4j


2、Redis的特点:

1、nosql数据库

2、key-value存储形式

3、内存存储数据

4、支持持久化

5、支持集群(适用于高并发)


3、Redis服务的搭建及访问

 

 

 

 也可以使用Redis-cli.exe 工具

 

 


4、Redis常用的五大数据类型

1、string:以字符串的形式存储数据

【说明】单个字符、多个字符、整数、小数都是string

【扩展】研究Redis的string类型的底层是如何实现的?

2、list: value值可以为多个,使用逗号分隔,有顺序,可以重复

3、hash: 可以存储对象的属性名以及属性名所对应的属性值

4、set: value值可以为多个,使用逗号分隔,无序、唯一

5、zset/sortedset: value值可以为多个,使用逗号分隔,有序、唯一

顺序:按score分数排序

【补充】

  • HyperLogLog:计算近似值的。

  • GEO:地理位置。

  • BIT:一般存储的也是一个字符串,存储的是一个byte[]。


5、Redis的各种命令

命令文档地址:Redis 命令参考 — Redis 命令参考

Redis中的数据根据存活时间分为:

1、永久的数据---默认就是永久的

2、易失的数据---在存入的时候设置了存活时间,过了存活时间,Redis就会删除掉对应的数据

1、select 库名      进入到对应的库下

2、set  key  value     给key起名 并赋值

3、get key             获取到key中所有的值

key: 

4、del key1 key2                   删除key及key中所包含的值

 如果后边输入一个不存在的key 例如nofoundstring 并不耽误删除存在的key 

5、exists key                              查看是否有这个key 有返回1 无返回0

 

6、expire key  second                      将key设成有效期,key必须存在   second为秒     

 10秒后get stu2 就为nil了  nil在redis中表示为空

7、pexpire key  millisecond    将key设成有效期,key必须存在    millisecond为毫秒

 5000毫秒也就是5秒后  stu1就为nil了  

8、keys *                                             查询当前库中一共有哪些key

 

 所选值的前后如果有值 或者所选值为前后任意一端都要在有数据那边加上*

包含指定值(例:s)的

9、move key 库名                               将当前库下指定的key移到其他库中

10、expire key second         设定存活时间 变为易失的 以秒为单位  key值必须先存在

11、ttl key                                                        查看剩余的存活时间

12、persist key                                                 将经过expire设定的key改为永久存活

-1为永久存活

13、randomkey                                                    随机获取一个key

 14、rename  oldkey newkey                                          给key命名一个新名

 

如果rename改名改成了该库中已经有的名还是返回ok但是会把原来存在的那个删除,只留下这个rename 改的key

15、renamenx oldkey newkey                   如果要改的名已经存在就不会进行修改保持原有

16、type key                                                        返回该key所对应的类型

String:

17、append key 值                                                    在原有值的基础上追加值

 

  如果需要加空格那就加双引 如果不需要加空格就不用加索引  key值如果没有就会创建一个

18、decr key                                                               将值进行减一

只针对int类型

19、incr key                                               将值进行加一

 

 只针对int类型

20、decrby key num                             将key中的值减num

只针对于int类型

21、incrby key num                            将key中的值加num                               

 只针对于int类型

22、incrbyfloat key float                将key中的值增加float

 只有增加才有 decr不能减少float类型

23、set                                               赋值

set key value 什么也不加默认为永久有效 

set key value EX second      创建的存活second秒

set key value PX millisecond   创建的key存活millisecond毫秒

NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。

XX :只在键已经存在时,才对键进行设置操作。

  

24、get key                                        取值

 只能针对于string类型的进行取值

25、mset key1 value1 key2 value2 ...            多个key-value进行赋值

26、mget key1 key2 ...                                                对多个key进行取值 

 

27、msetnx  key1 value key2 value ...   

添加多个key中有一个key存在所有的key都拒绝添加否则能添加  

28、setex key secod value              对key添加有效时间

 对key的存活事件进行设置有两种方式

29、setnx  key value                        当key不存在的时候会添加成功 

30、setrange key index value

第一位从0开始算将原先的值从index位进行替换,替换成value

31、strlen  key                          返回该字符串中元素的个数

       

32、getset key value                  给key赋一个新值,但是返回的是原来的 再查是新的

Hash(经常用来存对象):

33、hset key name value      

给key进行复赋值 第一个为名 第二个为值 都在key下

 

34、hget key name                      获取redis代表的key中 里边的Object Key里的值

35、hdel key name                     删除key中的name以及value

36、hexists key name                 查询key下是否有这个name有返回1没有返回0

37、hgetall key           获取该key下所有的name和所对应的value(一个key一个value)

38、hincrby key name num         将key下name对应的值加num

只是int类型才可以增加

39、hincrbyfloat key name float                将key下name对应的值加float        

 只是int类型才可以增加

40、hkeys key                                           将key下所有的name获取出来

41、hlen key                         将key下的name个数进行返回 

42、hmset key name1 value1 name2 value2 ...        存储多个对象

43、hmget key name1 name2 ...                               获取多个对象的值

k

44、hsetnx key name value          当key不存在时才可以创建且只能创建一个name1

45、hvals key                                       查询出当前key下的所有value值

List:

46、lpush key value1 value2 ...   给list集合进行赋值依次在左侧也就是前边进行赋值 

47、lrange key indexStart  indexStop                         获取list集合中的值

 

indexStart  0  就是第一个

indexStop  -1 就是之后的所有一直到最后一个 

index是从0开始的

48、rpush key value1 value2...    给list集合进行赋值依次在右侧也就是后边进行赋值 

 

49、lpop key              从集合左侧的第一个开始删除

 

50、rpop key             从集合右侧的第一个开始删除

 

 

52、rpoplpush oldkey  newkey           把oldkey的最后一个放到newkey的第一个

 

 newkey没有时可以创建

53、lpushx key value                           当key存在的情况下在头部添加value

 

 

54、rpushx key value                           当key存在的情况下在尾部添加value

55、lindex key index                               找当前key下对应的索引所存在的值 

 index 0 就是第一个   -1是最后一个

56、linsert key before|after oldvalue newvalue    在原有value值的 前|后 添加值

after  之后

before 之前

oldvalue没有的话插入不进去

57、llen key                                                       查看当前key下有几个值

58、lrem key num value             key中value从前往后删除num个

 

负数

0

 

正数是从前往后找   负数是从后往前找  0 所有的value都删除掉

59、lset key index newvalue             将key中的index值修改成newValue 

 index是从0开始的   -1是最后一个

60、ltrim key startindex   stopindex       保留key中 startindex到stopindex中的值

 startindex从0开始  stopindex最后为-1

Set(唯一 无序):

61、sadd key value1 value2 ...         向key中添加value value的值不可以重复且无序

 重复的值就不往里添加

 

 无序

62、smembers key                              查询key中的值

 

63、scard key                                      返回key中元素的个数

64、sdiff key1 key2                            返回key1中key2没有的数据

65、sdiffstore key key1 key2             将key1中key2没有的数据返回到key中 

key要是没有就创建  key要是有的话就将以前的值进行替换

66、sinter key1 key2                        返回key1中key2有的数据

67、sinterstore key key1 key2        将key1和key2共有的数据返回到key中

68、sismember key value       判断key中是否有value有的话返回1没有的话返回0

69、smove key1 key2 key1value        将key1中的值key1value放到key2中           

将key2有的话往里添加没有的话就覆盖

70、spop key                                     将key中的值随机删除掉一个

71、srandmember key                       随机获取key中的一个值

是获取不是删除 

72、srem key value                           移除key中的value

73、sunion key1 key2                      返回key1中和key2中的并集 

74、sunionstore key key1 key2            将key1和key2的并集返回到key中

ZSet/SortedSet(唯一 有序  通过score 根据score排序):

75、zadd key score1 value1 score2 value2 ...    把score1 value1 ...添加进key中

76、zrange key index1 index2                               查询key中inedx1到index2中的值 

只查询value值

 index1 从0开始

index2 最后为-1

77、zrange key index1 index2 withscores   

        查询key中index1到index2中的值以及score

  根据分数进行排序

78、zcard key                    查询key中的元素个数

79、zcount key minscore maxscore   

查询key中minscore到maxscore范围内的元素个数


6、Java操作Redis

往redis中赋值

package com.qf.redispro.Test;

import redis.clients.jedis.Jedis;

public class Test1 {
    public static void main(String[] args) {
        //1、创建和Redis服务的连接
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2、选择要操作的库
        jedis.select(6);
        //3、存入字符串
        jedis.set("javastu","张三");
        //4、关闭资源
        jedis.close();
        System.out.println("写入Redis成功!");
    }
}

从redis中往外取值

package com.qf.redispro.Test;

import redis.clients.jedis.Jedis;

public class Test2 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.select(6);
        String javastu = jedis.get("javastu");
        System.out.println("从java中获取的数据:" + javastu);
        jedis.close();
    }
}

存取对象

package com.qf.redispro.Test;

import com.qf.redispro.pojo.Dept;
import redis.clients.jedis.Jedis;

import java.util.HashMap;

public class Test3 {
    public static void main(String[] args) {
        Dept dept = new Dept(1001,"软件开发部","北京市海淀区");
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.select(6);
        //单个字段存储
        jedis.hset("dept1","deptNo",String.valueOf(dept.getDeptNo()));
        HashMap<String,String> map = new HashMap<String,String>();
        map.put("dName", dept.getDName());
        map.put("loc",dept.getLoc());
        jedis.hmset("dept1",map);

        //逐个读取
        System.out.println("读取信息如下:");
        System.out.println("部门编号:"+jedis.hget("dept1","deptNo"));
        System.out.println("部门名称:"+jedis.hget("dept1","dName"));
        System.out.println("部门位置:"+jedis.hget("dept1","loc"));

        //关闭资源
        jedis.close();
    }
}

byte类型存取图片视频

package com.qf.redispro.Test;

import com.qf.redispro.pojo.Dept;
import org.springframework.util.SerializationUtils;
import redis.clients.jedis.Jedis;

public class Test4 {
    public static void main(String[] args) {
        //图片、音频、视频等
        //以byte[]形式存取数据
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //默认使用索引为0的库
        Dept dept = new Dept(1001,"软件测试部","北京市朝阳区");
        String key = dept.getDName();

        //把key value 转换为 byte[]
        byte[] byteKey = SerializationUtils.serialize(key);
        byte[] byteValue = SerializationUtils.serialize(dept);

        //存储
        jedis.set(byteKey,byteValue);

        ///


        //取出数据
        //获取key
        String getKey= dept.getDName();
        //将key转换为byte类型
        byte[] byteGetKey = SerializationUtils.serialize(getKey);

        //利用转换后的key值形式读取
        byte[] byteAry = jedis.get(byteGetKey);

        //强转成对象
        //将byte类型的转换成对象类型
        Dept dept2 = (Dept) SerializationUtils.deserialize(byteAry);
        System.out.println("读取到的内容如下:" + dept2.toString());
    }
}

json格式存储数据

package com.qf.redispro.Test;

import com.alibaba.fastjson.JSON;
import com.qf.redispro.pojo.Dept;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        //以json格式存储数据
        List<Dept> list = new ArrayList<Dept>();
        Dept dept1=new Dept(1001,"软件开发部A","设计城906A");
        Dept dept2=new Dept(1002,"软件开发部B","设计城906B");
        Dept dept3=new Dept(1003,"软件开发部C","设计城906C");
        Dept dept4=new Dept(1004,"软件开发部D","设计城906D");
        Dept dept5=new Dept(1005,"软件开发部E","设计城906E");
        list.add(dept1);
        list.add(dept2);
        list.add(dept3);
        list.add(dept4);
        list.add(dept5);

        //创建redis连接
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //选择库
        jedis.select(7);
        String key = "depts";
        //转json
        String value = JSON.toJSONString(list);
        //存储
        jedis.set(key,value);
        System.out.println("存入成功!");
        jedis.close();
    }
}

json格式取出数据

package com.qf.redispro.Test;

import com.alibaba.fastjson.JSON;
import com.qf.redispro.pojo.Dept;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.List;

public class Test6 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.select(7);
        //获取到key中的数据
        String strJsonValue = jedis.get("depts");
        //输出
        //json转成集合类型
        List<Dept> list = JSON.parseArray(strJsonValue,Dept.class);
        //遍历输出
        for (Dept dept:list) {
            System.out.println(dept);
        }
        jedis.close();
    }
}

封装Redis连接池创造工具类

package com.qf.redispro.util;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import sun.net.www.content.text.Generic;

public class JedisUtil {
    //声明Jedis连接池对象
    private static JedisPool jedisPool=null;
    //在静态代码块中编写连接池的配置信息
    static{
        //创建连接池的配置对象
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        //配置连接池
        //连接池中最大活跃数量
        poolConfig.setMaxTotal(100);
        //最大空闲数量
        poolConfig.setMaxIdle(10);
        //最小空闲数量
        poolConfig.setMinIdle(5);
        //等待超时时间
        poolConfig.setMaxWaitMillis(3000);
        //创建连接池对象
        jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379);
    }
    //获取连接方法
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
    //关闭连接
    public static void closeJedis(Jedis jedis){
        jedis.close();
    }
}

设置有效期

package com.qf.redispro.Test;

import com.qf.redispro.util.JedisUtil;
import redis.clients.jedis.Jedis;

public class Test7 {
    public static void main(String[] args) {
        //通过连接池来获取连接
        Jedis jedis = JedisUtil.getJedis();
        jedis.select(7);
        //设置数据的有效期
        jedis.expire("depts",2);
        System.out.println("设置成功");
        //关闭连接
        JedisUtil.closeJedis(jedis);
    }
}

使用管道命令

package com.qf.redispro.Test;

import com.qf.redispro.util.JedisUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public class Test8 {
    public static void main(String[] args) {
        //管道技术的使用-----提升操作效率
        //1、获取链接
        Jedis jedis = JedisUtil.getJedis();
        jedis.set("javaNum", "0");

        //开始计时
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            //每次循环单独发送一个命令
            jedis.incr("javaNum");
        }
        long endTime = System.currentTimeMillis();

        //关闭当前的连接
        JedisUtil.closeJedis(jedis);

        //使用管道技术,批量发送命令
        jedis = JedisUtil.getJedis();

        //创建管道
        Pipeline pipeline = jedis.pipelined();
        jedis.set("javaNum2","0");

        //重新计时
        long beginTime2 = System.currentTimeMillis();
        for (int i=0;i<=100000;i++){
            //把发送给Redis的命令,转而发送给管道即可
            pipeline.incr("javaNum2");
        }
        //把管道里的命令提交给Redis
        pipeline.syncAndReturnAll();

        //获取结束时间
        long endTime2 = System.currentTimeMillis();

        System.out.println("在未使用管道的情况下,用时:" + (endTime-beginTime));
        System.out.println("在使用管道情况下,用时:" + (endTime2-beginTime2));
    }
}

Reids整合MyBatis

package com.qf.redispro.service.impl;

import com.alibaba.fastjson.JSON;
import com.qf.redispro.dao.DeptDao;
import com.qf.redispro.pojo.Dept;
import com.qf.redispro.service.DeptService;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.util.List;

@Service
public class DeptServiceImpl implements DeptService {

    @Resource
    DeptDao deptDao;


    @Override
    public List<Dept> findDept() {

        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.select(11);
        List<Dept> list = null;
        String str = jedis.get("depts");

        if(str==null||str.equals("")){
            list = deptDao.findDept();
            jedis.set("depts",JSON.toJSONString(list));
            jedis.close();
            return list;
        }

        list = JSON.parseArray(str,Dept.class);
        return list;
    }
}


7、Redis中的配置

1、添加密码

进行配置文件的编写

通过redis.cli.exe执行时,先要通过auth命令输入密码  

 

 图形化桌面工具

2、事务

理解:并不是数据库事务的要么都执行、要么都不执行

Redis中的事务:把多个操作命令,放在一个队列中,通过exec命令执行事务的时候,一起执行队列中的命令

1、multi:开启事务

2、开启事务后,命令都会存放到队列中

3、exec 一起执行队列中的命令

discard 取消队列中的命令

watch:观察某个key 在开启事务之前执行,如果别的客户端也watch这个Key,当前事务自动取消

3、Redis的集群

集群:保障Redis服务高可用(即时发生单点故障,依然可以提供缓存服务)

提升了数据请求处理的能力

Redis集群所带来的问题:例如集群中有10台服务器

1、存入数据到某一台服务器,集群中如何同步数据?

2、访问的时候,访问集群中的那一台?

3.1 主从集群(生产环境不用)      

搭建原则:一台主服务器---写入数据

多台从服务器---读取数据

分析:1、读写分离,从服务器会从主服务器拉取、同步数据

2、主服务器就一台,发生单点故障后,无法写入数据

结论:在生产环境中,不会采用主从结构的集群

搭建过程:

1、准备4个服务器:1主3从

2、主服务器主要配置了密码

3、配置从服务器---端口

                              ---服从于主服务器的信息、主服务器的密码

4、启动即可

3.2  哨兵集群

原理:在主从集群的基础上,为集群中的每一台服务器都添加了一个哨兵,哨兵的职责是监控主服务器,当主服务器宕机,哨兵会投票选举,在从服务器中选举出新的主服务器,其他从服务器从新选举的主服务器上拉取、同步数据(自动故障迁移)。

图解:

        

 

 

1、搭建主从集群

2、在每个Redis服务的目录下,添加哨兵配置文件sentinel.conf

【概念】

主观宕机:一个哨兵认为主服务器宕机,不一定是真正宕机,有可能是网络延迟的原因

客观宕机:达到一定数量的哨兵认为主服务器宕机,认为是真正宕机,进行failover(自动故障迁移)

sentinel.conf

port 26382
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel config-epoch mymaster 12
sentinel leader-epoch mymaster 13

3、在每个服务下编写哨兵服务的启动文件

startup-sentinel.bat

title sentinel-26379   //在窗口显示标题

redis-server.exe sentinel.conf --sentinel

4、启动哨兵集群

先主 后从 先主哨兵后从哨兵

哨兵集群原理解析:

每个哨兵每隔10秒获取当前集群中的服务的拓扑结构图

每个哨兵每隔2秒通过主服务器发布、订阅哨兵自己监控的主、从服务器的情况,以及哨兵自身的信息

每个哨兵每隔1秒会向所监控的主服务器发送ping命令(心跳检测),主服务器需要在规定时间(在哨兵的配置文件中sentinel down-after-milliseconds mymaster 5000进行的配置)内返回pong

一个哨兵检测到主服务器宕机,称为主观宕机

如果达到一定数量的哨兵认为主服务器宕机,称为客观宕机

发生客观宕机后,就要进行投票选举

每个哨兵通过发送is-master-down-by-addr命令,征询其他哨兵的同意,称为新的主服务器

其他哨兵,可以同意,也可以拒绝,也可以发送is-master-down-by-addr命令

当某个哨兵同意的数量> (哨兵数量/2+1)的时候,哨兵所在的服务器就称为新的主服务器,就可以完成自动故障迁移

如果没有选举出新的主服务器,就继续投票选举

3.3:cluster集群

只能应用在Linux系统下,多主多从结构,有多个主服务器,每个主服务器对应着一个从服务器

至少:3主3从

原理:去中心化,当主服务器宕机后,从服务器自动升级为主服务器,当主服务器重启后,自动降级为从服务器

在写入数据时,由于有多个主服务器,通过哪个主服务器写入呢?

引入了hash槽的算法


8、Redis的持久化机制

Redis和Memcache对比,Redis支持持久化,而Memcache不支持

通过持久化,把内存中的数据进行存储,当Redis服务重启后,自动从持久化文件中加载数据,无需进行缓存预热

支持3种方式的持久化:

1、rdb快照方式持久化,默认支持的方式

原理:1、由Redis fork()出一个子进程,由子进程进行持久化

           2、每次在符合时间条件时进行持久化,都会创建一个新的dump.rdb持久化文件

           3、用新的dump.rdb持久化文件去覆盖掉旧的文件

           4、快照方式,只是在符合条件的那一刻的数据进行了持久化,在本次持久化到下次持久化                   之间,Redis中新增的数据可能会丢失

优点:不去抢Redis的资源,由子进程进行的持久化

效率高

缺点:丢一部分数据

2、aof只追加文件方式持久化

aop持久化,一直在一个文件中通过追加文件的方式进行持久化,文件会越来越大,效率越来越低

缺点:效率低

优点:不丢任何数据

3、rdb、aof同时进行持久化

同时开启RDB和AOF的注意事项:

如果同时开启了AOF和RDB持久化,那么在Redis宕机重启之后,需要加载一个持久化文件,优先选择AOF文件。

如果先开启了RDB,再次开启AOF,如果RDB执行了持久化,那么RDB文件中的内容会被AOF覆盖掉。


9、Redis的淘汰策略

缓存预热:把热点数据存入Redis

问题:内存中的数据已经存满,再存数据的时候,就需要首先从内存删除数据,称为数据淘汰

key的生存时间到了,Redis会立即删除吗

        

不会立即删除。

  • 定期删除:Redis每隔一段时间就去会去查看Redis设置了过期时间的key,会再100ms的间隔中默认查看3个key。

  • 惰性删除:如果当你去查询一个已经过了生存时间的key时,Redis会先查看当前key的生存时间,是否已经到了,直接删除当前key,并且给用户返回一个空值。

数据淘汰的策略:

 在redis.windows.conf配置文件中

lru:最近,使用时间最少的key----强调的是使用时长

lfu:最近,使用频次最少的key----强调的是使用次数


10、Redis的常见问题及解决方案

1、缓存穿透

工具类方法:

public static String getData(String type,String key){
    //判断是何种类型,用对应的方法都Redis中获取指定key所对应的值
}

获取数据的思路:

第一步:到Redis中获取

第二步:如果到Redis中获取不到,则到数据库中获取。

第三步:把从数据库中查询的数据,存入Redis

问题:恶意的传递不存在的key,发出大量的获取数据的请求,大量的请求对应key的数据,缓存未命中,导致大量的请求到达数据库,从而把数据库请求瘫痪,这个问题称为缓存穿透

归纳:请求的数据在Redis中没有,数据库也没有

缓存穿透的解决方案:

1、缓存空值,把请求的key 使用空值进行缓存,如果下次还是用同样的key,那么请求就不会穿透,从而达到数据库了

缺点:可能会缓存大量的空值

2、布隆过滤,把Redis中所有的key,存入到一个集合里,当请求Redis数据的时候,判断请求的key是否在集合中,如果在集合中,则获取数据,如果不在则直接返回

缺点:存放Key的集合需要实时维护

如果查询的key不是热点输入,也没有道数据库中查询,直接返回空

2、缓存击穿

问题描述:请求的key,在Redis中有存在值,在某一时刻,大量的key同时过期了,如果此刻,有大量的请求去请求过期的key,请求就穿透Redis到达数据库服务器。

解决方案:

1、热点数据设置为永久有效

2、把热点数据的过期时间,分散设置,避免大量的数据在同一时间点过期

3、缓存雪崩

问题描述:Redis集群都不能正常提供服务了,导致大量的请求直接到达数据库

解决:确保集群的稳定性

4、缓存倾斜

问题描述:请求的热点数据,并没有平均分布在Redis集群的各个节点上,而是分布在某个或某几个节点上,如果大量的热点数据请求到达,可能造成倾斜的节点的宕机

解决方案:集群一定要合理

哨兵集群:热点数据的分布比较合理

cluster集群:key的hash值在hash槽中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值