String、Hash、List、Set、Zset、BitMap、HyperLog、Geo、Stream
常见的五种数据结构及其实现
- String 底层的数据结构主要实现是SDS(简单动态字符串),可以保存文本数据、二进制数据,获取字符串长度时间复杂度是O(1),并且会自动扩容
- List 3.2版本后,List数据类型底层数据结构只有quicklist实现。替代了双向链表。 quick实际上i是ziplist和linktlist的混合体,他将linkedlist按段切分,每一段使用ziplist来紧凑存储,多个ziplist之间使用双向指针串接起来
- Hash
Hash类型的底层结构是由压缩列表或哈希实现
- 如果哈希类型元素个数小于512个,所有值小于64字节,redis会使用压缩列表作为hash的底层数据结构
- 如果不满足上面的条件,会使用hash表作为hash类型的底层数据结构 在Redis7.0中,压缩列表已经被listpack数据结构实现 listpack与ziplist内存布局、实现原理与listpackn非常相似,都是连续的内存,前后有有标识元素,ziplist的entry中还存了上一个entry的长度,而listpack存的是自已entry data的长度
- Set 如果集合中的元素都是整数并且小于512,Redis会使用整数集合作为Set类型的底层结构; 否则Redis会使用哈希表作为Set类型的底层数据结构
- Zset
Zset的底层结构是有压缩列表或者跳表实现
- 有序元素集合少于128,并且每个元素的值小于64字节时,Redis会使用Ziplist作为底层数据结构(Redis7.0替换为listpack)
- 否则会使用跳表作为底层数据结构
Redis线程模型
Redis单线程是指【接收客户端请求→解析请求→i进行数据读写等操作→发送数据给客户端】这个h过程是由i一个线程来完成的。redis程序还有其他 的线程用于i其他任务,例如AOF刷盘 、关闭内存、释放内存。 Redis6.0前的单线程模型的工作原理如下1.首先调用epoll_create创建一个epoll对象并调用socket创建一个服务端socket 2.然后调用bindi绑定端口和调用list监听socket 3.接下来调用epoll_ctl将listen socket加入到epoll,同时注册连接事件处理函数 初始化完成后,主线程进入到了事件循环函数,主要会做以下的事情1.如果发送队列有任务,则通过iwrte函数将数据数据发送出去2.接下来调用epoll_wait等待事件到来 - 如果是连接事件,则会调用连接事件处理函数,该函数会调用acept获取已连接的socket,调用epoll_ctl将已连接的socket加入到epoll并注册读处理函数 - 如果是读事件,则会调用读事件处理函数,该函数会调用readu获取客户端发送的数据,并i解析命令后执行,然后将客户端对象添加到发送队列,并将执行结果写到发送缓存区,等待发送。- 如果是写事件,则会调用写事件处理函数,该函数通过write函数将u客户端发送缓存区里的数据发送出去后,如果这一轮没有发送完,就会继续注册写事件处理函数,等待epoll_wait发送可写后再进行处理
Redis为什么快
- 大部分操作都在内存中完成,i并且采用高效的内存结构,因此Redis的ii瓶颈有可能是机器的内存和网络宽带
- Redis采用单线程模型避免了多线程之间的竞争。
- Redis采用多路复用机制处理大量的客户端Socket请求
Redis6.0后引入多线程
Redis6.0支持的IO多线程,默认情况下只针对发送响应数据,并不会对于处理读请求。对于命令的执行还是使用单线程来处理。
Redis持久化
Redis有三种数据持久化的方式
- AOF日志 AOF有三种写回硬盘的策略 1.Always 每次执行完命令后,将AOF日志写入硬盘 2.EverycSec,每次写完命令后,先将命令写入到AOF文件缓冲区,然后每秒将缓存区内的内容写到硬盘 3.No Redis不控制写回硬盘的时机,转交给操作系统控制写回,每次i执行命令后,先将命令写入AOF的内核缓冲区,再由操作系统决定何时将缓存区的内容写入硬盘 AOF文件太大时,Redis会执行AOF重写来减少aof文件大小,通过后台子进程bgrewriteaof同过共享内存完成,在重写的过程中使用COW来保证数据被修改
- RDB快照 通过COW方式,在创建快照的时redis依然可以处理命令
- 混合持久化,集合了AOF和RDB的优点 集合了aof和rdb的优点,通过rdb和aof相结合的方式持久化,这种方式在恢复时可以快速的恢复。
Redis集群
1.哨兵模式 哨兵模式可以监控主从服务状态,并且提供主从节点故障转移功能2.切片集群模式 集群模式将数据分布在不同的服务器中,以次来降低对单节点的依赖,提高Redis的读写性能
Redis Cluster方案采用哈希槽(Hash Slot),来处理数据和节点之间的映射关系。在Redis Cluster方案中,一个集群共有16384个哈希槽,每个键值对会根据key映射到 一个Hash Slot里。
- 根据键值对的key,按照CRC16算法计算一个16bit的值
- 再用16bit对16384取模,得到0-16383范围内的模数,然后再映射到对应的节点
Redis过期删除与内存淘汰
- 惰性删除 不主动删除,每次访问时,都检测key是否过期,如果过期则删除该key
- 定期删除 每隔一段时间内,从数据库取出一定数量 的key进行检查,并删除其中过期的key 优点:
Redis内存淘汰机制
- 不进行数据淘汰的策略 noeviction 超过最大内存会报错
- volatile-random
- volatile-ttl
- volatile-lru
- volatile-lfu
- allkeys-random
- allkeys-lru
- allkeys-lfu LRU(least Recently Used)最少使用 LFU(least Frequently Used)最近不常用
Redis缓存设计
- Redis缓存雪崩
大量缓存数据在同一时间过期,这时有大量数据请求会访问数据库,从而导致数据库压力骤增,严重会造成数据库宕机
- 缓存失效时间随机
- 设置缓存不过期
- 缓存击穿
缓存中的热点数据i过期,导致大量请求访问数据库
- 热点数据不设置过期时间
- 缓存击穿
访问的数据不在缓存,也不在数据库
- 拦截非法请求
- 设置空值或者默认值
- 使用布隆过滤器快速判断数据是否存在,
Redis实现锁
SET lock_key unique_value NX PX 10000
NXo表示不存在才创建 PX表示key过期时间 而解锁时需要确认解锁的客户端是加锁的客户端,首先需要两部,但是redis不提供完整的原子操作,因此使用lua进行解锁操作
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
fi
单节点锁的优点 1 性能高校2.实现简单 单点故障问题
为了保证集群环境下分布式锁的可靠性,Redis设计出了RedLock来提供在分布式下锁的可靠性 Redlock算法的基本思想是