1、什么是Redis
Redis是一个高性能的key/value的分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库。
内存存储和持久化、发布与订阅、分布式锁等
string
string是二进制安全的,它可以包含任何数据,最大能存储512MB。
使用场景:常规key-value缓存应用;常规计数:微博数,粉丝数
hash
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
使用场景:存储部分变更数据,如用户信息等。
list
list列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
使用场景:可作为栈,队列,如获取最新得N个数据操作,消息队列系统。
set
set是string类型的无序集合,通过HashTable实现。
使用场景:交集、并集、差集;获取某段时间所有数据去重值
zset
zset和set一样是string类型元素的集合,且不重复
使用场景:sorted set是自动排序的,可以通过用户额外提供一个优先级score的参数为成员排序,并且插入有序。如排行榜,带权重的消息队列。
定时过期:每个设置过期时间的key,都创建一个定时器,到过期时间就会立即清除
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则删除
定期过期:每隔一段时间执行删除过期key操作
redis中同时使用了惰性过期和定期过期两种策略
当内存达到maxmemory极限时,需要使用LRU淘汰算法来决定清理掉哪些数据 LRU算法:也就是默认删除最近最少使用的键
redis提供6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
4.0版本后增加以下两种:
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key
redis提供了两种方式的数据持久化,一种是RDB,一种是AOF
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,具体过程是: Redis使用fork函数复制一份当前进程的副本 父进程继续接受处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件 当子进程写完写完所有数据后,会用该临时文件替换旧的RDB文件
RDB优点:
适合大规模的数据恢复
如果业务对数据完整性和一致性要求不是太高的话,RDB是一个不错的选择
RDB缺点:
如果系统在定时持久化之前出现宕机现象,此前还没有来得及写入磁盘的数据将会丢失
由于RDB是通过fork子进程来完成持久化工作的,如果数据集较大时,可能会导致整个服务器停止几百毫秒甚至一秒钟
AOF的出现主要是弥补RDB的不足,即数据不一致性,采用日志的形式记录每个些操作,并追加到文件中。 AOF优点:
数据的完整性和一致性较高 当日志过大时,可以自动启用Rewrite机制
AOF缺点:
AOF文件记录内容多,文件会越来越大,数据恢复也会越来越慢 根据同步策略的不同,AOF在运行效率上要慢于RDB
主从复制、哨兵模式、集群
主从复制
原理:当一个数据库启动后,会向主数据库发送SYNC命令。同时主数据库接收到SYNC命令后会开始在后台保存快照,并将保存快照期间接收的命令缓存起来。当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库接收到后,会载入快照文件并执行收到的缓存的命令。复制是初始化结束后,主数据库每接收到写命令时就会将命令同步给从数据库,从而保证主从数据库数据一致。
哨兵模式
监控主从服务器是否正常运行,主服务器故障,自动将从服务器转换为主服务器
集群模式
可以实现redis分布式存储,就是每台redis节点存储不同的内容
缓存穿透
缓存穿透是指缓存没有发挥作用,业务系统虽然去缓存中查询数据,但是缓存中没有数据,业务系统需要再次去存储系统中查询数据。
解决方案
当数据库找不到的时候,也将空对象写进缓存,并设置较短的过期时间
对于不合法的请求,可以使用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问到的key,不存在则过滤
布谷鸟过滤器
缓存击穿
在缓存数据过期那一刻,但是DB中存在的时候,大量请求回击穿到DB,导致DB压力过大
解决方案
可以设置热点数据永不过期
可以加一个互斥锁
缓存雪崩
设置缓存时,采用了相同的过期时间,导致某时刻缓存同时失效,请求全部转向DB,DB瞬时压力过大雪崩
Redis宕机,导致客户端的请求流向DB,拖垮DB
解决方案
对于缓存同时失效的情况
可以在原有的过期时间上加上一个随机值
对缓存更新操作进行加锁保护,保证只有一个线程能够进行缓存更新,未获取到锁的线程要么等待锁释放后重新读取缓存,要么就返回空值和默认值
后台更新
(1)定时读取——后台线程定时更新缓存
(2)消息队列通知——业务线程发现缓存失效后,通过消息队列发送一条消息通知后台线程更新缓存
双key策略
key有过期时间,key1没有过期时间,每次缓存读取不到key时,就返回key1的内容,然后触发一个事件。这个事件会同时更新key和key1。
对于“redis挂掉”这种情况
事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利用 redis 持久化机制保存的数据尽快恢复缓存
因为redis采用的是多路I/O复用模型,可以让单个线程处理多个链接请求,而且redis是基于内存操作,操作数据的速度非常快
对于不同场景设置缓存过期或不过期
维度化缓存与增量缓存:如一个商品,包含了多个维度属性,对商品属性进行维度化并增量更新
大Value缓存: 可以对大value进行拆分;或者拆分成多个小value
热点缓存: 对于访问非常频繁的热点缓存,如果每次都去远程缓存系统中获取,可能会因为访问量过大,导致缓存系统请求过多,负载过高等问题。解决方案是可以挂更多的从缓存,客户端通过负载均衡机制读取从缓存系统数据。
单实例中
基于Redis命令:
set key value NX PX max-lock-time
value要具有唯一性
释放锁时,lua脚本中一定要比较value,防止误解锁是
集群多节点
用官网推荐的Redission实现的分布式锁
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
Redis多线程并发竞争key,也就是多个系统同时对一个key进行操作,但是最后执行的顺序和我们期望的顺序不同,也就导致了结果的不同。
如何解决:
使用分布式锁:redis分布式锁/zookeeper分布式锁
将读和写串行到一个内存队列中,这样就不会出现不一致了,但是吞吐量就会下降(最好不要做这个方案)
先删除缓存,再更新数据库库