redis
Redis
Redis 相关点
Redis数据结构
- String(字符串)
- List(列表)
- Hash(键-值)
- Set(无序集合)
- ZSet(有序集合)
String-字符串
1 | 设置 key-value 类型的值 |
List-列表
1 | 将一个或多个值value插入到key列表的表头(最左边),最后的值在最前面 |
Hash-键值对
1 | 存储一个哈希表key的键值 |
Set-无序键值集合
1 | 往集合key中存入元素,元素存在则忽略,若key不存在则新建 |
Zset-有序集合
1 | 往有序集合key中加入带分值元素 |
Redis持久化
Redis持久化方式
- AOF日志(快照 + 追加命令)
- RDB快照(二进制文件)
AOF日志 (Appen Only File)
AOF 只会记录 写操作 ,读操作不会被记录,因为没意义
三种写回策略
如果每次修改都写入磁盘,会造成极大的资源浪费,所以redis 创建了 AOF 缓冲区,并提供了三种写回策略:
Always,每次执行完写操作,同步 AOF 日志数据回硬盘Everysec,每次执行完写操作,先将命令写到 AOF 内核缓冲区,每秒将缓冲区里内容写回硬盘No,将写回时机交给操作系统决定
这三种策略知识在控制 fsync() 函数调用时机
AOF 重写机制
为了避免 AOF 文件越写越大,提供了 AOF 重写机制
AOF 重写机制是在重写时,读取当前数据库中所有键值对记录到[新的 AOF 文件] ,然后将新的 AOF 文件替换旧的 AOF 文件,从而实现 AOF 文件的压缩。
为何用新文件替换旧文件? 这样确保如果重写失败了,也不会对现有 AOF 文件造成污染。
AOF 重写过程由后台子进程 bgrewriteaof 来完成
主进程会复制一份 [页表](记录虚拟地址与物理地址映射关系) 给子进程 ,这样做 节约物理内存资源
写时复制
当父进程或子进程对内存发起写操作时, CPU 会触发 写保护中断 ,操作系统会进行 物理内存复制,并重新设置映射关系,将父进程的内存读写权限设置为 可读写,最后才对内存进行写操作,这个过程叫 写时复制(Copy On Write)
主进程修改已经存在的 key-value ,就会发生 写时复制 , 只会复制主进程修改的物理内存数据,没修改的内存还是与子进程共享
为了解决 写时复制主线程修改的值与 AOF 文件中数据不一致问题, redis 设置了 AOF 重写缓冲区
当重写 AOF 期间,当 redis 执行完一个写命令,会 同时将这个命令写到 [AOF 缓冲区] 和 [AOF 重写缓冲区]
当子进程完成 AOF 重写工作后,会向主进程发送一条信号,主进程收到信号会做以下事情
- 将 AOF 重写缓冲区的数据写入 AOF 文件
- 覆盖原有 AOF 文件
RDB
AOF 记录的写操作命令, RDB记录的是某一瞬间的数据快照
因此在进行数据恢复时, RDB 优于 AOF
RDB 生成时机
RDB 提供 save 和 bgsave,区别在于是否在[主线程执行]
RDB通过配置文件来实现每隔一段时间自动执行一次 bgsave 命令
1 | save 900 1 |
选项名叫save ,实际执行的是 bgsave
- 900 秒内,对数据库进行了至少 1 次修改
- 300 秒内,对数据库至少进行了 10 次修改
注: RDB 快照是全量快照
RDB 的写时复制
当在进行 bgsave 时,主进程依旧能 继续处理操作命令
如果这时主线程要 修改共享数据里的某一块数据 (如 A),就会发生写时复制,这块数据的 物理内存会被复制一份(A·) ,然后 主线程操作数据副本(A·) ,此时 bgsave 子进程可以继续把原来的数据(A) 写入到 RDB 文件中
注意,发生写时复制时, RDB 快照保存的是原本的内存数据 ,而修改的数据是没办法写入本次 RDB 文件的, 主线程刚修改的数据交由下次处理
aof-use-rdb-preamble yes
混合使用 AOF 和 RDB ,进行 混合持久化
前半部分是 RDB 的全量数据,后半部本是 AOF 格式的增量数据
这样 加载的时候速度会很快
并且 加载完 RDB 内容后,才会加载后半部分的 AOF 内容, 这里的内容是 redis 后台子进程重写 AOF 期间,主线程处理的操作命令,使 数据丢失更少
过期删除策略和内存淘汰策略
过期删除策略
redis 可以对 key 设置过期时间,相对应的机制将过期的键值对删除,这个机制就是过期键值删除策略
设置过期时间
一般使用 expire <key> <n> , 表示 key 在 n 秒后过期
也可以在创建时对 key 设置过期时间 set <key> <value> ex <n> 或 setex <key> <n> <value> ,表示设置键值对时,同时设置过期时间(单位 秒 )
如果想查看某个 key 剩余存活时间,可以使用 TTL <key> 命令
1 | 设置键值对,同时指定过期时间 60 秒 |
如果反悔,取消 key 的过期时间,可以用 persist <key> 命令
1 | 取消 key1 的过期时间 |
Redis 如何判定过期
当我们设置过期时间时, redis 会把该 key 带上过期时间存储到一个 过期字典 里
(实际上时 哈希表 ,时间复杂度 O(1))
过期删除策略
- 定时删除
- 惰性删除
- 定期删除
定期删除: 在设置 key 的过期时间时,同时创建一个定时事件,当时间到达,由该事件处理器自动执行 key 的删除
惰性删除: 不主动删除过期 key , 每次从数据库访问时进行检测,如果过期则删除 key
定时删除: 每隔一段时间 [随机(默认20个)] 从数据库取出一定量的 key 进行检查,并删除其中的 过期 key
redis 过期删除策略
Redis 选择 [惰性删除 + 定期删除] 配合使用
惰性删除: 对访问的 key 判断是否过期,如果过期删除 key ,并且返回 null
定期删除: redis 默认每秒进行 10 次过期检查,通过 redis.conf.hz 进行设置;默认每次抽取 20 个 key,检查并删除过期的 key;如果本轮过期的数量超过 25% ,则重复执行,直至过期的 key 小于 25%,则停止继续删除过期 key , 等待下一轮检查
内存淘汰策略
过期删除策略是删除过期的 key ,而 Redis 内存淘汰策略是 当 Redis 运行内存超过 Redis 设置的最大内存后,用内存淘汰策略删除符合条件的 key , 以此来保障 Redis 高效运行
如何设置 Redis 最大运行内存
在 redis.conf 中,通过参数 maxmemory <bytes> 来设置
- 在 64 位操作系统中,这个默认值为
0,表示没有内存大小限制 - 在 32 位操作系统中,这个默认值为
3GB,因为 32 位的机器最大支持 4GB 内存
Redis 内存淘汰策略
- 不进行数据淘汰策略
- noevication(redis3.0后,
默认的内存淘汰策略):不进行数据淘汰,拒绝写入
- noevication(redis3.0后,
- 进行数据淘汰
- volatile-random:随机淘汰,只淘汰设置了
expire的 key - volatile-ttl:优先淘汰更早过期的 key
- volatile-lru:淘汰所有设置了过期时间中,最久未使用的 key
- volatile-lfu:淘汰所有设置了过期时间中,最少使用的 key
- allkeys-random:随机淘汰
- allkeys-lru:淘汰整个 key 中最久未使用的
- allkeys-lfu:淘汰整个 key 中最少使用的
- volatile-random:随机淘汰,只淘汰设置了
使用 config get maxmemory-policy 查看当前内存淘汰策略
设置内存淘汰策略方式
- 通过命令
config set maxmemory-policy <策略>设置 - 通过 redis.conf 设置
maxmemory-policy <策略>
LRU 和 LFU
LRU:Least Recently Used, 最久未使用 , 维护一个额外字段,记录此数据最后一次访问时间; 无法解决缓存污染
LFU:Least Frequently Used,使用频率最少 ,记录使用次数
布隆过滤器
布隆过滤器(Bloom Filter) 是一种空间效率高的概率数据结构,用于快速判断一个元素是否再一个集合中
布隆过滤器遵循 存在的不一定存在,不存在的一定不存在
基本原理
一个位数组和多个哈希函数来实现 。
流程
- 初始化
- 创建一个大小为m大小的
位数组 - 对要添加的元素使用
k个哈希函数分别计算,得到k个哈希值。
- 创建一个大小为m大小的
- 将这
k个哈希值对应到位数组中的位置,并将这些位置设置为1。 - 查询元素,对要检查的元素分别进行哈希,然后
将哈希值对应位置与位数组进行比较,如果对应位置都为1,则表示该元素可能存在
优点
- 空间效率高
- 查询速度快
- 简单易用
缺点
误判无法删除元素- 需要合理选择参数
高可用
todo