中间件知识点-存储中间件(redis)一

redis

redis基本命令及数据结构使用
主要数据结构:string、list、hash、集合set、有序集合zset

hash:
HSET key field value (在一个key中存储单个键值)
HMSET key field value [field value …] (在一个key中存储多个键值,即key的值为对象)

除了增删改查,还可以进行一些运算操作。具体有哪些命令可以看帮助文档。
通过help @sorted_set/@hash来查看帮助文档。

最佳实践
hash单个key不能存储大数据量数据,因为redis是单线程的,一次访问大数据量需要时间,会影响其它操作访问,太多会卡顿。
redis集群架构,将大数据量的数据分成多段,存储在不同redis中(即支持分片)。
Redis集群架构下不适合大规模使用hash数据结构,因为hash类型不支持分片,所以所有的hash类型的数据都会在一个hash槽中,这样就会导致某个节点的内存占用过大,而另外一些节点的内存占用很少,这样就会导致集群不均衡。
点赞实际场景可以用到redis,但是需要实现redis高可用

jedis、redistemplate的区别?
jedis和RedisTemplate都是Java操作Redis的客户端,但是它们的实现方式不同。jedis是Redis官方推荐的Java客户端,而RedisTemplate是Spring Data Redis提供的一个Redis客户端,它是对jedis和lettuce的封装。在Spring Boot 2.0之后,默认使用lettuce作为RedisTemplate的底层实现,但是也可以通过配置使用jedis。
jedis的性能比RedisTemplate要高,至少是RedisTemplate的3倍以上。但是jedis的实例是非线程安全的,需要通过JedisPool连接池去管理实例,在多线程情况下需要让每个线程有自己独立的jedis实例。而RedisTemplate是线程安全的,可以直接在多线程环境下使用。

Redis的单线程和高性能
Redis是单线程吗?
Redis 的单线程主要是指 Redis 的网络 IO 、命令的执行和键值对读写是由一个线程来完成的,这也是Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。所以说redis严格来说不是单线程,只是对所有的客户端来说,其发送命令到服务端后,是由1个线程来管理和执行他的命令的。

Redis 单线程为什么还能这么快?
因为它所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换性能损耗问题。正因为 Redis 是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如keys),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。

Redis 单线程如何处理那么多的并发客户端连接?
(不是说第一条redis命令执行完后,第二条才开始执行,而是会使用多路复用,来达到并发效果)
Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和连接事件放到队列中(IO多路复用程序有个队列),依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
在这里插入图片描述
(其实这里面说的不怎么对,io多路复用的原理应该是将连接事件和io事件分开,连接和io操作由不同的处理器来处理,这样就可以支持更多连接)

一条命令如set key value,它的流程是这样的:
1.和服务端建立连接
2.发送命令数据
3.服务端执行
4.服务端把执行结果发给客户端。

多路复用就是说只要s0,s1,s2,s3客户端连接到服务端后就先把其放到服务端多路复用程序中,先占个位,而不用等它们的value传到服务端才占位,哪个先传完value事件分派器就先处理哪个,这样就降低了事件处理器等待时间,提高了处理能力。这就是NIO。不会因为前面事件阻塞了,而导致后面的事件也被阻塞。
IO多路复用看netty io多路复用

Redis持久化
Redis持久化,有两种
1.RDB快照(snapshot)(默认)(bgsave使用写时复制(COW)机制,缺点是在redis宕机时会导致数据丢失)
可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。
比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:
#save 60 1000 //关闭RDB只需要将所有的save保存策略注释掉即可
还可以手动执行命令生成RDB快照,进入redis客户端执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。

save\bgsave命令区别:
1.save是同步的,bgsave是异步的
2.save缺点是会阻塞客户端命令,bgsave缺点是需要fork子进程,消耗内存

bgsave的写时复制(COW)机制:
Redis 借助操作系统提供的写时复制技术(Copy-On-Write, COW),在生成快照的同时,依然可以正常处理写命令。简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。
bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些数据也都是读操作,那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据,那么,这块修改后的数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
写时复制机制(原理)也用在CopyOnWriteList中。
缺点是:N 秒内数据集至少有 M 个改动才会进行保存副本,可能在redis宕机的时候,导致未持久化数据丢失。所以一般使用下面方式,即AOF方式。
2.AOF(append-only file)
(将修改的每一条指令记录进文件appendonly.aof中(类似mysql redo日志)(先将指令记录写入os cache,每隔一段时间fsync到磁盘))
从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件appendonly.aof中(先写入os cache,每隔一段时间fsync到磁盘)
AOF重写:AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件
AOF重写redis会fork出一个子进程去做(与bgsave命令类似),不会对redis正常命令处理有太多影响

RDB 和 AOF ,我应该用哪一个?
生产环境可以都启用,redis启动时如果既有rdb文件又有aof文件则优先选择aof文件恢复数据,因为aof一般来说数据更全一点。

Redis 4.0 混合持久化
(解决重放aof慢的情况,在aof重写过程做了修改。把重写一刻时间点之前内存的数据做rdb快照,时间点之后的内存数据使用aof格式存储。然后存到新的aof文件中)
重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
混合持久化优点集合了AOF和rdb持久化方式的优点,混合持久化也是AOF持久化方式,只不过做了些文件压缩优化。
混合持久化优点:文件变小,此重启效率大幅得到提升。

Redis数据备份策略(通过备份rdb/aof文件来进行redis数据备份)

  1. 写crontab定时调度脚本,每小时都copy一份rdb或aof的备份到一个目录中去,仅仅保留最近48小时的备份
  2. 每天都保留一份当日的数据备份到一个目录中去,可以保留最近1个月的备份
  3. 每次copy备份的时候,都把太旧的备份给删了
  4. 每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏

Redis主从架构
(当master节点宕机后,不会重写选举新的主节点,要结合哨兵集群架构一起使用)
master为主节点,运行的时候主要是用主节点的数据,同时对于主节点写数据时,会同步给从节点slave,当主节点宕机的时候会使用到从节点,并将从节点变为主节点。
默认不会主动将从节点变为主节点,要使用到哨兵集群架构才可以
也可以设置读写分离,主节点用于写,从节点用于读。一般只设置一个节点为写,不然数据容易乱。

redis数据同步(主从复制)原理
如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个PSYNC命令给master请求复制数据。
master收到PSYNC命令后,会在后台进行数据持久化通过bgsave生成最新的rdb快照文件,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。
当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
主从复制(全量复制)流程图:
在这里插入图片描述
这里选用rdb来保存快照和上面持久化方式无关,无需进行配置。是由服务端决定的。不开启也会用rdb方式来保存。
步骤5表示将master缓存在内存中的redis命令发送给slave,由slave执行相同的命令,而不是发送数据。
上面是全量复制

数据部分复制:(比如slave断开后重连时,只需复制部分
repl buffer缓存只有最近几分钟的数据,如果slave太久没从master同步数据的话就不能只从缓存中同步数据,还需从master持久化数据中拿。)
当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,redis改用可以支持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。
master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制。
主从复制(部分复制,断点续传)流程图:
在这里插入图片描述
如果master进程id变化了,或者从节点数据下标offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制。

如果有很多从节点,为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如下架构,让部分从节点与从节点(与主节点同步)同步数据。
在这里插入图片描述

Redis哨兵集群高可用架构(主要用来监控redis实例节点)
哨兵一般最少为3个
哨兵可以通过Jedis连接,springboot默认不使用jedis而是用lettuce,可以切换为jedis。
RedisTemplate是Spring Data Redis提供的一个Redis客户端,它是对jedis和lettuce的封装。
哨兵集群架构的缺点:
哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率。
单台redis并发量为10几万,这个量已经不小了,但是对于大的互联网公司来说还是不够的。
在这里插入图片描述

Redis Cluster集群
(可以解决哨兵集群架构只有一个主节点能对外提供服务的情况)redis3.0及之后支持
(但redis集群不支持读写分离,哨兵集群可以)
redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵∙也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点群(比如一个节点群为一主两从)(官方推荐不超过1000个节点,一般几十上百就很多了)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。
在这里插入图片描述

高可用集群模式会将数据分散存储到不同的主从节点群,而不是每个节点群的数据都一样。

Redis高可用集群搭建
redis集群需要至少三个master节点
redis集群和哨兵架构不一样的是,redis集群不支持读写分离,即不能只对slave为读,slave只能用于主从备份,切换为master。
master节点最好要和slave节点不在同一个机器上。之所以这么做是为了防止如果一台机器宕机了,则主从节点就直接不能用了。所以使用交叉模式。master节点宕机后会选举slave节点作为新的master节点。
将下面6个节点分成3分,做成3个主从节点群,且前面3个默认当成主节点
1)/usr/local/redis‐5.0.3/src/redis‐cli ‐a zhuge ‐‐cluster create ‐‐cluster‐replicas 1 1
92.168.0.61:8001 192.168.0.62:8002 192.168.0.63:8003 192.168.0.61:8004 192.168.0.62:8005 192.168.0.63:8006
第一次搭建要将防火墙的什么给关掉
redis集群可以通过jedis或springboot来访问

Redis集群原理
(讲redis集群中客户端怎么定位去存取数据的,包括节点槽位变化后客户端怎么能够去最新的槽点存取数据、节点间通信、节点选举)
高可用集群模式会将数据分散存储到不同的主从节点群,而不是每个节点群的数据都一样。

Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每 个节点中。 当 Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。这 样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不 一致的情况,还需要纠正机制来实现槽位信息的校验调整。

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

跳转重定位 :比如节点槽位更新了,但是客户端配置信息还没更新时。
当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。

Redis集群节点间的通信机制(为了节点信息的同步)
使用的是gossip协议,gossip协议可以降低各节点通信的压力
1.redis cluster节点间采取gossip协议进行通信
维护集群的元数据(集群节点信息,主从角色,节点数量,各节点共享的数据等)有两种方式:集中式和gossip
集中式:
优点在于元数据的更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的时候立即就可以立即感知到;不足在于所有的元数据的更新压力全部集中在一个地方,可能导致元数据的存储压力。 很多中间件都会借助zookeeper集中式存储元数据。
gossip:
gossip协议包含多种消息,包括ping,pong,meet,fail等等。gossip协议的优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后。
每个节点都有一个专门用于节点间gossip通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口。 每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他几点接收到ping消息之后返回pong消息。
2.如何解决网络抖动?加入timeout机制。
表示当某个节点持续timeout的时间失联时,才可以认定该节点出现故障,需要进行主从切换。

Redis集群选举原理
(slave节点会向其他群组所有节点发送选举要求,其他master节点和slave节点都会发,但是只有master节点才会响应。另外在一次选举周期内如果多个slave节点向其他组节点发送选举请求,其他master节点只会响应该周期内第一次发请求的slave节点。得票过半即可成为新master,如果得票相同则进入下一次选举。另外从节点不是主节点一进入fail状态就进行选举,而是会延迟选举)
当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:
1.slave发现自己的master变为FAIL
2.将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST 信息
3.其他集群节点收到该信息,只有master节点响应,判断请求者的合法性(即其他master确认该从节点的master变成fail后),并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack(即只会响应该周期内第一次发请求的slave节点)
4.尝试failover的slave收集master返回的FAILOVER_AUTH_ACK
5.slave收到超过半数master的ack后变成新Master(这里解释了集群为什么至少需要三个主节点,如果只有两个,当其中一个挂了,只剩一个主节点是不能选举成功的)
6.slave广播Pong消息通知其他集群节点。
从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票
•延迟计算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
•SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)
redis是一个AP架构,更多的是保证可用性
zookeeper是一个CP,更多的是保证一致性。

集群脑裂数据丢失问题?(配置过半同步机制)
(即redis集群如何解决在一组节点中出现多个主节点的时候)
(如果没有过半写成功才算成功机制则会有脑裂问题,有的话就不会)
可以在redis配置里加上参数min‐replicas‐to‐write 1来配置过半同步机制
(一般我们不用去解决脑裂问题,我们进量保证AP,因为就算redis数据丢失了,我们还有数据库进行兜底)
redis集群没有过半机制会有脑裂问题,网络分区导致脑裂后多个主节点对外提供写服务,一旦网络分区恢复,会将其中一个主节点变为从节点,这时会有大量数据丢失(因为之前的主节点变为从节点后,里面的数据就不能提供访问了,而且之前的主节点有些数据在其它节点是没有的)。
规避方法可以在redis配置里加上参数(这种方法不可能百分百避免数据丢失,参考集群leader选举机制):
min‐replicas‐to‐write 1 //写数据成功最少同步的slave数量,这个数量可以模仿大于半数机制配置,比如集群总共三个节点可 以配置1,加上leader就是2,超过了半数。
注意:这个配置在一定程度上会影响集群的可用性,比如slave要是少于1个,这个集群就算leader正常也不能
提供服务了,需要具体场景权衡选择。

zookeeper在写入信息的时候是除了master写入成功外,还需要半数节点同步成功才算成功的。
redis配置了min‐replicas‐to‐write 1 后是如何避免脑裂的?如下:比如上面的主节点被以为是宕机了(实际没有),则会选取右下角为主节点,而左下角变成了右下角的从节点,而不是上面节点的从节点,所以上面节点就没有了从节点,当向上面主节点写入数据的时候就没有slave同步,同步的slave数量就为0,这样上面的主节点就没法写入数据成功,只会向右下角主节点写入,这样就避免了脑裂问题。
一般情况下,我们是优先保证可用性,因为就算redis数据丢失了,也没有关系,丢失了会从数据库中拿。只是会影响响应时间。

Redis集群为什么至少需要三个master节点,并且推荐节点数为奇数(推荐为奇数而不是必须)?
(网上有说master必须为奇数,那是错的,不要被误导了)
1.至少三个master节点是因为只有两个master节点,当其中一个挂了,是达不到选举新master的条件的。因为slave只会得到一票(另外一个master那一票),这样票数是不符合过半要求。
2.master节点数为奇数,这里说的不是必须的,而是推荐为奇数。这是从节省机器资源角度出发说的。网上有说master必须为奇数,那是错的,不要被误导了。

集群的水平扩展
当添加节点成功以后,新增的节点不会有任何数据,因为它还没有分配任何的slot(hash槽),我们需要为新节点手工分配hash槽,即迁移槽位,迁移槽位时会将槽位原有的数据也会自动迁移过来。

分布式锁
1.借助redis的setnx命令来实现
2.redisson中间件分布式锁原理
redisson分布式锁用到了lua脚本+锁续命来实现分布式锁
在这里插入图片描述
lua脚本实现效果相当于执行setnx命令

redisson加锁自旋逻辑:加锁失败后不会一直while(true),逻辑如下:
会等待25s(锁超时后的时间)后再次加锁,并且不会阻塞。而不是立即加锁,失败后再立即尝试再次加锁。
对于没有抢到锁的线程,会进行订阅一个频道,这个频道的名称和锁名称有关,在锁被释放的时候会被通知到。
3.分布式锁要实现的完美点有个解决方案叫锁续命。就是定时延长锁失效时间,比如原来是30s后失效,等10后再设置其为30s后失效。比如redisson分布式锁就会用到锁续命逻辑,以防止锁过期而被自动释放。
给锁加过期/超时时间是为了避免死锁。
4.锁失效问题
锁失效问题即:master上存在key,即加锁成功,但是未同步到到slave,此时master宕机了,slave选为主节点后不存在key,这就是锁失效问题。没有好的办法解决。因为redis主要是保证了AP,如果CP也保证的话,性能就不是很好。zookeeper保证了CP,所以zookeeper性能没redis那么好。网上说redlock可以解决锁失效问题其实是误导,100%解决锁失效是不存在的 。
5.分布式锁解决并发的根本原理是将并行请求转换成串行化。其实和高并发是有违背的。

redis 的缓存穿透、缓存击穿、缓存雪崩的区别及解决方案?
缓存穿透(Cache Penetration):指的是查询一个不存在的数据,由于缓存没有命中,数据库中也不存在该数据,请求会直接穿透到数据库,导致数据库压力过大,甚至可能造成宕机。
缓存击穿(Cache Miss):指的是某一个热点key在缓存中过期或者被清除,此时大量请求涌入,由于缓存中没有该key的缓存数据,请求会直接穿透到数据库,导致数据库压力过大,甚至可能造成宕机。
缓存雪崩(Cache Avalanche):指的是缓存中大量的数据在同一时间过期失效,导致大量的请求涌入数据库,导致数据库压力过大,甚至可能造成宕机。

缓存穿透解决方案:
比如删除了数据库数据和缓存所有数据之后,访问了缓存和数据库都没法找到数据(缓存层和存储层都不会命中)。缓存和数据库压力都会特别大。
造成缓存穿透的基本原因有两个:
第一, 自身业务代码或者数据出现问题。
第二, 一些恶意攻击、 爬虫等造成大量空命中。
解决方案是:
a.设置空缓存,同时将空缓存的超时时间设置短一点。
b.布隆过滤器(如果用空缓存占用空间可能会非常大)
对于恶意攻击,向服务器请求大量不存在的数据造成的缓存穿透,还可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一般都能够过滤掉,不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
但布隆过滤器这种方法适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景, 代码维护较为复杂, 但是缓存空间占用很少。

缓存击穿解决方案:
设置热点数据永远不过期
加互斥锁,如果缓存为null则加锁从数据库中取数据
接口限流与熔断,降级

缓存雪崩指的是缓存中大量的数据在同一时间过期失效。
缓存雪崩解决方案:
1.使用redis集群架构和限流降级中间件。
2.使用多级缓存,如jvm缓存(jvm缓存每秒能扛百万级别并发,redis每秒10万并发)。原理类似HashMap(实际不是,而是使用ehcache等中间件),但是使用jvm缓存的话如果有多个进程,则需要进行数据同步,解决策略是使用mq来实现数据同步。
3.设置随机的过期时间,避免同一时间大量缓存同时失效。
4.使用分布式锁,保证在缓存失效时只有一个请求去重新加载数据,其他请求等待该请求完成后再获取数据。

使用mq来实现数据同步,又会出现短时间数据不一致,要不要再使用分布式锁来解决?
答案是否,因为没有绝对的数据一致,如果再加分布式锁就是多度设计了,要自己权衡。
在实际使用mq的时候,其不会在每个方法操作后再使用mq发消息
一般会这样做
1.只会将热点中的热点数据放到hashmap
2.一般会将map put的代码抽离处理,而不是直接将jvm缓存操作逻辑代码写在增删改查方
法里。原理是对redis操作进行拦截处理(搭配一个热点拦截系统)。

突发性热点缓存重建导致系统压力暴增问题解决?
(即同一时刻大量并发请求,同时访问了数据库,如何解决压力暴增、缓存和数据库双写不一致。参考缓存雪崩解决方案)
意思是:对于瞬间大并发量访问没有缓存的数据。即同一时刻大量访问冷门数据,此时所有请求都会同时访问数据库,而不是第一个请求访问数据库建了缓存后,第二个请求就可以用缓存,
而是所有请求都会访问数据库。
解决方案是:使用DCL双重校验锁机制。锁使用的是分布式锁
分布式锁使用互斥锁的话性能没那么好,如果读多写少的话一般使用分布式读写锁。
读写锁使用的也是redis的setnx来实现的。
分布式锁底层都是使用lua脚本实现

对于新浪微博普通热点事件可以用上面的双重校验锁解决方案。但是对于上亿级的流量架构这种方案不一定能扛住。
对于亿级流量使用上面说的缓存雪崩的解决方案:
即使用redis集群、限流降级中间件、双缓存机制等结合来使用(同样参考缓存雪崩解决方案)

开发规范
一.键值设计
A.key名设计
可读性:
业务名(或数据库名):表名:id
trade:order:1
简洁:
user:{uid}:friends:messages:{mid} 简化为 u:{uid}🇫🇷m:{mid}

B.value设计
(拒绝bigkey(防止网卡流量、慢查询)
一般认为value超过10KB就是bigkey
一般来说,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。
反例:一个包含200万个元素的list。)

bigkey的危害:
1.导致redis阻塞
2.网络拥塞
3. 过期删除(如果不是使用redis4.0过期异步删除而是同步删除,那么也会阻塞redis)

bigkey的产生:
一般来说,bigkey的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的,来看几个例子:
(1) 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
(2) 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
(3) 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需
要注意,第一,是不是有必要把所有字段都缓存;第二,有没有相关关联的数据,有的同学为了图方便把相关数据都存一个key下,产生bigkey。

如何优化bigkey:


  1. big list: list1、list2、…listN
    big hash:可以将数据分段存储,比如一个大的key,假设存了1百万的用户数据,可以拆分成
    200个key,每个key下面存放5000个用户数据
    如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmget,而不是hgetall),删除也是一样,尽量使用优雅的方式来处理。
    2.【推荐】:选择适合的数据类型。
    例如:实体类型(要合理控制和使用数据结构,但也要注意节省内存和性能之间的平衡)
    反例:
    1 set user:1:name tom
    2 set user:1:age 19
    3 set user:1:favor football
    正例:
    hmset user:1 name tom age 19 favor football
    3.【推荐】:控制key的生命周期,redis不是垃圾桶。
    建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期)

二.命令使用
1.【推荐】 O(N)命令关注N的数量
2.【推荐】:禁用命令
3.【推荐】合理使用select
4.【推荐】使用批量操作提高效率
5.【建议】Redis事务功能较弱,不建议过多使用,可以用lua替代

三、客户端使用
1.【推荐】
避免多个应用使用一个Redis实例
正例:不相干的业务拆分,公共数据做服务化。
2.【推荐】
使用带有连接池的数据库,可以有效控制连接,同时提高效率。
3.【建议】
高并发下建议客户端添加熔断功能(例如sentinel、hystrix)
4.【推荐】
设置合理的密码,如有必要可以使用SSL加密访问
5.【建议】
Redis对于过期键有三种清除策略:

  1. 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期
    key
  2. 主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一
    批已过期的key
  3. 当前已用内存超过maxmemory限定时,触发主动清理策略

内存淘汰策略是指当redis内存超出时,会根据用户设置的淘汰策略删除对应的key。
主要是以下几种:
volatile-lru
volatile-ttl
volatile-random
allkeys-lru
allkeys-random
volatile表示只对设置了expireTime的key进行删除。allkeys表示针对所有key进行删除。

redis性能优化
比如使用分段锁
比如将一个库存分成10份,这样就变成了10个锁,这样就降低了锁被占用而不能加锁的情况,和前面讲的mysql加锁优化差不多。

Redis队列Stream、Redis多线程详解
redis队列Stream
(Stream redis5.0提出 借鉴了kafka)
总的来说,如果是中小项目和企业,在工作中已经使用了 Redis,在业务量不是很大,而又需要消息中间件功能的情况下,可以考虑使用 Redis 的 Stream 功能。但是如果并发量很高,资源足够支持下,还是以专业的消息中间件,比如RocketMQ、Kafka 等来支持业务更好。

Redis多线程详解
redis为什么这么快?
1.内存操作
2.单线程
3.非阻塞io epoll 多路复用(涉及到nio reactor模式)
4.resp协议简单

Redis 中的线程和 IO 概述
1.Redis 基于 Reactor 模式开发了自己的网络事件处理器 - 文件事件处理器
(file event handler,后文简称为 FEH)
2.文件事件处理器的几个组成部分(socket、IO多路复用器、文件事件分派器、事件处理器)。
3.总结:客户端和 Redis 服务器通信的整个过程
a.Redis 启动初始化时,将连接应答处理器跟 AE_READABLE 事件关联。
b.若一个客户端发起连接,会产生一个AE_READABLE 事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个 socket的 AE_READABLE 事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。
c.当客户端向 Redis 发请求时(不管读还是写请求),客户端 socket 都会产生一个AE_READABLE 事件,触发命令请求处理器。处理器读取客户端的命令内容,然后传给相关程序执行。
d.当 Redis 服务器准备好给客户端的响应数据后,会将 socket 的 AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在 socket 产生一个 AE_WRITABLE 事件,由对应命令回复处理器处理,即将准备好的响应数据写入 socket,供客户端读取。
e.命令回复处理器全部写完到 socket 后,就会删除该 socket 的 AE_WRITABLE事件和命令回复处理器的映射。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值