阶段十-分布式-Redis02

news2024/11/15 12:35:38

第一章 Redis 事务

1.1 节 数据库事务复习

数据库事务的四大特性

  • A:Atomic ,原子性,将所以SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
  • C:Consistent,一致性,事务完成后,所有的数据的状态都是一致的。即A账户只要减去100,B账户就必定增加100
  • I:Isolation,隔离性,如果有多个事务并发执行,每个事务做出的修改必须与其他事务隔离
  • D:Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储

1.2 节 Redis 事务介绍

Redis事务是一组命令的集合,一个事务中的所有命令都将被序列化,按照一次性、顺序性、排他性的执行一系列的命令。

Redis事务三大特性

  • 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;

  • 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”。

  • 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚;

1.3 节 Redis 事务的基本操作

Multi、Exec、discard

示例1:正常执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> get k1 
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) "1"
3) (integer) 2
4) "2"

示例2:放弃事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k2 1
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> discard
OK

示例3:全部都不执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k3 1
QUEUED
127.0.0.1:6379(TX)> get k3 
QUEUED
127.0.0.1:6379(TX)> set k3
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors

注意:

命令集合中含有错误的指令(注意是语法错误),全部失败。

示例4:运行时错误

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k4 1
QUEUED
127.0.0.1:6379(TX)> incr k4
QUEUED
127.0.0.1:6379(TX)> get k4
QUEUED
127.0.0.1:6379(TX)> set name zhangsan
QUEUED
127.0.0.1:6379(TX)> incr name
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (integer) 2
3) "2"
4) OK
5) (error) ERR value is not an integer or out of range
6) "zhangsan"

注意: 运行时错误,即非语法错误,正确命令都会执行,错误命令返回错误。

Redis有三种集群模式

  • 主从模式

  • Sentinel模式

  • Cluster模式

第二章 Redis 主从复制

2.1 节 Redis 主从复制概述

什么是主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点

主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 (即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

  4. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

2.2 节 Redis 主从复制搭建

在一台虚拟机上搭建

新建redis6379.conf

内容:

include /usr/local/redis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb

同理:

新建redis6380.conf

include /usr/local/redis/redis.conf
pidfile /var/run/redis_6380.pid
port 6380
dbfilename dump6380.rdb

新建redis6381.conf

include /usr/local/redis/redis.conf
pidfile /var/run/redis_6381.pid
port 6381
dbfilename dump6381.rdb

【2】启动三台redis服务器

redis-server ./redis6379.conf
redis-server ./redis6380.conf
redis-server ./redis6381.conf

【3】查看系统进程

ps -ef |grep redis

  会有已经运行的进程

root     122739      1  0 00:18 ?        00:00:00 redis-server 0.0.0.0:6379
root     122877      1  0 00:18 ?        00:00:00 redis-server 0.0.0.0:6380
root     122965      1  0 00:19 ?        00:00:00 redis-server 0.0.0.0:6381
root     123210   3414  0 00:19 pts/0    00:00:00 grep --color=auto redis

【4】查看三台主机运行情况

   info replication 查看节点相关信息

#打印主从复制的相关信息
./redis-cli -p 6379
./redis-cli -p 6380
./redis-cli -p 6381
127.0.0.1:6379> info replication
127.0.0.1:6380> info replication
127.0.0.1:6381> info replication

【5】配从库不配主库

slaveof <ip> <port>

示例:在6380和6381上执行

127.0.0.1:6380> SLAVEOF 192.168.184.100 6379
OK

【6】在主机上写,在从机上可以读取数据

set k1 v1

2.3 节 Redis 主从复制原理

主从复制可以分为3个阶段

  • 连接建立阶段(即准备阶段)

  • 数据同步阶段

  • 命令传播阶段

复制过程大致分为6个过程

  1. 保存主节点(master)信息。

  2. 从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接

  3. 从节点与主节点建立网络连接

    从节点会建立一个 socket 套接字,从节点建立了一个端口为51234的套接字,专门用于接受主节点发送的复制命令

  4. 发送ping命令

    连接建立成功后从节点发送 ping 请求进行首次通信。

  5. 权限验证。

    如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

  6. 同步数据集。

    主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。

  7. 主从同步策略

        主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,            slave 在任何时候都可以发起全量同步。 redis 策略是,无论如何,首先会尝试进行增量同              步,如不成功,要求从机进行全量同步。

第三章 Redis 哨兵监控

3.1 节 Redis哨兵概述

主从复制的缺点:当主机 Master 宕机以后,我们需要人工解决切换

哨兵概述

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

哨兵作用

  1. 集群监控:负责监控redis master和slave进程是否正常工作

  2. 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员

  3. 故障转移:如果master node挂掉了,会自动转移到slave node上

  4. 配置中心:如果故障转移发生了,通知client客户端新的master地址

3.2 节 Redis哨兵搭建

【1】编写配置文件

新建sentinel-26379.conf文件

#端口
port 26379
#守护进程运行
daemonize yes
#日志文件
logfile "26379.log"
sentinel monitor mymaster 192.168.184.100 6379 2

参数解释:

sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是:该哨兵节点监控192.168.92.128:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。

同理

新建sentinel-26380.conf文件

#端口
port 26380
#守护进程运行
daemonize yes
#日志文件
logfile "26380.log"
sentinel monitor mymaster 192.168.184.100 6379 2

新建sentinel-26381.conf文件

#端口
port 26381
#守护进程运行
daemonize yes
#日志文件
logfile "26381.log"
sentinel monitor mymaster 192.168.184.100 6379 2

【2】启动哨兵节点

redis-sentinel sentinel-26379.conf
redis-sentinel sentinel-26380.conf
redis-sentinel sentinel-26381.conf

【3】查看哨兵状态

  先进入哨兵客户端

redis-cli -p 26379

 查看哨兵信息

info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.9.133:6379,slaves=2,sentinels=3

 sentinel_masters:1 该哨兵为主节点 

 address=192.168.9.133:6379 监控的ip

 slaves=2 有两个从节点

 sentinels=3 总共有三个哨兵监控

【4】测试故障转移,使用kill命令杀掉主节点

 查看所有服务

ps -ef | grep redis

  根据id杀死6379进程

kill -9  进程id

  哨兵会重新票选新的主节点,注意:需要等待一定的时间

  【5】查看哨兵节点信息

info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.9.133:6380,slaves=2,sentinels=3

  监听的ip变成了6380,6380变成了新的主节点

3.3 节 Redis 哨兵原理

3.3.1 监控阶段

注意:

  1. sentinel(哨兵1)----->向master(主)和slave(从)发起info,拿到全信息。

  2. sentinel(哨兵2)----->向master(主)发起info,就知道已经存在的sentinel(哨兵1)的信息,并且连接slave(从)。

  3. sentinel(哨兵2)----->向sentinel(哨兵1)发起subscribe(订阅)。

3.3.2 通知阶段

sentinel不断的向master和slave发起通知,收集信息。

3.3.3 故障转移阶段

通知阶段sentinel发送的通知没得到master的回应,就会把master标记为SRI_S_DOWN,并且把master的状态发给各个sentinel,其他sentinel听到master挂了,说我不信,我也去看看,并把结果共享给各个sentinel,当有一半的sentinel都认为master挂了的时候,就会把master标记为SRI_0_DOWN。

3.3.4 投票阶段

方式: 自己最先接到哪个sentinel的竞选通知就会把票投给它。

剔除一些情况:

  1. 不在线的

  2. 响应慢的

  3. 与原来master断开时间久的

  4. 优先级原则

第四章 Redis Cluster

完善哨兵模式的缺点:

  1. 当master挂掉的时候,sentinel 会选举出来一个 master,选举的时候是没有办法去访问Redis的,会存在访问瞬断的情况;

  2. 哨兵模式,对外只有master节点可以写,slave节点只能用于读。尽管Redis单节点最多支持10W的QPS,但是在电商大促的时候,写数据的压力全部在master上。

  3. Redis的单节点内存不能设置过大,若数据过大在主从同步将会很慢;在节点启动的时候,时间特别长;

4.1 节 Redis Cluster概述

Redis集群的优点

  • Redis集群有多个master,可以减小访问瞬断问题的影响

  • Redis集群有多个master,可以提供更高的并发量 

  • Redis集群可以分片存储,这样就可以存储更多的数据

4.2 节 Redis Cluster模式搭建

Redis的集群搭建最少需要3个master节点,我们这里搭建3个master,每个下面挂一个slave节点,总共6个Redis节点;

【1】 准备环境

 三个linux虚拟机

第1台机器: 192.168.9.131 8001端口 8002端口
第2台机器: 192.168.9.132 8001端口 8002端口
第3台机器: 192.168.9.133 8001端口 8002端口

拷贝配置文件

将redis安装目录下的 redis.conf 文件分别拷贝到8001和8002目录下

cp /usr/local/redis-6.2.6/redis.conf /usr/local/redis-cluster/8001
cp /usr/local/redis-6.2.6/redis.conf /usr/local/redis-cluster/8002

修改redis.conf文件以下内容 8001

# 端口
port 8001
# 后台启动
daemonize yes
# 进程id
pidfile "/var/run/redis_8001.pid"
#指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据
dir /usr/local/redis-cluster/8001/
#启动集群模式
cluster-enabled yes
#集群节点信息文件,这里800x最好和port对应上
cluster-config-file nodes-8001.conf
# 节点离线的超时时间
cluster-node-timeout 5000
#去掉bind绑定访问ip信息
#bind 127.0.0.1
#关闭保护模式
protected-mode no
#启动AOF文件
appendonly yes
#如果要设置密码需要增加如下配置:
#设置redis访问密码
#requirepass 123456
#设置集群节点间访问密码,跟上面一致
#masterauth 123456

修改redis.conf文件以下内容 8002

# 端口
port 8002
# 后台启动
daemonize yes
# 进程id
pidfile "/var/run/redis_8002.pid"
#指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据
dir /usr/local/redis-cluster/8002/
#启动集群模式
cluster-enabled yes
#集群节点信息文件,这里800x最好和port对应上
cluster-config-file nodes-8002.conf
# 节点离线的超时时间
cluster-node-timeout 5000
#去掉bind绑定访问ip信息
#bind 127.0.0.1
#关闭保护模式
protected-mode no
#启动AOF文件
appendonly yes
#如果要设置密码需要增加如下配置:
#设置redis访问密码
#requirepass 123456
#设置集群节点间访问密码,跟上面一致
#masterauth 123456

三台机器同理

【3】启动集群

每台机器都启动服务器

redis-server /usr/local/redis-cluster/8001/redis.conf
redis-server /usr/local/redis-cluster/8002/redis.conf

创建集群

redis-cli -a redis-pw --cluster create --cluster-replicas 1 
192.168.9.131:8001 192.168.9.131:8002	
192.168.9.132:8001 192.168.9.132:8002	
192.168.9.133:8001 192.168.9.133:8002

 没有密码去掉 -a   redis  -pw

查看帮助命令

 src/redis‐cli --cluster help

参数:

  • create:创建一个集群环境host1:port1 ... hostN:portN

  • call:可以执行redis命令

  • add-node:将一个节点添加到集群里,第一个参数为新节点的ip:port,第二个参数为集群中任意一个已经存在的节点的ip:port

  • del-node:移除一个节点

  • reshard:重新分片

  • check:检查集群状态

【4】测试集群

连接任意一个客户端

redis-cli -a redis-pw -c -h 192.168.9.131 -p 8001

参数:

  • ‐a表示服务端密码

  • ‐c表示集群模式

  • -h指定ip地址

  • -p表示端口号

查看集群的信息

cluster info

4.3 节 Redis Cluster 原理

Redis Cluster将所有数据划分为16384个slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。只有master节点会被分配槽位,slave节点不会分配槽位。

槽位定位算法: k1 = 127001

Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。

HASH_SLOT = CRC16(key) % 16384

命令执行

set k1 v1

存入了12706槽位中

可以通过{}来定义组的概念,从而是key中{}内相同内容的键值对放到同一个slot中。

mset k1{test} v1 k2{test} v2 k3{test} v3

 让三个key在同一个槽位之中

故障恢复案例:

杀死Master节点之后还会重新选举

观察节点信息

cluster nodes

第五章 SpringDataRedis 连接集群

 【1】添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

【2】编写配置

spring:
  redis:
    cluster:
      nodes: 192.168.9.131:8001,192.168.9.131:8002,192.168.9.132:8001,192.168.9.132:8002,192.168.9.133:8001,192.168.9.133:8002

【3】编写测试

@SpringBootTest
class RedisDemo2ApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("k6","v6");
        System.out.println(redisTemplate.opsForValue().get("k6"));
    }
}

第六章 Redis常见问题探析(面试题)

6.1 节 Redis脑裂问题

6.1.1 什么是集群脑裂

Redis的集群脑裂是指因为网络问题,导致Redis Master节点跟Redis slave节点和Sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。

个人理解就是有一个主节点宕机了,无法与客户端进行连接,就不会参与新的选举,在其恢复状态后还是以主节点的身份参与工作,造成了多个主节点同时存在的问题,这就是脑裂问题。 

6.1.2 解决方案

redis.conf配置参数:

min-replicas-to-write 1
min-replicas-max-lag 5

参数:

  • 第一个参数表示最少的slave节点为1个

  • 第二个参数表示数据复制和同步的延迟不能超过5秒

6.2 节 Redis 缓存预热问题

6.2.1 缓存冷启动

缓存中没有数据,由于缓存冷启动一点数据都没有,如果直接就对外提供服务了,那么并发量上来Mysql就裸奔挂掉了。

6.2.2 冷启动应用场景

新启动的系统没有任何缓存数据,在缓存重建数据的过程中,系统性能和数据库负载都不太好,所以最好是在系统上线之前就把要缓存的热点数据加载到缓存中,这种缓存预加载手段就是缓存预热。

6.2.3 解决思路

  • 提前给redis中灌入部分数据,再提供服务

  • 如果数据量非常大,就不可能将所有数据都写入redis,因为数据量太大了,第一是因为耗费的时间太长了,第二根本redis容纳不下所有的数据

  • 需要根据当天的具体访问情况,实时统计出访问频率较高的热数据

  • 然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热

6.3 节 Redis缓存穿透问题

6.3.1 概念

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

用户访问空的数据库与缓存

解释:

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

6.3.2 解决方案

  • 对空值缓存:如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果缓存,设置空结果的过期时间会很短,最长不超过5分钟。简单的说就是返回一个空值

  • 布隆过滤器:如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。

6.3.3 布隆过滤器

布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

所以判断不存在的数据最快

6.3.4 代码实现

【1】引入依赖

<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.7.17</version>
</dependency>

【2】java代码实现

// 初始化 注意 构造方法的参数大小10 决定了布隆过滤器BitMap的大小
BitMapBloomFilter filter  = new BitMapBloomFilter(10);
filter.add("123");
filter.add("abc");
filter.add("ddd");
boolean abc = filter.contains("abc");
System.out.println(abc);

6.4 节 Redis 缓存击穿问题

6.4.1 概念

某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

6.4.2 解决方案

  • 互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,其他线程直接查询缓存。

  • 热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存(不咋靠谱)

6.4.3 代码实现(有待改进)

理念是对的,但是满足不了的企业的实际需求

真正的解决方案是使用第三方的软件进行控制

public String get(String key) throws InterruptedException {
       String value = jedis.get(key);
       // 缓存过期
       if (value == null){
           // 设置3分钟超时,防止删除操作失败的时候 下一次缓存不能load db
           //setnx 如果有值返回0,如果没有值设置值返回1
           Long setnx = jedis.setnx(key +"mutex", "1");           
           jedis.pexpire(key + "mutex", 3 *60);
           // 代表设置成功
           if (setnx == 1){
               // 数据库查询
               //value = db.get(key);
               //保存缓存
               jedis.setex(key,3*60,"");
               jedis.del(key + "mutex");
               return value;
           }else {
               // 这个时候代表同时操作的其他线程已经load db并设置缓存了。需要重新重新获取缓存           
               //这个睡眠不够严谨,不能保证操作完成,value的值已经更新完成
               Thread.sleep(50);
               // 重试
               return get(key);
           }
       }else {
           return value;
       }
}

6.5 节 Redis缓存雪崩问题

6.5.1 概念

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

就是大量的数据同时过期

6.5.2 解决方案

二级缓存解决:先查询本地nginx缓存查询有没有数据,有数据,直接返回;nginx缓存没有数据再去redis分布式缓存查询,如果有将redis缓存同步nginx缓存在返回;如果redis没有,取数据库查询,数据库存在将数据同步到redis并返回。只要nginx的过期时间和redis的过期时间不一样就解决了缓存雪崩问题,并合理使用了本地缓存和分布式缓存。

6.6 节 Redis 开发规范

6.6.1 key设计技巧

  1. 把表名转换为key前缀,如 tag:

  2. 把第二段放置用于区分key的字段,对应msyql中主键的列名,如 user_id

  3. 第三段放置主键值,如 2,3,4

  4. 第四段写存储的列名

6.6.2 value设计

拒绝bigkey

防止网卡流量、慢查询,string类型控制在10KB以内,hash、 list、set、zset元素个数不要超过5000。

6.6.3 命令使用

  1. 禁用命令 :禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。

  2. 合理使用select :redis的多数据库较弱,使用数字进行区分,很多客户端支持较差, 同时多业务用多数据库实际还是单线程处理,会有干扰。

  3. 使用批量操作提高效率

    1. 原生命令:例如mget、mset。

    2. 非原生命令:可以使用pipeline提高效率

注意:但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。

  1. 不建议过多使用Redis事务功能

    Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上。

6.7 节 Redis 数据一致性问题

三种更新策略

  1. 先更新数据库,再更新缓存

  2. 先删除缓存,再更新数据库

  3. 先更新数据库,再删除缓存

先更新数据库,再更新缓存

这套方案,大家是普遍反对的。为什么呢?

线程安全角度 ,同时有请求A和请求B进行更新操作,那么会出现

(1)线程A更新了数据库

(2)线程B更新了数据库

(3)线程B更新了缓存

(4)线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

先删缓存,再更新数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

(1)请求A进行写操作,删除缓存

(2)请求B查询发现缓存不存在

(3)请求B去数据库查询得到旧值

(4)请求B将旧值写入缓存

(5)请求A将新值写入数据库

注意: 该数据永远都是脏数据。

先更新数据库,再延时删缓存

这种情况存在并发问题吗?

(1)缓存刚好失效

(2)请求A查询数据库,得一个旧值

(3)请求B将新值写入数据库

(4)请求B删除缓存

(5)请求A将查到的旧值写入缓存

发生这种情况的概率又有多少?

发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的,因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。

第七章 总结

【1】简述以下redis事务?

答:redsi的事务和关系型数据库事务解决问题不一样,原则也不一样,没有acid原则,redis事务保证命令按照队列顺序执行问题,所以redis事务分为两个阶段,第一个阶段是命令入队,第二个阶段是命令执行。

如果队列中命令都证正确,那么按照入队顺序执行,如果队列中命令语法错误,队列所有命名都不执行,如果队列命令语法没错,但是执行失败,只有该命令本身不会执行,其他命令都会执行

redis事务命令

multi 开启事务
exec 执行事务
discard 取消事务

【2】简述一下Redis的主从复制模式

答:Redis集群模式分三种

  • 主从模式

  • 哨兵模式

  • 集群模式

主从模式从一定程度上解决单机Redis的并发问题和可用性问题。但是主从模式问题很大,主节点挂了,需要手动将从节点切换为主节点,所以实际开发很少用到。然后Redis主从复制是哨兵机制和集群机制的根本,所以理解主从模式很重要。

主从模式原理如下:

  1. 从节点保存主节点(master)信息。

  2. 从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接。

  3. 从节点与主节点建立网络连接 。

  4. 发送ping命令 :连接建立成功后从节点发送 ping 请求进行首次通信

  5. 权限验证:如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

  6. 主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。

  7. 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。 redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

【3】简述一下哨兵模式

答:哨兵模式主要解决主从复制master节点挂掉之后的,切换从节点为主节点问题,哨兵模式通过监控主从复制主节点和从节点完成自动切换主节点功能。

原理如下:

【4】简述以下Redis集群模式

答:集群模式至少需要6个redis服务器,集群模式就是n个主从复制模式,但是需要根据插槽机制分配每个节点存取数据范围。来解决单个主从redis不能存取数据过大问题,因为会降低从节点复制速度。

原理:

【5】Redis脑裂问题解决?

答:Redis主从模式下,由于主节点挂了,从节点新选出主节点,由于网络情况,挂掉主节点没有降级为从节点,导致有两个主节点可以写数据。导致挂掉的主节点的数据无法同步而丢失问题。解决办法

min-replicas-to-write 1
min-replicas-max-lag 5

【6】如何解决缓存冷启动问题?

答:冷启动是指服务器缓存中没有数据直接启动,访问量有比较大,mysql直接裸机运行。通过缓存预热解决,通过storm实时计算出热点数据,然后定时将热点数据写入缓存。

【7】如何解决缓存穿透问题?

答:查询一个redis和mysql肯定都不存在的数据是缓存穿透,例如查询id为-1的数据,多半为认为恶意攻击。解决方案:去数据库查不存在在redis存null,并且设置过期时间5分钟。

或者用布隆过器解决:布隆过滤器是一个bitMap数组,它说不存在的元素一定不存在,他说存在的未必存在。

【8】如何缓存击穿问题?

答:某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。从造成问题的原因去解决,当热点Key失效的一刹那,大并发裸奔访问数据库,数据库被打垮。我们只需要在热点Key失效的一刹那保证只有一个请求过来就可以了。这种需求肯定用锁解决,理论上可以用同步锁解决,但这个不靠谱,锁只能锁一个进程,但是微服务是多个服务是多个进程,根本不起作用。所以用分布式锁解决。

【9】如何解决缓存雪崩问题?

答:大量缓存同时失效,数据库被击垮问题。解决思路不让缓存同时失效,比如加给缓存过期时间加随机数,但是当缓存数据足够大时,这个效果不那么明显了,可以通过二级缓存技术实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1354084.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue前端文字效果:如何让一段文本像是手动一个一个字打出来的

效果展示 自己做的AI聊天机器人界面&#xff0c;我觉得比微信还好看 由于这个前端略微复杂&#xff0c;下文用最简单的例子来展示&#xff1a; 分析需求 对于AI聊天工具的前端&#xff0c;如果AI生成的文本像是一个一个字打出来的&#xff0c;就会让AI看起来更像真的人&…

详解卡尔曼滤波(Kalman Filter)

1. 从维纳滤波到卡尔曼滤波 黑盒&#xff08;Black Box&#xff09;思想最早由维纳&#xff08;Wiener&#xff09;在1939年提出&#xff0c;即假定我们对从数据到估计中间的映射过程一无所知&#xff0c;仅仅用线性估计&#xff08;我们知道在高斯背景下&#xff0c;线性估计…

STM32 HAL库定时器触发DMA并口数据传输

代码目的&#xff1a; STM32与FPGA通讯&#xff0c;通过8位并口线进行通讯&#xff0c;16byte的数据在10us之内通过8位并口数据线传给FPGA&#xff0c;FPGA读取该数据。 HAL库设置说明&#xff1a; 时钟采用80MHz&#xff0c;由于16byte的数据要在10us之内传完&#xff0c;那…

[笔记] 使用 qemu 创建虚拟磁盘并安装 grub

之前使用 wsl 进行了直接创建虚拟磁盘并安装 grub,现在希望能够直接借助 qemu 的工具创建虚拟磁盘文件并安装 grub,由于需要用到 nbd(net block device网络块设备) 模块,在 wsl 中并不支持,因此这里使用到了 Hypver-V 虚拟机创建了一个 Ubuntu 系统,在系统中安装了 qemu 和 gru…

物联网安全:保护关键网络免受数字攻击

物联网 (IoT) 彻底改变了当今互联世界中的各个行业&#xff0c;实现了智能家居、自动驾驶汽车和先进的工业系统。然而&#xff0c;随着物联网设备数量的急剧增加&#xff0c;这些设备和相应网络的安全性已成为人们关注的焦点。本文旨在探讨物联网安全的重要性&#xff0c;同时简…

Pandas教程(五)—— 数据重塑透视及批量处理

1.数据重塑 重塑数据主要有两种方式&#xff0c;分别是 stack&#xff08;堆叠&#xff09;和 unstack&#xff08;拆堆&#xff09;&#xff0c;他们两个是互逆的操作 函数作用 data.stack&#xff08; &#xff09; 堆叠 会“旋转”或将列中的数据透视到行 列 一一> 行…

AWS(三):如何在AwsManagedAd目录和windowsAD实例之间建立双向信任。

前提&#xff1a; 1.创建好了一个AWS managed AD目录&#xff0c;我的目录域名为:aws.managed.com 2.创建好了一个windows AD实例并提升了为域控服务器,实例域名为:aws2.com 看过我AWS 一和二的应该都会创建windows实例了&#xff0c;切记不能将其无缝加入到aws managed AD的…

(JAVA)-反射

什么是反射&#xff1f; 反射允许对成员变量&#xff0c;成员方法和构造方法的信息进行编程访问。 说简单点就是反射能将类里面的构造方法&#xff0c;成员变量,修饰符,返回值&#xff0c;注解&#xff0c;类型&#xff0c;甚至异常等类里面的所有东西都能够获取出来。 关于C…

打造炫酷粒子效果的前端利器tsParticles

前端潮流速递 &#xff1a;打造炫酷粒子效果的前端利器tsParticles 在现代前端开发中&#xff0c;动画和视觉效果是吸引用户的关键元素之一。而实现炫酷而引人入胜的粒子效果&#xff0c;常常需要耗费大量的时间和精力。然而&#xff0c;有了 tsParticles&#xff0c;这一切变…

GitHub上的15000个Go模块存储库易受劫持攻击

内容概要&#xff1a; 目前研究发现&#xff0c;GitHub上超过15000个Go模块存储库容易受到一种名为“重新劫持”的攻击。 由于GitHub用户名的更改会造成9000多个存储库容易被重新劫持&#xff0c;同时因为帐户删除&#xff0c;会对6000多个存储库造成重新劫持的危机。目前统计…

【MySQL·8.0·源码】MySQL 表的扫描方式

前言 在进一步介绍 MySQL 优化器时&#xff0c;先来了解一下 MySQL 单表都有哪些扫描方式。 单表扫描方法是基表的读取基础&#xff0c;也是完成表连接的基础&#xff0c;熟悉了基表的基本扫描方式&#xff0c; 即可以倒推理解 MySQL 优化器层的诸多考量。 基表&#xff0c;即…

半导体行业-SECS/GEM协议 JAVA与SECS/GEM通信 什么是配方?springboot集成SECS通信协议 配方管理S7FX

Java与SECS基础通信 Java实现SECS指令S2F17获取时间 Java实现SECS指令 S10F3 终端单个显示例子 Java实现SECS指令 S7FX配方管理 Java实现SECS指令 S5F1报警/取消报警上传 实例源码及DEMO请查阅 JAVA开发SECS快速入门资料&#xff0c;SECS S7F19 什么是半导体配方&…

Unity3D UGUI图集打包与动态使用(TexturePacker)

制作图集的好处&#xff1a; 众所周知CPU是用来处理游戏的逻辑运算的&#xff0c;而GPU是用来处理游戏中图像的。在GPU中&#xff0c;我们要绘制一个图像需要提交图片&#xff08;纹理&#xff09;到显存&#xff0c;然后再进行绘制&#xff08;在这个过程中会产生一次DrawCall…

书生-浦路大模型全链路开源体系

2023年&#xff0c;大模型成为热门关键词 论文链接 大模型已经成为发展通用人工智能的重要途经 模型评测过程&#xff1a;从模型到应用 全链条开源开发体系 | 数据&#xff1a; 多模态融合 万卷包含文本、图像和视频等多模态数据&#xff0c;涵盖科技、文学、媒体、教育和法…

力扣hot100 二叉树的层序遍历 BFS 队列

&#x1f468;‍&#x1f3eb; 题目地址 时间复杂度&#xff1a; O ( n ) O(n) O(n)空间复杂度&#xff1a; O ( n ) O(n) O(n) &#x1f60b; 队列写法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode…

Pycharm恢复默认设置

window 系统 找到下方目录-->删除. 再重新打开Pycharm C:\Users\Administrator\.PyCharm2023.3 你的不一定和我名称一样 只要是.PyCharm*因为版本不同后缀可能不一样 mac 系统 请根据需要删除下方目录 # Configuration rm -rf ~/Library/Preferences/PyCharm* # Caches …

vue的小入门

vue的快速上手 Vue概念 是一个用于构建用户界面的渐进式框架优点&#xff1a;大大提高开发效率缺点&#xff1a;需要理解记忆规则 创建Vue实例 步骤&#xff1a; 准备容器引包创建Vue实例new Vue()指定配置项el data>渲染数据 el指定挂载点&#xff0c;选择器指定控制…

OpenCV-14图片的四则运算和图片的融合

一、图片的四则运算 1. 加法运算 通过使用API add来执行图像的加法运算 cv2.add&#xff08;src1&#xff0c; src2&#xff09;需要再其中传入两张图片。 图片就是矩阵&#xff0c;图片的加法运算就是矩阵的加法运算。 因此加法运算中要求两张图的shape必须是相同的。 首…

【vue/uniapp】使用 uni.chooseImage 和 uni.uploadFile 实现图片上传(包含样式,可以解决手机上无法上传的问题)

引入&#xff1a; 之前写过一篇关于 uview 1.x 版本上传照片 的文章&#xff0c;但是发现如果是在微信小程序的项目中嵌入 h5 的模块&#xff0c;这个 h5 的项目使用 u-upload 的话&#xff0c;图片上传功能在电脑上正常&#xff0c;但是在手机的小程序上测试就不会生效&#x…

C# windows服务程序开机自启动exe程序

我们使用传统的Process.Start(".exe")启动进程会遇到无法打开UI界面的问题&#xff0c;尤其是我们需要进行开启自启动程序设置时出现诸多问题&#xff0c;于是我们就想到采用windows服务开机自启动来创建启动一个新的exe程序&#xff0c;并且是显式运行。 首先是打开…