Redis - 缓存机制及使用场景详解(二)

一. Redis都有哪些使用场景

  • Redis是基于内存的nosql数据库,可以通过新建线程的形式进行持久化,不影响Redis单线程的读写操作
  • 通过list取最新的N条数据
  • 模拟类似于token这种需要设置过期时间的场景
  • 发布订阅消息系统
  • 定时器、计数器

常见的数据类型及应用场景详解:https://blog.csdn.net/qq_43030934/article/details/131213135

二. Redis有哪些功能?

1、基于本机内存的缓存

当调用api访问数据库时,假如此过程需要2秒,如果每次请求都要访问数据库,那将对服务器造成巨大的压力,如果将此sql的查询结果存到Redis中,再次请求时,直接从Redis中取得,而不是访问数据库,效率将得到巨大的提升,Redis可以定时去更新数据(比如1分钟)。

2、数据恢复、持久化的功能

如果电脑重启,写入内存的数据是不是就失效了呢,这时Redis还提供了持久化的功能。
redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
优点:

  • 数据更完整,安全性更高,秒级数据丢失(取决于 fsync 策略,如果是 everysec,最多丢失 1 秒的数据);
  • AOF 文件是一个只进行追加的命令文件,且写入操作是以 Redis 协议的格式保存的,内容是可读的,适合误删紧急恢复。

缺点:

  • AOF文件比RDB文件大,且恢复速度慢。
  • 数据集大的时候,比rdb启动效率低。所以这种方式更适合数据要求相对严谨的时候。

RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
缺点:

  • 数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。

3、哨兵(Sentinel)和复制

Sentinel可以管理多个Redis服务器,它提供了监控、提醒以及自动的故障转移功能;

复制则是让Redis服务器可以配备备份的服务器;

Redis也是通过这两个功能保证Redis的高可用;

4、集群(Cluster)

单台服务器资源总是有上限的,CPU和IO资源可以通过主从复制,进行读写分离,把一部分CPU和IO的压力转移到从服务器上,但是内存资源怎么办,主从模式只是数据的备份,并不能扩充内存;

现在我们可以横向扩展,让每台服务器只负责一部分任务,然后将这些服务器构成一个整体,对外界来说,这一组服务器就像是集群一样。

三. Redis和 memecache 区别

1、Redis相比memecache,拥有更多的数据结构和支持更丰富的数据操作。

  • Redis支持key-value,常用的数据类型主要有String、Hash、List、Set、Sorted Set。
  • memecache只支持key-value。

2、内存使用率对比,Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于memecache。

3、性能对比:Redis只使用单核,memecache使用多核。

4、Redis支持磁盘持久化,memecache不支持。

Redis可以将一些很久没用到的value通过swap方法交换到磁盘。

5、Redis支持分布式集群,memecache不支持。

四. 缓存穿透、缓存击穿、缓存雪崩

1、缓存穿透

缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。

解决方案
1.最基本的就是做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。 比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
2.对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。

3.对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该Bitmap过滤。

1、缓存击透

缓存击穿问题也叫热点Key问题,就是缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:
缓存击穿和缓存雪崩的区别在于缓存击穿针对某一key缓存,缓存雪崩是很多key。

  • 互斥锁:当同个业务不同线程访问redis未命中时,先获取一把互斥锁,然后进行数据库操作,此时另外一个线程未命中时,拿不到锁,等待一段时间后重新查询缓存,此时之前的线程已经重新把数据加载到redis之中了,线程二就直接缓存命中。这样就不会使得大量访问进入数据库

优点:没有额外的内存消耗,保证一致性,实现简单
缺点:线程需要等待,性能受影响,可能有死锁风险实时调整,监控哪些数据是热门数据,实时的调整key的过期时长针对热点Key设置缓存永不失效
实时调整,监控哪些数据是热门数据,实时的调整key的过期时长
针对热点Key设置缓存永不失效

3、缓存雪崩

当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。

解决方案

  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其它线程等待;
  • 做二级缓存; 不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀; 针对 Redis 服务不可用的情况:
  • 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
  • 限流,避免同时处理大量的请求。

针对热点缓存失效的情况:

  • 设置不同的失效时间比如随机设置缓存的失效时间。
  • 缓存永不失效。

五. 缓存和数据库数据的一致性

1、淘汰缓存

数据如果为较为复杂的数据时,进行缓存的更新操作就会变得异常复杂,因此一般推荐选择淘汰缓存,而不是更新缓存。

2、选择先淘汰缓存,再更新数据库

假如先更新数据库,再淘汰缓存,如果淘汰缓存失败,那么后面的请求都会得到脏数据,直至缓存过期。

假如先淘汰缓存再更新数据库,如果更新数据库失败,只会产生一次缓存穿透,相比较而言,后者对业务则没有本质上的影响。

3、延时双删策略

如下场景:同时有一个请求A进行更新操作,另一个请求B进行查询操作。

1.请求A进行写操作,删除缓存
2.请求B查询发现缓存不存在
3.请求B去数据库查询得到旧值
4.请求B将旧值写入缓存
5.请求A将新值写入数据库

次数便出现了数据不一致问题。采用延时双删策略得以解决。

redis_conn.delete('img_%s' % uuid)
db.update(data);
time.sleep(100)
redis_conn.delete('img_%s' % uuid)

转化为中文描述就是

1.先淘汰缓存
2.再写数据库(这两步和原来一样)
3.休眠1秒,再次淘汰缓存

这么做,可以将0.1秒内所造成的缓存脏数据,再次删除。这个时间设定可根据俄业务场景进行一个调节。

4、数据库读写分离的场景

两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。

1.请求A进行写操作,删除缓存
2.请求A将数据写入数据库了,
3.请求B查询缓存发现,缓存没有值
4.请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
5.请求B将旧值写入缓存
6.数据库完成主从同步,从库变为新值

依旧采用延时双删策略解决此问题。
此外还有两种解决方案:

5. 先更新数据库,再删除缓存,引入消息队列

1.更新数据库数据;
2.缓存因为种种问题删除失败
3.将需要删除的key发送至消息队列
4.自己消费消息,获得需要删除的key
5.继续重试删除操作,直到成功

6.先更新数据库,再删除缓存,使用mysql binlog日志

1.更新数据库数据
2.数据库会将操作信息写入binlog日志当中
3.订阅程序提取出所需要的数据以及key
4.另起一段非业务代码,获得该信息
5.尝试删除缓存操作,发现删除失败
6.将这些信息发送至消息队列
7.重新从消息队列中获得该数据,重试操作。

以上就是缓存机制及使用场景的介绍,希望对你有所帮助!

你可能感兴趣的:(并发缓存,Redis,redis,缓存,数据库)