Redis总结
概述
-
什么是Redis,redis为什么那么快?
- redis是高性能非关系型的键值对数据库,与传统的数据库不同的是Redis的数据是存在内存中的,所以redis被广泛应用于缓存方向,
- redis储存键和值之间的映射,键的类型只能为字符串,值支持五种数据类型:字符串,列表,集合,散列表,有序集合。
- 完全基于内存,使用key-value的hastable模式,hashtable的查找和操作的时间复杂度都是O(1)。采用单线程的,避免各种进程和线程切换所导致的上下文切换和竞争条件,也不存在多线程或者多进程导致的切换而消耗CPU,不用考虑各种锁的问题,不存在加锁释放锁操作,也不会因为死锁问题而导致性能下降。多路I/O复用模型,非阻塞I/O。没有使用一般的系统调用,会浪费一定的时间去移动和请求。
-
Redis的优缺点?
- 优点:读写性能优秀,支持数据持久化方式,支持事务,数据结构丰富,支持主从复制。
- 缺点:数据库的内存受到内存的限制,不能作为海量数据的高性能读写。不具备自动容错和恢复功能,较难支持在线扩容。
-
为什么要用缓存?
- 高性能:第一次访问数据较慢,需要从磁盘中读取数据,将该数据放入内存的缓存中,下次访问是直接访问内存所以速度会相当快。
- 高并发:直接操作缓存能够承受的请求是远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存而不是经过数据库。
-
常用数据结构和场景
数据类型 值 应用场景 String 字符串,整数和浮点数 字符串或一部分进行操作以及整数的自增和自减 List 列表 从两端压入或弹出元素 Set 无序集合 检查一个元素是存在与集合中,计算交集,并集和差集 HASH 包含键值对的无序散列表 获取所有键值对,检查某个键是否存在 ZSET 有序集合 根据分值范围或者成员来获取元素 计算一个键的排名 -
常用数据结构和操作
- String
> set hello world > get hello > del hello
- List
> rpush lsit-key item > rpush list-key item2 > lrange list-key 0 -1 #展示list的所以数据 > lpop list-key > lrange list-key 0 -1
- SET
> sadd set-key item > sadd set-key item2 > smembers set-key > srem set-key item2 #pop set顶的数据
- HASH
> hset hash-key sub-key1 value1 > hset hash-key sub-key2 value2 > hgetall hash-key > hdel hash-key
- ZSET
> zadd zset-key 728 member1 > zadd zset-key 982 member2 #根据分数的排序 > zrangebyscore zset-key 0 800 withscores > zrem zset-key member1 > zrange zset-key 0 -1 withscores
-
应用场景
- 计数器 String自增自减
- 缓存 将热点数据放到内存作为缓存 设置缓存的最大容量 以及淘汰策略去保证缓存的命中率
- 会话缓存 让用户的会话信息具有状态
- 查找表 例如DNS记录就适合使用Redis进行存储,查找表和缓存类似
- 消息队列(发布/订阅功能) list可以通过lpush和rpop写入和读取消息,不过最好使用Kafka,Rabbit MQ
- 分布式锁 在分布式场景下,无法使用单机环境下的锁对多个结点进行同步,可以使用Redis自带的SETNX命令实现分布式锁,除此之外
- SET可以实现交集,并集从而实现共同好友功能,Zset可以实现有序性操作,从而实现排行榜功能
-
底层的数据结构
- dict是一个散列表结构,使用拉链法解决哈希冲突
- 有序集合采用跳跃表来实现,基于多指针有序链表实现的,可以看成多个有序链表,在查找时,从上层指针查找,找到对应的区间之后再到下一层去查找。
- 跳跃表与红黑树等平衡树相比,插入速度快,因此不需要进行旋转等操作来维护平衡,更容易实现。
持久化
- Redis持久化?
- 将内存中的数据写到磁盘中去,防止服务宕机了内存数据丢失
- 持久化机制和各自的优缺点?
- RDB(默认)Redis DataBase ,一定时间将redis的数据以快照的形式保存到磁盘中,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。缺点:有一段间隔时间内才会进行持久化,如果在这一期间发生故障,会丢失一部分数据。
- AOF,是指所有的命令行记录以Redis命令请 求协议的格式完全持久化储存,保存为aof文件
- Redis持久化数据和缓存怎么做扩容?
- 一致性hash对2^32取模。将hash函数的值空间看作是一个虚拟的圆环,也叫hash环。需要通过数据key找到对应的服务器然后存储,通过数据key的哈希值落在哈希环上的节点,如果命中了机器结点就储存在此机器结点上,如果在环上,就顺时针直到碰到第一个机器。
- 这样在添加或者删除新机器时,数据会自动的流入下一个结点。
- 一致性哈希的数据倾斜问题,某些结点数据多,其他结点没有数据。使用虚拟结点来解决数据倾斜问题,每个机器节点会进行多次哈希,最终每个机器节点上会出现多个虚拟节点存在。使用这种方法来大大削弱甚至避免了数据倾斜问题。Dubbo的负载均衡使用一致性hash策略。
过期键的删除策略
- 过期策略有哪些?
- 定时过期,到了定时器记录的时间后,过期就会删除数据,该策略可以立即清除过期的数据。但是占用cpu资源处理过期资源。
- 惰性过期,当访问才去判断是非过期,过期则清除,对cpu友好,对内存不友好。
- 定期过期,是上面二者的结合,过一段时间就扫描看是否过期。
内存淘汰策略
- 内存淘汰策略
- 全局的键空间选择性移除
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错
- allkeys-lru: 当内存不足以容纳新写入数据时,移除最近最少使用的key
- Allkeys-random:当内存不足以容纳新写入的数据时,在键空间中,随机移除某个key
- 设置过期时间的键空间选择性移除
- valatile-lru,volatile-random, volatile-ttl,都是设置过期时间的键空间选择性移除。
- 全局的键空间选择性移除
缓存异常
-
缓存雪崩?
- 指在同一段时间内,缓存大面积的失效,导致后面的请求都会落到数据库上,数据库短时间内接受大量的请求而崩掉。
- 解决方案,缓存数据的过期时间设置随机,防止同一时间大量的数据过期现象发生,给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效。如果缓存标记失效,则更新数据缓存。
-
缓存穿透?
- 缓存和数据库都没有的请求,导致所有请求都落到数据库上,造成数据库短时间内承受大力请求而崩掉。
- 使用redis设置一个key-null对这类数据进行过滤,或者采用布隆过滤器,将所有可能存在的数据哈希到足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截
-
缓存击穿?
- 缓存中没有而数据库中有的数据(一般是缓存时间到期导致的),并发用户多引起数据库压力瞬间增大,造成过大压力,与缓存雪崩不同,缓存击穿是指多用户并发对一个数据进行访问,而缓存雪崩是对不同数据的访问
- 设置热点数据永不过期,加互斥锁。
-
缓存预热?
-
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,可以避免在用户请求的时候,先查询数据库,然后再数据缓存的问题,
-
只有热点数据才有缓存的价值。
-
Redis实战
- 延时队列,时间戳作为key,调用zadd来生成消息,消费者使用zrangbyscore获取n秒之前的数据做轮询处理。