基础数据结构
字符串
常数复杂度获取字符串长度,减少内存重分配
常用在缓存、计数、共享Session哈希
压缩列表和字典
存放用户信息,比如购物车集合
整形集合(可能会导致升级,重新分配长度)和字典,不允许又重复元素
利用 Set 的交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。有序集合
压缩列表、跳表和字典(跳表实现 lgn 的范围查找,字典实现 O(1)的单个查找)
通过score排序。常用来实现延时任务和排行榜列表
压缩列表和双端链表
可以实现简单的消息队列
高级数据结构
- 「BloomFilter」
- 判断一个数是否在 BloomFilter 里面,判断为在则不一定在,不存在的话就肯定不存在
- BloomFilter 需要提供一个初始化大小,和误判率,然后根据这两个参数决定空间占用
- bloomFilter 其实就是一个大型的位数组,当添加一个数进来时,会利用多个 hash 函数对 key 进行 hash 运算在对位数组进行取余,每个 hash 函数都会的到一个位置,在把位数组的这些位置置为 1
- 判断是否存在也是进行多个 hash 运算,再把得到的结果拿去位数组里面比较,如果都是 1 的话则有可能存在,否则不存在
- 「HyperLogLog」
统计 uv - 「PubSub」
- 「geo」
位置运算,将立体坐标切换,进制运算(就像分蛋糕一样),越长越精确 - 「redis为什么快」
io 多路复用模型(一个 select 多个)和非阻塞式 io(有数据就返回)
纯内存操作
合理的数据结构 单线程操作,避免了上下午切换
缓存雪崩、击穿、穿透
- 「缓存穿透」
恶意用户通过不存在的 key 去查缓存,当缓存不存在,就落入数据库,会导致每条请求都进入数据库
「解决」:一方面可以在程序端进行拦截,一方面可以使用 BloomFilter 判断 key 是否存在,如果不存在就返回,存在则访问缓存和数据库 - 「缓存雪崩」
大量的 key 失效,一方面会导致 redis 花大量的时间去清理 key 其次是大量的数据库请求会打进来,导致挂掉 「解决」:给 key 设置过期时间的时候加一个随机时间并使用集群模式,还有限流 - 「缓存击穿」
大量的请求查询同一个 key,恰好 key 失效了,这时候大量的会去请求数据库
「解决」:加锁或者分布式锁,当第一个线程拿到了锁,并从数据库拿到了数据就重新将数据缓存到 redis 中,其它的线程直接从缓存里面拿
持久化
「RDB」
rdb 保存服务器的所有键值对数据,可以用 save 和 bgsave 命令执行
sava 命令会导致服务器阻塞
bgsave 是产生一个子进程去处理,处理完成通知主进程,不会产生阻塞
服务器启动的时候就会去加载 RDB 文件,如果启用了 AOF,redis 加载会优先加载 AOF 文件 服务器通过用户配置的 save 条件。自动执行 bgsave「AOF」
AOF 通过存储服务器的写命令来保存服务器的状态
当 AOF 打开时,客户端的写命令会被追加到 AOF 的缓冲区末尾
缓冲区何时同步到 AOF 文件中,是通过配置 appendfsnyc 选项来决定
选项如下:默认是 everysec
always:客户端写一条命令就同步到 AOF 文件中(最多丢失 1 条命令,安全性最高,效率最慢)
everysec: 每秒将缓冲区的数据同步到 AOF 文件(可能 1 秒前的数据)
no:何时同步由操作系统决定(会导致内存的数据丢失)AOF 加载如下图:
AOF 重写
AOF重写会产生一个新的AOF文件,新的文件比原来的AOF文件小
会将新的命令写入AOF缓存中,当新的AOF产生创建完成,就会吧缓冲区的内容追加到AOF文件中,保持新旧的两个AOF文件一致,在用原子操作将新的AOF文件替换过去。
过期策略
- 通过 expire 指定过期时间,ttl 查看过期时间。指定过期了的 key 会单独存在过期字典中
- 可以通过 ttl 去查看还剩多久过期
- 定时删除(因为 redis 是单线程的,在系统有多余的内存且在处理大量的任务时候,定时删除会占用一定的 cpu,但是它对内存最友好)
- 惰性删除(对内存不友好,可能永久不会删除即内存泄漏)
- 定期删除采用贪心算法,每次随机从过期数据库抽取 20key,如果过期的 key 超过 4 分之一则循环,直到不满足条件或者时间大于指定时间(25ml)
- redis 采用惰性删除和定期删除结合的方式
哨兵(Sentinel)
「哨兵会通过项主服务器发送info命令获取所有从服务气地址,以及连向这些从服务器」
「当哨兵检测到主服务器有故障,会广播给这台服务器的所有哨兵,查看它们是否同意主服务器下线,当满足哨兵配置的下线个数时,该哨兵认为服务器下线,开始选出领头哨兵(超过半数投票)」
「领头哨兵进行故障转移切换选出从库当主库」
「当主节点发生故障时,客户端会重新向 sentinel 要地 址,sentinel 会将最新的主节点地址告诉客户端」
主从
内存淘汰
当内存不够用时,内存淘汰机制就会上场。淘汰策略分为:
- 当内存不足以容纳新写入数据时,不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。(Redis 默认策略)
- 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(LRU推荐使用)
- 当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。
- 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,最少使用的 key 优先被淘汰。。这种情况一般是持久化存储的时候才用。
- 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。
- 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除
分布式锁
分布式锁+过期时间来实现(防止程序出错导致永远不过期
超时问题:随机value,在释放锁用lua指令去匹配随机value是否一致
Redis与Mysql双写一致性方案
先更新数据库,再删缓存
(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存 ok,如果发生上述情况,确实是会发生脏数据.
但是由于数据库的读操作(第5步)的速度远快于写操作(第三步)的,所以脏数据很难出现。可以对异步延时删除策略,保证读请求完成以后,再进行删除操作。
应用场景
- 秒杀系统
- 限流
- 统计 pv、uv
本文使用 mdnice 排版