Redis系列(八)Redis常用实战场景

Redis数据丢失场景

因为Redis是AP模型,所以它的数据是不可靠的,存在一些丢失数据的场景。

持久化丢失

  • 采用RDB或者不持久化, 会有数据丢失,因为是手动或者配置以快照的形式来进行备份。
  • 采用AOF默认会有1s的数据丢失。

解决方法

启用AOF,以命令追加的形式进行备份,但是默认也会有1s丢失,这是在性能与数据安全性中寻求的一个最适合的方案,如果为了保证数据一致性,可以将配置更改为always,但是性能很慢,一般不用。

# appendfsync always

主从切换

因为主从同步是异步的,主服务器以异步的方式同步给从服务器,这样做提升了性能,但是由于是异步同步到从。所以存在数据丢失的可能。

  1. master写入数据k1,由于是异步同步到slave,当master没有同步给slave的时候,master挂了。
  2. slave会成为新的master,并且没有同步k1。
  3. master重启,会成为新master的slave,同步数据会清空自己的数据,从新的master加载。
  4. 此时,数据k1丢失。

sentinel脑裂

发生脑裂的时候,会产生2个master,数据丢失过程如下。

  1. 假如有个sentinel集群与Redis集群,如图
    在这里插入图片描述

  2. 当我的master机器(192.168.1.1)跟另外2台发生分区容错,网络断开。但192.168.1.1本身没有挂。

  3. sentinel2跟sentinel3数量大于三分之二,满足故障转移条件(sentinel故障转移需要过半),这个时候会从2个slave中选出一个master,此时系统就会存在两个master。这里假设192.168.1.2是第二个master

  4. 客户端假如连接到sentinel1,数据会写入192.168.1.1;连接到sentinel2,数据就会写入192.168.1.2。同时有2个master写入数据。假如向192.168.1.1写了k1。

  5. 当网络恢复后,192.168.1.1会变成192.168.1.2的从节点,192.168.1.1的数据全部从192.168.1.2同步。

  6. k1丢失。

解决方法

尽量减少主从切换跟sentinel数据丢失的解决办法:

min-replicas-to-write 1			//保证master至少有1个从节点。至少有1个从节点同步主节点的数据,但是由于是异步同步,所以是最终一致性 不会确保有数据写入
min-replicas-max-lag 10			//主从同步的延迟时间必须小于等于10s

Redis缓存跟DB的数据一致性问题

在并发环境下,可能会出现redis跟DB的数据不一致的情况,产生Redis缓存跟DB一致性问题。

怎么产生

  • 查询缓存逻辑:
    1. 请求查询DB之前,先去查询Redis,如果Redis存在,直接返回;如果Redis不存在,从DB查询。
    2. 从DB查询后,回写到Redis。
  • 修改数据的逻辑:
    1. 修改DB的数据,DB数据修改成功后,删除Redis的数据。
    2. 下一次查询该数据的时候,会先从DB查询,然后更新到Redis。
      • 为什么不在修改的db的同时,修改Redis数据?因为Redis里可以存的是一个json化的数据,修改其中的一个字段的话,效率较低。
  • 丢失场景:
    1. 线程A请求缓存,没有缓存,从DB拿到数据1。
    2. 线程B将数据1更改为2,并且删除Redis缓存,此时,DB的值为2。
    3. 线程A更新缓存,将数据1写入redis。
    4. 此时,redis数据为1 ,db数据为2,出现了数据一致性问题。

所以,数据一致性产生的根本问题,是查询DB 跟操作Redis不是原子性的,所以并发会导致数据一致性问题。

解决方法

强一致性方案(不可取)

  1. 采用锁机制,不让有并发。
    • 在更新的时候,采取锁的机制,不让其他线程进行删除Redis数据的操作。即,更新数据A的时候,其他线程不能查询数据A。这个方法会拖慢整个性能,违背了Redis的初衷。

所以,我们只能采用最终一致性,不应该去保证强一致性

最终一致性方案

  1. 每个缓存数据(key)设置过期时间
    • 设置过期时间,就算这条数据在Redis和DB的值不一致,也只是在有效时间内的不一致。
  2. Mysql canal等数据同步工具(用的比较少)
    • 捕捉到DB的更改,同步到相关Redis,相对比较复杂,要知道每个数据对应的缓存。

Redis缓存雪崩、穿透、击穿问题

缓存雪崩

缓存雪崩就是Redis的大量热点数据同时过期(失效)(或者redis宕机了),因为设置了相同的过期时间,刚好这个时候Redis请求的并发量又很大,就会导致所有的请求落到数据库。

解决方法

  1. 保证Redis的高可用,(比如使用Redis主从、哨兵、Cluster等方法),防止由于Redis宕机导致数据请求全部落到DB。
  2. 通过加随机数,使key在不同的时间过期
  3. 加互斥锁或者使用队列,针对同一个key只允许一个线程到数据库查询。(一般不会用这个方法,会影响性能)。
  4. 缓存定时预先更新,避免同时失效。

缓存穿透

缓存穿透是指用户一直大量请求一个缓存和数据库中都没有的数据,因为redis没有这个数据,所以请求会全部落到DB。这时的用户很可能就是攻击者,恶意搞你们公司的,攻击会导致数据库压力过大。

解决方法

  1. 封攻击者的IP。
  2. 用布隆过滤器。

布隆过滤器

实现原理

redis的布隆过滤器可以用bitMap(位图)这样的数据类型实现。布隆过滤器保存一个bit数组,数组的每个元素只有0和1两种值,其中0代表不存在某个数据,1代表存在某个数据。

存入数据的过程

当一个元素加入布隆过滤器中的时会进行如下操作:

  1. 使用布隆过滤器中的hash函数对元素值进行计算,返回对应的hash值(一般有多个hash函数得到多个hash值);

  2. 根据返回的hash值映射对应的二进制集合的下标;

  3. 将下标对应的二进制数据改成1,如下图。假设要存入一个值“test”,用3个不同的hash函数分别计算出下标2、6、8,然后把数组中下标对应的元素组改为1。
    在这里插入图片描述

判断某个数据是否存在

当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:

  1. 对给定元素再次用多个hash函数计算hash值;

  2. 根据返回的hash值判断位数组中对应的元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,则说明该元素不在布隆过滤器中。如下图,可以看出值“test1”的对应的下标元素值不全为1,所以这个值不在集合中。

在这里插入图片描述

误判
  • 由上可以看出,布隆过滤器是有可能误判的,因为两个不同的元素的hash计算结果有可能一样,发生冲突。所以,布隆过滤器能判断一定不存在,没法判断数据一定存在。

减少误判的方法

  1. 增加多种hash函数,对元素多次hash。
  2. 增大位图的数组的大小。

优点

  1. 读写速度快。布隆过滤器存储空间和插入/查询时间都是常数(即hash函数的个数)。
  2. 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。
  3. 节省空间。布隆过滤器可以用bitMap来实现。

缺点

  1. 存在误判。随着存入的元素数量增加,误判率随之增加(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息)。但是如果元素数量太少,则使用散列表足矣。
  2. 不能删除其中的数据。因为一个位可能有其他的元素占有,如果把位图设为0,那么可能db有的数据你就查不到。

使用场景

  • 大数据去重,比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5 亿以上!);
  • 垃圾邮件过滤,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;
  • 缓存穿透,将已存在的缓存放到布隆过滤器中,当黑客频繁访问不存在的缓存时迅速返回避免缓存及数据库挂掉;

缓存击穿

单个key过期的时候,有大量并发请求这个key,请求落到db,造成压力。

解决方法

  1. 使用互斥锁,回写redis,并且采用双重检查锁来提升性能!减少对DB的访问。

慢查询分析

  • 许多存储系统都会有慢日志查询,提供给开发跟运维来找到哪些指令是比较耗时的。比如Mysql。那么Redis中也会有慢日志查询。
  • 但是,Redis的慢查时间只会去统计执行指令的时间,不会统计网络消耗时间。所以没有慢查不代表没有超时。

多慢才是慢查询?

慢查询的标准是可配的,配置如下:

# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
slowlog-log-slower-than 10000		//值以微秒为单位,即1000000us = 1s;如果值为0,就强制记录所有的命令。如果是负数,则禁用慢查询。默认值10ms,查询超过10ms就认为是慢查。

# There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128					//记录的最大长度,最多存储多少条慢查记录,超过这个值,最早的就会被覆盖。

查看慢查信息

127.0.0.1:6379> slowlog get 10 //查询慢查询记录,10为要查询的数量
1) 1) (integer) 2 //慢查询标志id
   2) (integer) 1659806161 //发生的时间戳
   3) (integer) 11025 //耗时 11.025ms
   4) 1) "EVAL" //执行指令和参数
      2) "local size = redis.call('hget', KEYS[1],'size');local hashIterations = redis.call('hget', KEYS[1],'hashIterations');assert(siz... (83 more bytes)"
      3) "1"
      4) "{product:bloom}:config"
      5) "1459688"
      6) "5"
   5) "192.168.8.23:59236"
   6) ""
2) 1) (integer) 1
   2) (integer) 1659806161
   3) (integer) 11019
   4) 1) "hget"
      2) "{product:bloom}:config"
      3) "hashIterations"
   5) "?:0"
   6) ""

对慢查进行清理

127.0.0.1:6379> slowlog reset

发现慢查后如何处理

  1. 检查指令。尽量不使用hgetall, keys *等全量查询的指令。

  2. 尽量不要存大对象。

    • 一般而言,value超过10k的就算大对象了,但是要根据实际业务来考虑。
    • 可以把大对象拆分成多个子对象
  3. 查找大对象的指令

./redis-cli -p 6380 --bigkeys

Redis的阻塞分析

  1. 首先,业务记录好相关日志,通过降级、报警等系统能够知道redis是否发生了阻塞。
  2. 发生阻塞的原因主要有几点:
    • 外部原因:网络阻塞、CPU竞争等
    • 内部原因:(1)指令执行时间长,如keys *;(2)数据结构不合理,保存一些大key;(3)fork子进程阻塞,比如说aof刷盘阻塞。

参考资料

  • 《咕泡云课堂》
  • redis官网

https://redis.io/docs/stack/bloom/

  • 布隆过滤器

https://mp.weixin.qq.com/s/t2GFWaFZtz6TtcTiCbe4lg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值