Redis是一个使用C语言开发的高速缓存数据库。redis是Nosql数据库中使用较为广泛的非关系型内存数据库,redis内部是一个key-value存储系统。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型,类似于Java中的map)。Redis基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一。
使用场景:记录帖子点赞数,点击数,评论数;
缓存近期热帖;
缓存文章详情信息;
记录用户会话信息。
功能:数据缓存功能;分布式锁的功能;支持数据持久化;支持事务;支持消息队列。
redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。
RDB就是在不同的时间点,将 redis 存储的数据生成快照,然后对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。
AOF就是将 redis 执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
与快照持久化相比,AOF 持久化 的实时性更好,如果没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。Redis 4.0 开始支持 RDB 和 AOF 的混合持久化,如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:(1)最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
(2)缓存无效 key:从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
(3)布隆过滤器:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。(但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。)
缓存雪崩:当缓存服务器重启或者缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求,引起数据库压力过大甚至宕机。
解决方案:(1)分散key的失效时间,在批量往 Redis 存数据的时候,把每个 Key 的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效。
(2)使用 redis 集群部署,即使有 redis 挂了,依然有其他的 redis 可以过来提供服务,当然最好就是使用redis cluster集群,多个redis master,不仅有主备服务可以在故障时进行切换,即使真的主备都挂了,那也只是一部分数据不能提供服务了,还有其他的master可以给其他提供服务,尽量的减少redis宕机所带来的影响。
(3)设置热点数据永远不过期,有更新操作就更新缓存
缓存击穿:指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
解决方案:(1)设置热点数据永远不过期
(2)加互斥锁
String,hash,list,set,zset
(1)volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
(2)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
(3)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
(4)allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
(5)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
(6)no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。
(7)volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
(8)allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
(1)定时删除策略
创建一个定时器,当 key 设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。
优点: 节约内存,到时间就删除,快速释放掉不必要的内存占用。
缺点: CPU压力很大,无论CPU此时负载量多高均占用CPU,会影响 redis 服务器响应时间和指令吞吐量。
总结: 用处理器性能换取存储空间(拿时间换空间)
(2)惰性删除策略
数据到达过期时间,不做处理。等下次访问该数据时,如果未过期,返回数据;发现已过期,删除,返回不存在。
优点: 节约CPU性能,发现必须删除的时候才删除。
缺点: 内存压力很大,出现长期占用内存的数据。
总结: 用存储空间换取处理器性能(拿空间换时间)
(3)定期删除策略
定期删除策略是定时删除策略和惰性删除策略的一种整合折中方案。
定期删除策略是每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对CPU的影响;另一方面定时删除有效的减少了因惰性删除带来的内存浪费。
实现过程: 过期键的定期删除策略由 activeExpireCycle 函数实现,每当Redis服务器的周期性操作 serverCron 函数执行时,activeExpireCycle 函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。
总结: 周期性抽查存储空间(随机抽查,重点抽查)
Redis 使用的策略
redis 使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。