Redis学习笔记

Redis笔记

此笔记部分根据狂神说redis部分视频记录,感谢狂神说!下面为视频路径:https://www.bilibili.com/video/BV1S54y1R7SB?t=951&p=35

一:简介
nosql

​ no only sql 不仅仅是sql
​ 一般称之为非关系型数据库
​ web2.0存在一些问题:(3高)
​ 1.高并发
​ 2.大数据
​ 3.高扩展,高可用
​ 分类:
​ key/value的格式
​ 文档型格式
​ 列类型
​ 图

redis

redis是使用c语言开发的一个高性能键值对的数据库,可以用作数据库、缓存和消息中间件
支持的数据类型(五大基础类型,三种特殊类型)

​ String(★) 字符串
​ hash(理解) 散列
​ list 列表
​ set 集合
​ sortedSet(zset) 有序集合

​ geospatial 地理空间

​ hyperloglog 范围查询

​ bitmaps

默认16个数据库,默认0号

select 3 #切换数据库
DBSIZE	#查看数据库的数据量
keys *	#查看数据库锁有的key
flushall #清空所有数据库
flushdb #清空当前数据库

redis是单线程的(6.0以后支持多线程),官方表示redis是基于内存操作的,redis的瓶颈是根据计算机的内存和网络宽带。

  1. 多线程(CPU上下文切换:耗时)不一定比单线程快。redis是将所有数据放在内存中的,所以使用单线程去操作效率就是最高的。多次读写都是在一个cpu上的。
二:安装
1.下载redis
2.上传到linux
3.安装redis	
	mkdir /usr/local/redis
	mv /root/redis.tar /usr/local/redis
	cd /usr/local/redis
	tar -xvf redis.tar
4.编译redis 依赖 gcc
	yum install gcc-c++
	make
5.安装redis
	make PREFIX=/usr/local/redis install
6.配置
	复制一个redis.conf 到bin目录下
	启动服务器的方式1:	
		前台启动的方式:
			cd /usr/local/redis/bin
			./redis-server redis.conf
		后台的方式:
			配置一下redis.conf
				修改:daemonize yes
			保存退出
	启动客户端
		简单的方式:
			./redis-cli    #连接本地端口号为 6379的服务器
		推荐的方式
			./redis-cli -h 连接ip -p 端口号
			
redis的停止
	方式1:通过kill -9 进程号(不推荐)
	方式2:通过客户端发送命令
		./redis-cli -h ip -p port shutdown

性能测试:redis-benchmark.exe

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

三:基本操作方式
  1. string
    • 赋值:格式: set key value
      例如:set username tom

    • 取值:格式: get key
      例如: get username

    • 先获取再设置:getset key value,如果不存在值,返回nil
      例如: getset username jack

    • 删:del key
      例如: del d

    • 了解

      • 对于数字类型 自增和自减

        incr key ++

        decr key –

      • 增加或减少指定的数量

        incrby key int

        decrby key int

      • 拼接字符串,如果当前key不存在,就相当于set key value

        append key value

      • 字符串范围

        getrange key1 0 3 设置key1的值

        getrange key1 0 -1 相当于get key1

        setrange key2 1 xx 替换指定位置开始的字符串

      • setex(set with expire)设置过期时间

        setex key3 30 hello

      • setnx(set if not exist)如果不存在设置,在分布式锁中使用

        setnx mykey redis

      • 批量设置

        mset key1 v1 key2 v2 key3 v3

        mget key1 key2

        msetnx k4 v4 key1 v1 mset是原子性的操作,要么一起成功,要么一起失败

        user:{id}:{filed}

        mset user:1:name shangsan user:1:age 2

        mget user:1:name user:1:age

  2. list
    • 赋值:
      左边:lpush key value value2 value3
      右边:rpush key value value2 value3

    • 取值:
      左边:lpop key
      右边:rpop key

    • 获取所有元素
      lrange key 0 -1

    • 获取元素的个数
      llen key

    • 扩展:
      lpushx key value :若有则添加 若没有则不添加
      rpushx key value :若有则添加 若没有则不添加

      		lrem key count value:从左边移除count个value
      			若count>0 :从左边移除count个value
      			若count<0 :从右边移除count个value
      			若count=0 :从右边移除所有的value
      		
      		lset key index value
      			设置链表中指定索引的元素值  0 代表是第一个
      				-1代表的是最后一个
      
  3. hash:
    • 了解
      • 存入一个map集合
        user username tom
        age 18
        sex 1
      • 存值:
        存入一个值
        hset key subkey subvalue
        存入多个值
        hmset key subkey1 subvalue1 subkey2 subvalue2
      • 获取:
        获取一个值
        hget key subkey
        获取多个值
        hmget key subkey1 subkey2
      • 移除值:
        hdel key subkey subkey
        给一个key添加指定的数字
        hincrby key subkey int
  4. set
    • 添加
      sadd key value1 valuse2
    • 删除
      srem key value1 valuse2
    • 获取
      smembers key
    • 判断是否是是set中的一员
      sismember key value
    • 运算
      差集: sdiff s1 s2
      交集: sinter s1 s2
      并集: sunion s3 s4
    • 获取数量
      scard key
      srandmember key:随机获取一个
  5. sortedSet
    • 添加元素
      zadd key score m1 score m2
    • 获取元素
      zscore key m:获取指定成员的得分
      zcard key:获取key的长度
    • 删除元素
      zrem
  6. 通用的操作
    • keys * :查看所有的key
    • del key:删除指定的key
    • exists key:判断一个key是否存在
    • rename oldkey newkey:重命名
    • expire key 秒数:
      ttl key :查看一个key剩余存活时间
      -1:持久存活
      -2:不存在
    • type 判断一个可以属于什么类型
  7. 发布订阅
    • subscribe channel 订阅给定的频道的信息
    • psubscribe pattern 订阅一个或多个符合给定模式的频道
    • unsubscribe channel 指退订给定的频道
    • unpsubscribe [pattern] 退订所有给定模式的频道。
    • publish channel message 将信息发送到指定的频道
  8. 事务

    Redis单条命令是保存原子性的,但是事务不保证原子性。redis事务没有隔离级别的概念,redis事务的本质是一组命令的集合,一个事务中的所有命令都会被序列化,在发起命令后,才会按照顺序执行。一致性、顺序性、排他性

    • 简介:

      • 批量操作在发送 EXEC 命令前被放入队列缓存。
      • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
      • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
    • 命令:

      • multi 开始事务
      • exec 执行事务
      • discard 回滚
      127.0.0.1:6379[3]> multi
      OK
      127.0.0.1:6379[3]> set k1 v1
      QUEUED
      127.0.0.1:6379[3]> set k2 v2
      QUEUED
      127.0.0.1:6379[3]> get k2
      QUEUED
      127.0.0.1:6379[3]> set k3 v3
      QUEUED
      127.0.0.1:6379[3]> exec
      1) OK
      2) OK
      3) "v2"
      4) OK
      
    • 编译型异常(命令出错),所有事务都不会被执行

    • 运行时异常(如:0/1),其他的命令可以正常执行,错误命令抛出异常

    监控:

    • 悲观锁:认为什么时候都会出问题,无论做什么都会加锁
    • 乐观锁:认为什么时候都不会出现问题,不会上锁,更新数据时去判断在此期间是否有人修改过这个数据。获取version,更新时比较version
    #正常
    127.0.0.1:6379[3]> set money 100
    OK
    127.0.0.1:6379[3]> set out 0
    OK
    127.0.0.1:6379[3]> watch money
    OK
    127.0.0.1:6379[3]> multi
    OK
    127.0.0.1:6379[3]> decrby money 20
    QUEUED
    127.0.0.1:6379[3]> incrby out 20
    QUEUED
    127.0.0.1:6379[3]> exec
    1) (integer) 80
    2) (integer) 20
    
    #多线程修改时,使用watch可当作乐观锁操作
    127.0.0.1:6379[3]> watch money
    OK
    127.0.0.1:6379[3]> multi
    OK
    127.0.0.1:6379[3]> decrby money 10
    QUEUED
    127.0.0.1:6379[3]> incrby out 10
    QUEUED
    #在执行前,另外一个线程修改了值,会导致执行失败
    127.0.0.1:6379[3]> exec
    (nil)
    #解决办法
    127.0.0.1:6379[3]> unwatch	#先解锁,再获取锁
    OK
    127.0.0.1:6379[3]> watch money
    OK
    
  9. 连接命令
    • AUTH password 验证密码是否正确
    • ECHO message 打印字符串
    • PING 查看服务是否运行
    • QUIT 关闭当前连接
    • SELECT index 切换到指定的数据库
  10. geospatial地理位置,可以推算地理位置的信息,底层原理zset
    • geoadd 将指定的地理空间位置(纬度【-180度到180度】、经度【-85.05112878度到85.05112878度】、名称)添加到指定的key中,两极无法添加。一般会下载城市数据,直接通过java程序一次性导入。

      127.0.0.1:6379[3]> geoadd china:city 120.15 30.28 hangzhou
      (integer) 1
      127.0.0.1:6379[3]> geoadd china:city 113.62 34.75 zhengzhou
      
    • geopos获取当前定位

      127.0.0.1:6379[3]> geopos china:city beijing tianjin
      1) 1) "116.39999896287918"
         2) "39.900000091670925"
      2) 1) "117.19999998807907"
         2) "39.120000488192183"
      
    • 两个位置之间的直线距离

      127.0.0.1:6379[3]> geodist china:city beijing tianjin km
      "110.6313"
      
    • georadius:以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

      127.0.0.1:6379[3]> georadius china:city 110 30 1000 km
      1) "hangzhou"
      2) "zhengzhou"
      

    georadiusbymember:指定成员的位置被用作查询的中心。

    127.0.0.1:6379[3]> georadiusbymember china:city beijing 1000 km
    1) "zhengzhou"
    2) "tianjin"
    3) "beijing"
    

    geohash:返回一个或多个位置元素的 Geohash (11位)表示。

    底层原理:zset

    127.0.0.1:6379[3]> zrange china:city 0 -1
    1) "hangzhou"
    2) "zhengzhou"
    3) "tianjin"
    4) "beijing"
    127.0.0.1:6379[3]> zrem china:city tianjin
    (integer) 1
    127.0.0.1:6379[3]> zrange china:city 0 -1
    1) "hangzhou"
    2) "zhengzhou"
    3) "beijing"
    
  11. hyperloglog:数据结构,作基数统计的算法,如网页的UV(一个人访问一个网站多次,还算作一个人)

    基数:不重复的元素,允许容错

    127.0.0.1:6379[3]> pfadd mykey a d b  d d s k	#创建
    (integer) 1
    127.0.0.1:6379[3]> pfcount mykey	#统计数量
    (integer) 5
    127.0.0.1:6379[3]> pfadd mykey2 k i y g p w v
    (integer) 1
    127.0.0.1:6379[3]> pfmerge mykey3 mykey mykey2	#合并,并集
    OK
    127.0.0.1:6379[3]> pfcount mykey3
    
  12. bitmaps:位图,数据结构,非0即1

    位运算,可用作打卡、签到等场景

    127.0.0.1:6379[3]> setbit sign 0 0	#设置
    (integer) 0
    127.0.0.1:6379[3]> setbit sign 1 1
    (integer) 0
    127.0.0.1:6379[3]> setbit sign 2 1
    (integer) 0
    127.0.0.1:6379[3]> getbit sign 2	#获取某个index的值
    (integer) 1
    127.0.0.1:6379[3]> bitcount sign	#统计记录
    (integer) 2
    
四:Jedis

Jedis是官方推荐的java连接开发工具,

  1. 导入对应的依赖

    
    <dependency>
        <groupId>redis.clientsgroupId>
        <artifactId>jedisartifactId>
        <version>3.2.0version>
    dependency>
    
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>fastjsonartifactId>
        <version>1.2.62version>
    dependency>
    
  2. 编码测试

    • 连接数据库

    • 操作命令

    • 断开连接

      public static void main(String[] args) {
          Jedis jedis = new Jedis("127.0.0.1", 6379);
          System.out.println(jedis.ping());
          
      }
      
    • API与命令相似

    • 例:事务

      public static void main(String[] args) {
          Jedis jedis = new Jedis("127.0.0.1", 6379);
      
          jedis.flushDB();
      
          JSONObject jsonObject = new JSONObject();
          jsonObject.put("hello", "world");
          jsonObject.put("name", "kuangshen");
      
          Transaction multi = jedis.multi();
          String s = jsonObject.toJSONString();
          try {
              multi.set("user1", s);
              multi.set("user2", s);
              int i = 1 / 0;
              multi.exec();
          } catch (Exception e) {
              multi.discard();
              e.printStackTrace();
          } finally {
              System.out.println(jedis.get("user1"));
              System.out.println(jedis.get("user2"));
              jedis.close();
          }
      
      
      }
      
五:spring boot整合

springboot操作数据:spring-data, jpa, mongodb, redis

SpringBoot2.x以后,jedis被改成了lettuce
jedis:采用的直连,多个线程操作不安全,若要避免,则要使用jedis pool连接池,更像Bio模式
lettuce:采用netty,可以在多个线程中共享,不存在线程不安全的情况,可以减少线程数量,更像Nio模式
  • 源码

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")//不存在就生效
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
          throws UnknownHostException {
        //默认的RedisTemplate没有过多的设置,redis对象需要序列化
        //两个泛型都是Object,需要强制转换
       RedisTemplate<Object, Object> template = new RedisTemplate<>();
       template.setConnectionFactory(redisConnectionFactory);
       return template;
    }
    
    @Bean
    @ConditionalOnMissingBean	//由于string类型是最长使用的类型,所以单独提取出来的
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
          throws UnknownHostException {
       StringRedisTemplate template = new StringRedisTemplate();
       template.setConnectionFactory(redisConnectionFactory);
       return template;
    }
    
  • 导入依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
  • 配置连接

    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    
  • 测试及编码

  • @Autowired
    private RedisTemplate redisTemplate;
    
    @Test
    void contextLoads() {
        //opsForValue操作string
        //opsForList list
        //opsForHyperLogLog HyperLogLog
        //除了基本的操作,常用的方法都可以直接通过RedisTemplate操作,比如事务
        //清空数据库通过connection操作
        /*RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushDb();*/
    
        redisTemplate.opsForValue().set("mykey", "关注狂神说Java的公众号");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }
    
  • 序列化配置,默认为JdkSerializationRedisSerializer。自定义序列化配置网上查询。

    @Nullable
    private RedisSerializer keySerializer = null;
    @Nullable
    private RedisSerializer valueSerializer = null;
    @Nullable
    private RedisSerializer hashKeySerializer = null;
    @Nullable
    private RedisSerializer hashValueSerializer = null;
    

    若不序列化,会报错SerializationException

六:Redis config

配置文件redis.windows.conf

  1. 大小写不敏感

  2. 包含,INCLUDES,

  3. NETWORK,网络

    bind 127.0.0.1	#绑定IP、端口号
    port 6379
    protected-mode yes	#以守护线程的方式运行,默认为no
    
  4. GENERAL

    # This can be one of:
    # debug (a lot of information, useful for development/testing)
    # verbose (many rarely useful info, but not a mess like the debug level)
    # notice (moderately verbose, what you want in production probably)生产环境使用
    # warning (only very important / critical messages are logged)
    loglevel notice
    
    logfile ""	#日志文件位置名
    databases 16	#数据库数量
    
  5. SNAPSHOTTING,快照

    持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb, .aof

    redis是内存数据库,若没有持久化,就会断电即失

    #持久化规则,如果xxxs内,至少有xxx个key进行修该,进行持久化操作
    save 900 1
    save 300 10
    save 60 10000
    
    stop-writes-on-bgsave-error yes	#持久化出差后,是否还进行工作
    rdbcompression yes	#是否压缩rdb文件,需要消耗CPU资源
    
    rdbchecksum yes	#保存rdb检查
    dir ./	#文件保存目录
    
  6. REPLICATION配置主从复制,具体查看

  7. SECURITY,安全,redis密码两种方式,配置文件修该或下述方式。

    127.0.0.1:6379> config get requirepass	#查看密码
    1) "requirepass"
    2) ""
    127.0.0.1:6379> config set requirepass "123456"	#设置密码
    OK
    127.0.0.1:6379> ping
    (error) NOAUTH Authentication required.
    127.0.0.1:6379> auth 123456	#登录
    OK
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) "123456"
    
  8. LIMITS,服务端限制

    # maxclients 10000
    # maxmemory 
    # maxmemory-policy noeviction	内存达到上限后的策略
    	# volatile-lru -> remove the key with an expire set using an LRU algorithm
    	# allkeys-lru -> remove any key according to the LRU algorithm
    	# volatile-random -> remove a random key with an expire set
    	# allkeys-random -> remove a random key, any key
    	# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
    	# noeviction -> don't expire at all, just return an error on write operations
    
  9. APPEND ONLY MODE,aof配置

    appendonly no	#默认不开启,使用rdb方式
    appendfilename "appendonly.aof"
    
    # appendfsync always	#每次修改后同步,消耗性能
    appendfsync everysec	#每秒执行一次
    # appendfsync no		#不执行同步
    
七:持久化

redis是内存数据库,若没有持久化,就会断电即失。在主从复制中,rdb是在从机上备用的,aof几乎不使用

rdb:redis database

RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。redis会单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时rdb文件中,待持久化过程结束后,再用这个临时文件替换上次持久化好的文件,成为正式的rdb文件,子线程退出。缺点是最后一次持久化后的数据可能丢失。

文件默认是dump.rdb

触发机制:

  1. save的规则满足的条件下
  2. 执行flushall命令
  3. 退出redis

恢复rdb文件:只需将rdb文件放在redis启动目录即可。

aof:append only file

将所有数据都记录下来,在恢复时会重新把所有命令都执行一遍。以日志的形式来记录每个写操作。只需要将appendonly no #默认不开启,使用rdb方式改为yes,然后重启即可生效。

文件默认是appendonly.aof,若aof文件被破坏,则会启动失败。可使用redis-check-aof进行修复,会直接将错误命令删除

redis-check-aof --fix appendonly.aof

当同时开启两种持久化方式,重启时会优先载入aof文件来恢复原始数据。因为rdb只用作后备,所以一般只用save 900 1即可。

八:主从复制

是指将一台redis服务器的数据,复制到其他redis服务器,前者称为master(主节点),后者称为从节点(slave/follower),数据的复制是单向的,只能从主节点到从节点,主节点只能写,从节点只能读。默认情况下,每台主节点都是主节点,且一个主节点可以有多个从节点,一个从节点只能有一个主节点。一个集群至少有三个服务器,一主二从。

作用:

  1. 数据冗余
  2. 故障恢复
  3. 负载均衡
  4. 高可用(集群)基石

环境配置:

只配置从库,不配置主库。

127.0.0.1:6379> info replication
# Replication	查看当前的信息
role:master
connected_slaves:0	#从机
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

#复制redis的配置文件,对端口号、pidfile、logfile、dbfilename进行更改
cp redis.conf redis79.conf
cp redis.conf redis80.conf
cp redis.conf redis81.conf

#更改完成后用配置文件启动,并用ps -ef|grep redis查看启动线程
redis-server kconfig/redis79.config
redis-server kconfig/redis80.config
redis-server kconfig/redis81.config

一主二从

slaveof 127.0.0.1 6379	#在从机上配置从机的主机地址
info replication	#查看主机信息

上述命令行配置的主从复制时暂时的,真实的主从配置应该在配置文件中配置

# slaveof  

# masterauth 

细节

主机可以写,从机只能读,主机中的所有数据都会被从机保存。若主机shutdown,从机依旧是从机,主机重新启动后,从机会自动连接上主机,能获取到主机写入的新主机。

如果使用命令行配置主从复制,当redis重新启动后,会默认恢复成主机,所以,当从机shutdown后,需要重新配置。

复制原理

slave启动成功连接到master后,会发送sync同步命令,master接到命令,会启动存盘进程,将整个数据文件传送给slave,并完成一次同步。

  • 全量复制:slave接到数据库文件后,将其存盘并加载到内存中
  • 增量复制:master继续将新的收集到的修改命令依次传送给slave,完成同步

只要重新连接到主机,一次全量复制将被执行。

主机–>从机/主机–>从机方式(层层链路)

配置完成之后中间的redis依旧是从节点,当主节点shutdown后,需要进行如下配置

slaveof no one	#让自己变成主机

如果第一个主节点恢复了,就需要重新配置。哨兵模式不需要配置上述步骤。

九:哨兵模式

redis2.8+提供了Sentinel(哨兵)架构。能够后台监控主机是否故障,如果发生故障,根据投票数自动将从库转换为主库

。通过发送命令,等待redis服务器响应,从而监控多个redis实例。使用多个哨兵进行监控,形成多哨兵模式。

如果主服务器宕机,哨兵1先检测到这个结果,系统不会马上进行投票(主观下线),当其他哨兵也检查到并数量达到一定后,哨兵就会发起投票,进行故障转移,切换成功之后,就会通过发布订阅模式,实现票数最多的从机切换主机(客观下线)。

配置sentinel.config

#主机名称 端口号 宕机时,从机投票
sentinel monitor myredis 127.0.0.1 1

#启动哨兵
redis-sentinel kconfig/sentinel-conf

如果主机恢复之后,只能归并到新的主机下,当作从机。

  1. 哨兵集群,基于主从复制模式
  2. 主从可以切换,故障可以转移,是主从模式的升级,系统的可用性更好
  3. 集群数量一旦达到上限,在线扩容十分麻烦。
  4. 哨兵模式的配置麻烦,可以在网络上查询
十:缓存穿透和雪崩

服务的高可用问题

缓存穿透(查不到)

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

解决方案

  1. 布隆过滤器

    是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先校验,不符合丢弃,从而避免了底层存储系统的查询压力。

  2. 缓存空对象

    当存储层不命中后,即使返回空对象也会被存储起来,同时设置一个过期时间,之后访问这个数据会直接从缓存中获取,保护后端数据源。

    • 如果空值能被缓存起来,这就意味着缓存需要更多的空间存贮更多的键,因为可能会有很多空值的键。
    • 即使对空值设置了过期时间,还是会存在存储层和缓存层的数据会有一段时间的不一致,这对于需要保证一致性的业务会有影响
缓存击穿(量太大,缓存过期)

概念

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案

  1. 设置热点数据永不过期

  2. 加互斥锁

    分布式锁,使用分布式锁,保证每个key同时只有一个线程去查询后端服务,其他的线程没有获得分布式锁的权限,因此只需要等待即可。这种方式把高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

缓存雪崩

概念

缓存雪崩是指,缓存层出现了错误,不能正常工作了(redis宕机,缓存集中过期失效等)。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

解决方案

  1. redis高可用

    多设置几台redis,搭建redis集群。(异地多活)

  2. 限流降级

    这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

  3. 数据预热

    数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

sh形式存储,在控制层先校验,不符合丢弃,从而避免了底层存储系统的查询压力。

  1. 缓存空对象

    当存储层不命中后,即使返回空对象也会被存储起来,同时设置一个过期时间,之后访问这个数据会直接从缓存中获取,保护后端数据源。

    • 如果空值能被缓存起来,这就意味着缓存需要更多的空间存贮更多的键,因为可能会有很多空值的键。
    • 即使对空值设置了过期时间,还是会存在存储层和缓存层的数据会有一段时间的不一致,这对于需要保证一致性的业务会有影响

你可能感兴趣的:(redis)