redis重点复习

1.AOF日志

5 04 | AOF日志:宕机了,Redis如何避免数据丢失?

Redis是先执行命令,把数据写入内存,然后才记录日志

 

 

 

AOF日志重写:当我们对一个列表先后做了6次修改操作后,列表的最后状态是[“D”, “C”, “N”],此时,只用LPUSH u:list “N”, “C”, "D"这一条命令就能实现该数据的恢复,这就节省了五条命令的空间。对于被修改过成百上千次的键值对来说,重写能节省的空间当然就更大了。

2.数据不一致

不一致原因:

  • 查,命中从redis取出;没命中,查mysql,之后保存缓存

  • 修改或保存,先删除缓存,再更新mysql

问题1:修改时,删除缓存,但是还没有更新mysql,此时查请求,发现没有命中缓存,查mysql之后保存缓存。(这样mysql就与redis数据不一致了)

  • 查,命中从redis取出;没命中,查mysql,之后保存缓存

  • 修改保存,先更新mysql,再删除缓存

问题2:查请求,没有命中缓存,查数据库,然后需要更新缓存;假如在查完之后更新缓存之前,有一个update请求,更新mysql,删除缓存,此时读请求做更新缓存。

https://blog.csdn.net/wd_boy/article/details/88289763

  • 延时双删:1.del redis;2.update mysql;3.sleep;4.del redis

  • databus:insert、update、delete操作都是先更新mysql;根据binlog发送mq,更新redis

3.更新数据为什么不直接更新redis,而是删除呢?

没有并发:

1、先更新数据库,再更新缓存:如果更新数据库成功,但缓存更新失败,此时数据库中是最新值,但缓存中是旧值,后续的读请求会直接命中缓存,得到的是旧值。

2、先更新缓存,再更新数据库:如果更新缓存成功,但数据库更新失败,此时缓存中是最新值,数据库中是旧值,后续读请求会直接命中缓存,但得到的是最新值,短期对业务影响不大。但是,一旦缓存过期或者满容后被淘汰,读请求就会从数据库中重新加载旧值到缓存中,之后的读请求会从缓存中得到旧值,对业务产生影响。

如果有并发:

1、先更新数据库,再更新缓存,写+读并发:线程A先更新数据库,之后线程B读取数据,此时线程B会命中缓存,读取到旧值,之后线程A更新缓存成功,后续的读请求会命中缓存得到最新值。这种场景下,线程A未更新完缓存之前,在这期间的读请求会短暂读到旧值,对业务短暂影响。

2、先更新缓存,再更新数据库,写+读并发:线程A先更新缓存成功,之后线程B读取数据,此时线程B命中缓存,读取到最新值后返回,之后线程A更新数据库成功。这种场景下,虽然线程A还未更新完数据库,数据库会与缓存存在短暂不一致,但在这之前进来的读请求都能直接命中缓存,获取到最新值,所以对业务没影响。

3、先更新数据库,再更新缓存,写+写并发:线程A和线程B同时更新同一条数据,更新数据库的顺序是先A后B,但更新缓存时顺序是先B后A,这会导致数据库和缓存的不一致。

4、先更新缓存,再更新数据库,写+写并发:与场景3类似,线程A和线程B同时更新同一条数据,更新缓存的顺序是先A后B,但是更新数据库的顺序是先B后A,这也会导致数据库和缓存的不一致。

4.缓存穿透、雪崩、

缓存穿透

原因:查询不存在的key

解决办法:①.布隆过滤器;②.为不存在的值,添加默认值;③.以缓存为主,没有就是没有,有专门的异步线程去加载缓存(异步线程加载缓存,定期或者根据条件触发去加载)

缓存击穿

原因:某个key失效时,正好有大量请求

解决办法:①.更新缓存时,添加分布式锁,只能有一个线程去查mysql,然后更新缓存;②.以缓存为主,没有就是没有,有专门的异步线程去加载缓存(异步线程加载缓存,定期或者根据条件触发去加载)

缓存雪崩

原因1:大量缓存同一时间失效

解决办法:避免同一时间失效,设置过期时间时,加一个随机值,尽量避免同一时间大量过期;限流,服务降级,返回默认值

原因2:redis宕机了

解决办法:使用的热数据尽量分散在不同的机器;多个副本,实现高可用;限流,服务降级,返回默认值

5.缓存污染

在一些场景下,有些数据被访问的次数非常少,甚至只会被访问一次。当这些数据服务完访问请求后,如果还继续留存在缓存中的话,就只会白白占用缓存空间。这种情况,就是缓存污染。

如何解决:

回忆一下缓存淘汰策略:

  volatile-lru :从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。

 

  volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。

  volatile-random:从已设置过期时间的数据集(server.db[i].expires)中随机挑选数据淘汰。

  allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰。

  allkeys-random:从数据集(server.db[i].dict)中随机挑选数据淘汰。

  no-envivtion(驱逐):禁止驱逐数据。

Redis 从 4.0 版本开始增加了 LFU 淘汰策略:

LFU 缓存策略是在 LRU 策略基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用 LFU 策略筛选淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出缓存。如果两个数据的访问次数相同,LFU 策略再比较这两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。

 

6.RDB日志

6 05 | 内存快照:宕机后,Redis如何实现快速恢复?

Redis提供了两个命令来生成RDB文件,分别是save和bgsave。

  • save:在主线程中执行,会导致阻塞;

  • bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件生成的默认配置。

7.生成RDB快照文件时,redis可以有写操作么?

Redis就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。如果主线程要修改一块数据(例如图中的键值对C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave子进程会把这个副本数据写入RDB文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

8.Redis崩溃恢复

Redis 4.0中提出了一个混合使用AOF日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作。

9.主从同步

主从从模式

  1. 从库发送同步命令

  2. 主库生成RDB快照文件,同步到从库

  3. 同步这段时间的写操作,主库先放到缓存,然后从库加载完所有数据后,再把这部分数据写到从库

10.主从断网

  • 2.8之前,再全量同步一次

  • 2.8之后,增量同步。具体实现,主库会把写操作写到环形缓冲区,主库记录自己写的位置,从库记录自己读到的位置。(所以当这个缓冲区比较小时,如果主从同步超过了这个缓冲区,就会造成主从数据不一致问题)

11.主从数据不一致解决

  1. 业务可以接受,忽略

  2. 强制走主库

  3.  

12.哨兵机制

监控、选主、通知

判断主库是否下线:哨兵集群,少数服从多数

选主:筛选、打分最后定主库(网络好坏,主从同步快慢)

13.事务

当命令不正确时,事务所有语句都会回滚

当数据结构不正确时,事务前后都会被执行,不会回滚

14.Redis Cluster

一个切片集群共有16384个哈希槽,哈希槽类似于分区,每个键值对都会根据它的key,被映射到一个哈希槽中。

Redis实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散。

客户端和集群建立连接后,实例就会把哈希槽的分配信息发给客户端。客户端收到哈希槽信息后,会把哈希槽信息缓存在本地

 

Redis Cluster重定向:客户端给一个实例发送数据读写操作时,这个实例上并没有相应的数据,客户端要再给一个新实例发送操作命令。

1.当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端返回下面的MOVED命令响应结果,这个结果中就包含了新实例的访问地址。

2.通过返回的MOVED命令,就相当于把哈希槽所在的新实例的信息告诉给客户端了。这样一来,客户端就可以直接和172.16.19.5连接,并发送操作请求了。

 

Ask

1.客户端向实例2发送请求,但此时,Slot 2中的数据只有一部分迁移到了实例3,还有部分数据没有迁移。

2.客户端就会收到一条ASK报错信息GET hello:key (error) ASK 13320 172.16.19.5:6379

这个结果中的ASK命令就表示,客户端请求的键值对所在的哈希槽13320,在172.16.19.5这个实例上,但是这个哈希槽正在迁移。

3.客户端需要先给172.16.19.5这个实例发送一个ASKING命令。这个命令的意思是,让这个实例允许执行客户端接下来发送的命令。

4.客户端再向这个实例发送GET命令,以读取数据。

ASK命令表示两层含义:第一,表明Slot数据还在迁移中;第二,ASK命令把客户端所请求数据的最新实例地址返回给客户端,此时,客户端需要给实例3发送ASKING命令,然后再发送操作命令。

和MOVED命令不同,ASK命令并不会更新客户端缓存的哈希槽分配信息。

15.Redis数据淘汰机制

https://www.cnblogs.com/tqlin/p/11864644.html

Redis在每个服务客户端执行一个命令的时候,都会先检测使用的内存是否超额。

在Redis中,我们可以设置Redis的最大使用内存大小(server.maxmemory)。当Redis内存数据集大小上升到一定程度的时候,就会施行数据淘汰机制。Redis提供了一下6种数据淘汰机制:

volatile-lru :从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中随机挑选数据淘汰。

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰。

allkeys-random:从数据集(server.db[i].dict)中随机挑选数据淘汰。

no-envivtion(驱逐):禁止驱逐数据。

LRU机制: Redis保存了lru计数器server.lrulock,会定时的去更新(redis定时程序severCorn()),每个Redis对象都会设置相应的lru值,每次访问对象的时候,Redis都会更新redisObject.lru。

LRU淘汰机制: 在数据集中随机挑选几个键值对,取出其中lru最大的键值对淘汰。所以,Redis并不能保证淘汰的数据都是最近最少使用的,而是随机挑选的键值对中的。

TTL机制: Redis数据集结构中保存了键值对过期时间表,即 redisDb.expires。

TTL淘汰机制: 在数据集中随机挑选几个键值对,取出其中最接近过期时间的键值对淘汰。所以,Redis并不能保证淘汰的数据都是最接近过期时间的,而是随机挑选的键值对中的。

自己实现一个LRU算法:

  1. 一个队列用于存放所有数据

  2. 当用户访问某个数据时,把队列里的数据取出放在表头

  3. 当数据满时,把表尾的数据清除

  4.  

16.秒杀系统设计

1.秒杀开始前:静态页面

2.秒杀开始:判断库存是否足够

  • 库存放到redis缓存中
  • redis判断库存是否充足
  • 如果库存足够,扣减库存。注意,第二步和第三步需要原子操作,所以使用Lua脚本实现

3.秒杀下单:(因为获取到购买资格的用户比较少,所以mysql可以支撑)

  • 用户获得商品购买资格
  • 下单、支付使用mysql,需要使用事务保证数据一致性,错误之后进行回滚
posted @ 2022-04-08 14:27  刘尊礼  阅读(59)  评论(0)    收藏  举报