Redis主程序采用单线程原因分析
- 使用单线程模型能带来更好的可维护性,方便开发和调试,引入多线程,就要引入并发控制,处理线程竞争问题,增加程序运行的不确定性和线程切换带来的开销
- 使用单线程模型也能并发的处理客户端的请求:io多路复用
- Redis 服务中运行的绝大多数操作的性能瓶颈都不是 CPU,都是基于内存操作,大部分时间耗费在网络IO
Redis是什么
- Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。
- 它是一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库
- 性能优秀,数据在内存中,读写速度非常快,支持并发 10W QPS。单进程单线程,是线程安全的,采用 IO 多路复用机制。丰富的数据类型,支持字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。支持数据持久化。可以将内存中数据保存在磁盘中,重启时加载。主从复制,哨兵,高可用。可以用作分布式锁。可以作为消息中间件使用,支持发布订阅
Redis基本数据类型
- 字符串 string
- 列表 list
- 哈希 hash
- 集合 set
- 有序集合 hset
Redis 数据类型编码方式
- raw
- int
- ht
- zipmap
- likedlist
- ziplist
- intset
Redis String
- String 是 Redis 最基本的类型,一个 Key 对应一个 Value。Value 不仅是 String,也可以是数字
- String 类型是二进制安全的,意思是 Redis 的 String 类型可以包含任何数据,比如 jpg 图片或者序列化的对象。String 类型的值最大能存储 512M
Redis Hash
- Hash是一个键值(key-value)的集合。Redis 的 Hash 是一个 String 的 Key 和 Value 的映射表,Hash 特别适合存储对象
Redis List
- List 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)
- List就是链表
- Redis List 的是实现是一个双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了额外的内存开销
Redis Set
- Set 是 String 类型的无序集合。集合是通过 hash表 实现的。Set 中的元素是没有顺序的,而且是没有重复的
Redis Zset
- Zset 和 Set 一样是 String 类型元素的集合,且不允许重复的元素
- Sorted Set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序
Redis缓存
- 缓存穿透:查询不存在的数据,缓存和数据库都查不到,就会一直穿过缓存查询数据库
- 缓存空值,设置过期时间
- 布隆过滤器,判断key是否存在
- 缓存击穿:高并发场景,大量请求查询一个key,当这个key过期时,大量查询落到数据库中,导致数据库压力过大
- 查询数据库加分布式锁,有一个请求落到数据库上,让后其余请求就可以读缓存了
- 缓存雪崩:大量key失效,导致大量查询落到数据库上
Redis 是单线程的,为什么还能这么快
- Redis 完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度是 O(1)。
- 数据结构简单,对数据操作也简单。
- 采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的 CPU 切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。
- 使用多路复用 IO 模型,非阻塞 IO
Redis 过期策略
- 定期删除
- Redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
- Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。
- 1.从过期字典中随机 20 个 key;
- 2.删除这 20 个 key 中已经过期的 key;
- 3.如果过期的 key 比率超过 1/4,那就重复步骤 1
- 惰性删除:当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西
Redis内存淘汰策略
- noeviction: 当内存使用超过配置的时候会返回错误,不会驱逐任何键
- allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
- volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
- allkeys-random:加入键的时候如果过限,从所有key随机删除
- volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
- volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
- allkeys-lfu:从所有键中驱逐使用频率最少的键
挖坑 Lru 和 Lfu
Redis持久化
- AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里,命令的集合
- RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存,保存策略;Redis 需要做持久化时,Redis 会 fork 一个子进程,子进程将数据写到磁盘上一个临时 RDB 文件中
Redis 集群模式
- 主从复制
- 哨兵模式
- Cluster 模式
Redis 主从复制
- 架构图
- 初次同步
- 当从服务器收到 SLAVEOF 命令后,会向其主服务器执行同步操作,进入主从复制流程。
- 从服务器向主服务器发起SYNC 或 PSYNC 命令
- 主服务器执行 BGSAVE命令,生成 RDB 文件,并使用缓存区记录从现在开始的所有写命令
- RDB 文件生成完成后,主服务器会将其发送给从服务器
- 从服务器载入 RDB 文件,将自己的数据库状态同步更新为主服务器执行 BGSAVE命令时的状态。
- 主服务器将缓冲区的所有写命令发送给从服务器,从服务将执行这些写命令,数据库状态同步为主服务器最新状态
- SYNC 与 PSYNC 的区别
- 当主从同步完成后,如果此时从服务器宕机了一段时间,重新上线后势必要重新同步一下主服务器,SYNC与 PSYNC命令的区别就在于断线后重复制阶段处理的方式不同。
- SYNC:从服务器重新向主服务器发起 SYNC命令,主服务器将所有数据再次重新生成 RDB 快照发给从服务器开始同步
- PSYNC:从服务器重新向主服务器发起 PSYNC命令。主服务器根据双方数据的偏差量判断是否是需要完整重同步还是仅将断线期间执行过的写命令发给从服务器
Redis 哨兵模式
- 哨兵模式引入了一个 Sentinel 系统去监视主服务器及其所属的所有从服务器。一旦发现有主服务器宕机后,会自动选举其中的一个从服务器升级为新主服务器以达到故障转义的目的。
- 同样的 Sentinel 系统也需要达到高可用,所以一般也是集群,互相之间也会监控。而 Sentinel 其实本身也是一个以特殊模式允许 Redis 服务器。
- 架构图
Redis cluster 模式
- Redis 哨兵模式实现了高可用,读写分离,但是其主节点仍然只有一个,即写入操作都是在主节点中,这也成为了性能的瓶颈。
- Redis 在 3.0 后加入了 Cluster 模式,它采用去无心节点方式实现,集群将会通过分片方式保存数据库中的键值对
- Redis 的每个节点都可以分为主节点与对应从节点。主节点负责处理槽,从节点负责复制某个主节点,并在主节点下线时,代替下线的主节点
- 架构图