Welcome to 发呆鱼.|

发呆鱼

园龄:3年4个月粉丝:1关注:0

redis 入门学习笔记

1 发展

单机mysql

image-20220209111539313

瓶颈:

  • 数据量太大
  • 数据的索引,一个机器内存放不下
  • 访问量,一个机器承受不了

Memcached + mysql +垂直拆分

优化数据结构和索引---->文件缓存------> Memcached

image-20220209112010675

分库分表 水平拆分 mysql集群

image-20220209112626033

现在的一个互联网项目

image-20220209114233013

为什么是NoSQL(not only sql)

如用户个人信息,社交网络,地理位置,这些数据类型的存储不需要一个固定的格式,方便横向扩展。

NoSql 特点

  • 解耦
  • 方便扩展,数据库之间没有关系,很好扩展
  • 大数据量 高性能
  • 数据类型是多样的,不需要事先设计数据库

传统rdbms 和 nosql

rdbms:

  • 结构化组织
  • sql
  • 数据和关系都存在单独的表中
  • 严格的一致性
  • 基础的事务

nosql:

  • 不仅仅是数据
  • 没有固定的查询语句
  • 键值对存储,列存储,文档存储,图形数据库
  • 最终一致性
  • cap定理和base

3v + 3高·

3v:

  • 海量 volume
  • 多样 variety
  • 实时 velocity

3 高:

高并发

高可拓

高性能

NoSQL 分类

KV 键值对

  • 新浪: redis
  • 美团: redis+tair
  • 阿里,百度: redis memecahe

文档行数据库

  • mongoDB

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • Neo4j
  • InfoGrid

2 redis 入门

2.1 概述

百度百科

​ Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

​ Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings)散列(hashes)列表(lists)集合(sets)有序集合(sorted sets) 与范围查询, bitmapshyperloglogs地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication)LUA脚本(Lua scripting)LRU驱动事件(LRU eviction)事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

中文官网:redis中文官方网站: http://www.redis.cn/

redis 能做什么

  1. 内存存储,持久化(rdb,aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器,计数器(浏览量)

2.2 安装

ubuntu 下

sudo apt-get install redis-server 安装redis

配置文件在
/etc/redis/ 下

sudo redis-server <配置文件的路径> 通过配置文件启动 不加配置文件则使用默认

2.3 基础知识

DBSIZE  查看数据库大小


select index 使用数据库

keys * 查看数据库中所有的key

flushdb 情况当前数据库

FLUSHALL 情况全部数据内容

EXISTS key 判断key 是否存在 存放返回1 不存在返回0

set key value 设置键值对
get key  获取值

 move key db 移除键值对
 
 EXPIRE key seconds 设置键值多久后过期
 
 ttl key 查看键值剩余时间
 
 type key 查看类型
 
 更多命令: http://www.redis.cn/commands.html

redis 是单线程的

redis 是基于内存操作的,cpu 不是redis 性能瓶颈,redis 的瓶颈是机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。

为什么redis单线程还那么快。

redis 是将全部的数据都放在内存中的,而在计算机中 cpu速度 > 内存读写> 磁盘读写。在使用多线程时,会进行线程调度,从而会对内存进行读写。使用单线程就减少了频繁的线程调度,所以使用多线程会效果会好一点。

3 redis 数据类型

3.1 5大数据类型

string

 APPEND key value  想key的值后面添加字符串 ,如果不存在就相当于 set key value
 
 STRLEN key 查看长度
 
 incr key  == +1
 decr key  == -1
 
 INCRBY key increment 增加步长
 DECRBY key decrement 减少步长
 
 
 getrange key  start end 获取范围内字符
 
 setrange key  offset value 替换指定位置字符
 
  setex key seconds value 如果存在 设置多少秒后失效
  
  setnx key value 如果不存在就设置,如果已经存在就返回0,不会覆盖
  
  mset k1 v1 k2 v2 k3 v3 一次设置多个值
  mget k1 k2 k3 一次获取多个
  
  msetnx x x x x   原子性操作,要成功全部成功,要失败全部失败
  
mset user:1:name fadaiyu user:1:age 12  #设置对象
mget user:1:name user:1:age
  
 getset key value 先获取再设置
 
 

list

 lpush key value 将一个值或者多个值插到列表的头部(左边)
 
 lrange key start stop 从列表中按范围取值
 
  rpush key value 将一个或者多个值插入到列表右边
  
  lpop 从左边移除
  rpop 从右边移除
  
  lindex key index 按序号取值
  
  llen key 获取列表长度
  
  lrem key count value 从列表中移除指定个数指定的值
  
  ltrim key start end 截取指定部分
  
  rpoplpush source key 从原来列表中右边取出一个放到新列表中
  
  lset key index value 设置列表中index 位置的值
  
   linsert key BEFORE|AFTER pivot value 将值插入到列表中某个元素的前面或者后面
   
   

set

集合,里面的值不能重复

sadd key member ,,, 向集合中添加值

smembers key 查看集合

sismember key member 判断集合中是否包含某值

scard key 查看集合中元素个数

srem key member 移除集合中元素

srandmember key count 随机抽出指定个数的元素

spop key 随机删除元素

smove source destination member 将指定元素移动到另外一个集合中

sdiff key1 key2 求key1 中有而key2 中没有的元素

sinter key key 求交集

sunion key key 求并集


hash

hash 更适合对象的存储
hset key field value 向哈希表中存入一个键值对
hget key field 获取

hmset key field value [field value [.....] ] 一次存入多个值
hmget key field field

hgetall key 获取全部

hdel key filet 删除哈希中指定键值对

hlen key 或许哈希表字段个数

hexists key filet 判断哈希中指定字段是否存在

hkeys key 获取哈希中所有filet
hvals key 获取哈希中所有的值

hincrby key field increment

 hsetnx key field value

Zset 有序集合

zadd key index value 


 zrangebyscore grade -inf +inf withscores 从小到大排序 -inf 无限小 +inf 无穷大  withscores 返回带值
 
 zrem key field 移除元素
 
 zcard key 获取集合中个数
 
 zcount key min max 计算区间个数
 
 
 zrevrange key start stop [WITHSCORES] 从高到低

3.2 三个特殊数据类型

geospatial

geoadd key longitude latitude member [longitude latitude member ...]  添加地理位置

geopos key member [member ...] 获取某个或者多个地点的坐标值

 geodist key member1 member2 [unit] 两地之间距离
 m:米
 km:千米
 mi: 英里
 ft 英尺
 
 georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC
 查询指定坐标附件多远范围内的坐标
 
 georadiusbymember 
 
  geohash key member 将二维的坐标返回为一维的字符串
  
geospatial 底层使用zset 
可以使用zset 命令对其操作


hyperloglog

占用的内存是固定的

可以用来做网页uv

pfadd key member [member ....] 添加

pfcount key 计数

pfmerge destkey sourcekey [sourcekey ...] 合并两组元素


bitmaps

统计用户信息,活跃,不活跃。登录,未登录。 打卡信息。

bitmaps位图,用二进制来进行记录,只有0和1两个状态。

setbit key offset value 设置值

getbit key offset 获取值

bitcount key 统计为1 个数

4 事务

redis 单条命令是保证原子性的,但是事务不保证原子性。

redis 事务没有隔离级别的概念。所以的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会被执行!

redis 事务:一组命令的集合,一个事务中所有命令都会被序列化,在事务执行的过程中,会按照顺序执行。

一次性,顺序性,排他性

-------- 队列 set set set 执行------

开启事务:multi
命令入队:.......
执行事务: exec
放弃事务:discard  放弃事务后,队列中所有事务都不会被执行。

出现错误

编译型异常:(代码错误) 事务中所有的命令都不会被执行。

运行时异常:如果事务队列中存在运行时异常,那么在执行事务时,其他的命令可以正常执行,错误命令抛出异常。

监控

悲观锁:什么时候都会出问题,无论做什么都加锁。

乐观锁:

  • 认为什么时候都不会出现错误,所以不会上锁。跟新数据的时候去判断一下,在此期间是否有人修改过这个数据。

  • 获取version

  • 跟新的时候比较version

使用watch key 可以当作redis 的乐观锁


127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec //在执行事务之前监视money是否发生改变,如果发生改变事务执行失败。
(nil)

unwatch 解锁  //如果执行失败,先解锁,再监视。

5 jedis

导入依赖

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>4.1.1</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.79</version>
    </dependency>
</dependencies>

测试

public static void main(String[] args) {

    Jedis jedis = new Jedis("127.0.0.1", 6379);
    //jedis 中的方法就是redis 的指令
    System.out.println(jedis.ping());
    jedis.select(0); //选择数据库
    System.out.println(jedis.flushDB()); //清空数据
    System.out.println(jedis.exists("name")); //判断是否存在

    System.out.println(jedis.set("name","fadaiyu")); //添加
    System.out.println(jedis.get("name")); //获取
    System.out.println(jedis.dbSize());  //数据库大小
    jedis.flushDB();
    jedis.watch("name");  //监视
    Transaction multi = jedis.multi();  //开启一个事务
    multi.set("name","11111");
    multi.exec();    //事务提交
    System.out.println(jedis.get("name"));


}
public static void main(String[] args) {

    Jedis jedis = new Jedis("127.0.0.1", 6379);

    JSONObject jsonObject=new JSONObject();
    jsonObject.put("name","fadaiyu");
    jsonObject.put("age",12);

    Transaction multi = jedis.multi();

    String str=jsonObject.toString();
    try {
        multi.set("user1",str);
        multi.set("user2",str);
        multi.exec();


    }catch (Exception e){
        multi.discard();// 如果发生异常。回滚
    }
    finally {

    }
    System.out.println(jedis.get("user1"));
    jedis.close();

}

6 配置文件

单位

# 设置单位
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
# 单位对大小写不敏感
# units are case insensitive so 1GB 1Gb 1gB are all the same.

# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
# 将其他配置文件导入
# include .\path\to\local.conf
# include c:\path\to\other.conf

网络

#绑定端口
bind 127.0.0.1

protected-mode yes 保护模式

port 6379 端口

通用

daemonize yes 守护进程开启

pidfile /var/run/redis_6379.pid  如果以后台方式运行,我们就需要指定一个pid文件‘

# Specify the server verbosity level.
# 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 数据库数量

always-show-logo yes 是否显示logo

持久化

持久化规则
save 900 1    900 秒内,如果至少有一个key 进行了修改 就进行持久化操作
save 300 10  
save 60 10000

stop-writes-on-bgsave-error yes 持久化出错后是否继续工作

rdbcompression yes 是否压缩rdb 文件,如果是,会消耗一定cpu资源

rdbchecksum yes 保存rdb 文件时,进行错误坚持

dir ./  rdb 保存的目录


REPLICATION 复制

replicaof <masterip 主机的地址> <masterport 主机的端口>

SECURITY 安全

requirepass 设置密码,默认没有密码


CLIENTS 客户端

maxclients 10000 最大客户端数

MEMORY MANAGEMENT

maxmemory <bytes> 最大内存容量

maxmemory-policy noeviction  内存达到上限后处理策略
    noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。(默认值)
    allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
    volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
    allkeys-random: 所有key通用; 随机删除一部分 key。
    volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
    volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。

APPEND ONLY MODE

appendonly no 默认不开启,默认使用rdb,在大多数情况下,rdb够用

appendfilename "appendonly.aof" 持久化文件的名字


# appendfsync always 每次修改都会保存
appendfsync everysec 每秒执行一次,但可以会丢失这一秒的数据
# appendfsync no 不执行,操作系统自己同步数据 速度最快。


7 持久化

7.1 rdb

image-20220211200934820

在指定时间间隔内将内存中的数据集快照写入磁盘,也就是snapsho快照,它恢复时是将快照文件直接读到内存里。redis 会单独fork一个子程序来进行持久化,会先将数据写入一个临时文件中,待持久化过程都结束后,再用这个临时文件替换上次持久化好的文件,主进程是不进行任何io操作的。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,rdb方式要比aop更加高效。rdb的缺点是最后一次持久化后的数据可能会丢失。

触发机制

  • save 规则满足后
  • 执行flushall 命令后
  • 退出redis 也会产生dum.rdb

恢复rdb 文件

  • 只要放在启动目录下,就会自动使用。

优缺点

优点:

  • 适合大规模的数据恢复
  • 对数据的完整性要求不高。

缺点:

  • 需要一定的时间间隔。如果redis 发生宕机,这个最后一次修改的数据就没有了。
  • fork 进程的时候,会占用一定的时间内容空间。

7.2 AOF

image-20220211204705173

以日志的形式来记录每个写操作,只需追加文件,但不可以改写文件,redis 启动之初会读取该文件重构数据库。

优缺点

优点:

  • 每次修改都会同步,文件的完整性会更好
  • 每秒同步一次,可以会丢失一秒的数据。
  • 从不同步,效率最高

缺点:

  • 相对于数据文件来说,aof 远大于rdb 修复的速度也慢于rdb
  • aof 运行效率也要比rdb慢.

重写规则

aof 默认文件无限追加
no-appendfsync-on-rewrite no 是否打开

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb   如果aof 大于64m,就会重新fork一个进程,对文件进行重写。

8 发布订阅

redis 发布订阅是一种消息通信模式,发送者pub 发送信息,订阅者sub 接收信息。

redis 客户端可以订阅任意数量的频道。

image-20220211213247871

 psubscribe pattern [pattern ...] 订阅一个或者多个符合给定模式的频道
 
 pubsub subcommand [argument [argument ...]] 查看订阅与发布系统状态
  
 publish channel message 将信息发送到指定的频道
 
 punsubscribe [pattern [pattern ...]] 退订所有给定模式的频道
  
 subscribe channel [channel ...] 订阅一个或多个频道信息
 
 unsubscribe [channel [channel ...]] 退订指定的频道
 
 

9 主从复制

9.1 概念

​ 主从复制指将一台服务器的数据,复制到其他redis服务器。前者称为主节点,后者成为从节点。数据复制是单向的,只能从主节点到从节点。

主从复制的作用:

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的另一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务。
  • 高可用基石:除了上述作用以外,主从复制还是哨兵喝集群能够实现的基础。

要将redis 应用于项目中,只使用一台redis是不够的:

  • 从结构上讲,单个redis承受的压力较大,容易发生故障。,
  • 从容量上讲,单个redis内存容量有限,单台redis最大使用内存不应该超过20G

9.2 环境配置

info replication 查看当前库的信息

# Replication
role:master 当前角色
connected_slaves:0 从机个数

复制配置文件
修改端口号
修改pid名字
修改log文件名字
修改dump.rdb文件名字

分别使用不同的配置文件启动redis

image-20220219204549267

9.3 主从配置

一般情况,每台redis 都默认为主机

slaveof 127.0.0.1 6380

slaveof no one  让自己成为主机

也可以在配置文件中配置

replicaof <masterip 主机的地址> <masterport 主机的端口>

细节

主机可以写,从机只能写不能读,主机中多有数据都会被同步从机。

主机断开后,从机可以继续读。主机恢复后,从机自动连接到主机上。

如果从机使用命令行配置,从机断开后再恢复默认为主机。

image-20220219212550032

复制原理

从机启动成功连接到主机后会发送一个sync 命令。主机接到命令后,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,再后台进程执行完毕后,主机将整个数据文件传送到从机,完成数据同步。

全量复制:从机在接收到数据库文件后,将其存盘并加载到内存。只要重新连接主机,一定会执行全量复制。

增量复制:主机继续将新的所有收集到的修改命令依次传送给从机,完成同步。

9.4 哨兵模式

当主机宕机后,需要将另一台服务器切换成主服务器。redis2.8后正式提出Sentinel(哨兵模式)架构来解决这个问题。

redis提供了哨兵的命令,哨兵是一个独立的进程,它会独立运行,其原理是哨兵通过发送命令等待redis服务响应,从而监控运行的多个redis实例。如果主机故障,根据投票数自动将从库转为主库。

image-20220219213708374

哨兵的作用:

  • 通过发送命令,让redis服务器返回监控运行状态,包括主服务器和从服务器。
  • 当哨兵检测到主服务器宕机后,会自动将从机切换为主机,然后发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机。

然后哨兵也可能出现问题,故可以使用多个哨兵进行监视,各个哨兵之间还会进行监控,形成多哨兵模式

image-20220219214502736

假设主服务器宕机后,哨兵1先检测到这个结果后,系统并不会马上开始重新选择主机,这仅仅是哨兵1主观认为这个主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用时,并且达到一定数量,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行故障转移操作,切换成功后,发布订阅模式,让各个哨兵把自己监控的从服务器切换主机,这个过程成为客观下线。

哨兵配置文件

sentinel monitor 主机名称 ip port  1
后面的1代表主机挂了,slave 投票让谁接替成为主机,票数最多就会成为主机。

redis-sentinel sentinel.conf 启动哨兵

当主机挂了后,哨兵自动选出新的主机,当原来的主机重新回来后,哨兵将其变为新主机的从机。

优缺点

优点;

  • 基于哨兵模式,所有主从复制优点,它都有
  • 故障转移,系统可用性好

缺点;

  • redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦。
  • 实现哨兵模式配置很麻烦

哨兵模式的全部配置

哨兵sentinel实例运行的端口 默认26379
port 26379

哨兵sentinel的工作目录
dir /tmp

哨兵sentinel监控的redis主节点的 ip port
master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2

当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000

这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1

故障转移的超时时间 failover-timeout 可以用在以下这些方面:
1. 同一个sentinel对同一个master两次failover之间的间隔时间。
2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
3.当想要取消一个正在进行的failover所需要的时间。 
4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
默认三分钟
sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000

 SCRIPTS EXECUTION
配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
对于脚本的运行结果有以下规则:
若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。

通知脚本
 shell编程
sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh

 客户端重新配置主节点参数脚本
 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
 以下参数将会在调用脚本时传给脚本:
 <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
 目前<state>总是“failover”,
 <role>是“leader”或者“observer”中的一个。
 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
 这个脚本应该是通用的,能被多次调用,不是针对性的。
 sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 一般都是由运维来配置!

10 redis 缓存穿透和雪崩了解

10.1 缓存穿透

概念

image-20220219222058732

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

解决方案

布隆过滤器

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

image-20220219223810540

如果控制能够被存储起来,这就意味着缓存需要更多的空间存储更多的键,因此这当中可能会有很多空值的键。

即使对空值设置了过期时间,还是会在缓存层和存储层的数据会有一段时间不一致,这对于需要保持一致性的业务造成一定影响。

10.2 缓存击穿

概念

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

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并回写缓存,这位导致数据库压力瞬间增大。

解决方案、

设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。

加互斥锁

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

10.3 缓存雪崩

概念

指在某一个时间端,缓存集中过期失效。那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。这时候如果运维马上又重启数据库,马上又会有新的流量把数据库打死。这就是缓存雪崩。

image-20220219230519841

集中过期并不是致命的,比较致命的缓存雪崩式缓存服务器某个节点宕机或者断网,因为自然形成的雪崩一定实在某个时间端集中创建缓存,这个时候数据库也是可以顶住压力的,无非就是对数据库产生周期性的压力而已。而缓存服务器节点的宕机,对数据库服务器的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

redis 高可用

多设置几台redis 这样一台挂掉后,其他还可用继续工作。异地多活

限流降级

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

数据预热

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

== 跟随《遇见狂神说》 学习==
对缓存穿透和雪崩暂未有深刻的理解,后面会继续学习

本文作者:发呆鱼

本文链接:https://www.cnblogs.com/dyiblog/articles/15927836.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   发呆鱼  阅读(25)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起