redis 知识总结

2024-01-25 10:09:48 浏览数 (1)

简介

  • 开源的内存键值对存储数据库。
  • 支持数据结构:字符串、哈希表、列表、集合、有序集合等。
  • 特点:高性能、高可用、可扩展、可持久化。
  • 应用场景:缓存、分布式锁、消息队列、统计系统等。

数据操作

  • 键的类型:字符串
  • 值的类型:字符串、哈希表、列表、集合、有序集合等。

字符串

  • 写入
    • set key value,设置键值
    • setnx key value,设置键值,如果键已存在时设置失败返回0,否则返回1。
    • setex key seconds value,设置键值,过期时间(秒)
    • mset key value [key value ...],批量设置键值
    • incr key,自增,如果无法解析为整数将报错。
    • incrby key increment,加上整数。
    • decr key,自减,如果无法解析为整数将报错。
    • decrby key decrement,减去整数。
    • append key value,追加字符串到末尾,如key不存在等于set。
    • del key [key ...],删除键。
    • expire key seconds,设置过期时间(秒)。
    • ttl key,查看剩余过期时间(秒)。
  • 读取
    • get key,获取键值
    • mget key [key ...],批量获取键值。
    • strlen key,获取长度。
    • keys pattern,查找键,支持正则。
    • exists key [key ...],判断键是否存在,存在1,不存在0。
    • type key,查看 value 类型。

哈希表

  • 写入
    • hset key field value,设置哈希表字段值。
    • hmset key field value [field value ...],批量设置哈希表字段值。
    • hdel key field[field ...],删除哈希表字段。
  • 读取
    • hget key field,获取哈希表字段值。
    • hmget key field [field ...],批量获取哈希表字段值。
    • hgetall key,获取哈希表所有字段和值。
    • hkeys key,获取哈希表所有字段。
    • hvals key,获取哈希表所有值。
    • hlen key,获取哈希表字段数量。
    • hexists key field,判断哈希表字段是否存在。

列表

  • 写入
    • lpush key value [value ...],将值插入列表头部。
    • rpush key value [value ...],将值插入列表尾部。
    • lpop key,移除并返回列表第一个元素。
    • rpop key,移除并返回列表最后一个元素。
    • lset key index value,设置指定索引的元素值。
    • ltrim key start stop,裁剪列表,仅保留子集。
  • 读取
    • lrange key start stop,获取列表指定范围内的元素。
    • lindex key index,获取指定索引的元素。
    • llen key,获取列表长度。

集合

  • 写入
    • sadd key member [member ...],将一个或多个成员元素加入到集合中,已经存在将忽略。
  • 读取
    • smembers key,获取集合所有成员。
    • scard key,获取集合成员数量。
    • sismember key member,判断 member 元素是否是集合 key 的成员。
    • sinter key [key ...],获取多个集合的交集。
    • sdiff key [key ...],获取某个集合与其他集合的差集。
    • sunion key [key ...],获取多个集合的并集。

有序集合

  • 写入
    • zadd key score member [score member ...],将一个或多个成员元素及其分数值加入到有序集 key 中,如果成员已经存在,将更新其分数值。
  • 读取
    • zrange key start stop [withscores],获取有序集 key 中指定区间内的成员。
    • zcard key,获取有序集 key 的成员数。
    • zscore key member,返回有序集 key 中,成员 member 的分数值。
    • zrank key member,返回有序集 key 中成员 member 的排名。
    • zcount key min max,计算在有序集 key 中,指定闭区间分数范围的成员数量。

实现原理

高性能的关键

  • 内存数据库的特点,cpu占用少,瓶颈在网络IO。
  • 高并发的基础:IO多路复用,linux的epoll。
  • 命令执行采用单线程模型串行化,避免上下文切换和加锁。
  • 4.0版本之前网络IO和命令执行都是在一个线程中完成。
  • 4.0版本开始支持网络IO的多线程,命令执行单线程。

字符串存储

  • int,字符串长度小于等于12字节且可以表示为整数时,采用int类型。
  • embstr,字符串长度小于40字节时,数据内嵌存储。
  • raw,字符串长度大于等于40字节时,数据存储到另外一个结构体中。
  • 字符串值最大容量512M。

哈希表存储

  • ziplist,元素较少时使用压缩列表,节省内存占用,线性查找。
  • hashtable,元素较多时使用哈希表。

列表存储

  • ziplist,元素较少时使用压缩列表,节省内存占用。
  • linkedlist,元素较多时使用双向链表。

持久化机制

  • RDB:定期写入,基于快照恢复,数据丢失。
  • AOF:每次追加,基于日志重放恢复。
    • 同步策略:
      • 每次写操作都追加,安全性最高,但性能差。
      • 每秒写入。
  • RDB AOF:混合实现。

内存淘汰策略

  • 何时触发:超过最大内存限制时。
  • 淘汰范围:
    • allkeys所有键
    • volatile存在过期时间的键。
  • 淘汰算法:
    • LRU:最近最少使用,淘汰最长时间未使用的数据。
    • LFU:最不经常使用,淘汰使用次数最少的。
    • ttl:剩余时间最短的先淘汰。
    • 随机:随机淘汰。

应用场景

缓存

缓存的更新策略
  • 内存淘汰,内存不足时淘汰数据。
  • 超时剔除,ttl 时间到期删除。
  • 主动更新,应用中数据库写入同时更新缓存。
  • 根据场景选择更新策略:
    • 低一致性或固定数据,使用内存淘汰。
    • 高一致性或频繁更新的数据,使用主动更新,超时剔除做备用方案。
双写一致性
  • 主动更新需确保双写一致性。
  • 先修改数据库,再删除缓存。
  • 如果先删除缓存,再修改数据库,高并发时可能存在一致性问题。
  • 强一致性:分布式事务处理写数据库与缓存。
  • 最终一致性:消息队列异步更新,缓存更新失败重试机制。
  • 如果数据库读写分离,需要延迟更新缓存避免读取到旧数据。
缓存穿透
  • 定义:数据不在缓存也不在数据库中,导致每次都会查数据库。
  • 问题:如果利用该漏洞高并发穿透,可能导致数据库崩溃。
  • 解决:
    • 缓存空置,设置较小ttl。
    • 使用布隆过滤器,可以用极少内存代价,避免不需要的查询。
布隆过滤器
  • 定义:空间效率极高的概率型数据结构,检测元素是否在一个集合里。
  • 概率:有可能有,无肯定无。
  • 原理:
    • 结构由初始值0的位图数组和n个哈希函数组成。
    • 每次通过n个哈希取模,映射到位图上。
    • 写入时标记1,读取是判断n个位置是否都为1,不支持删除。
  • 使用:4.0后提供的功能,插件形式,需配置开启。
  • 命令:
    • bf.add key value,将元素添加到布隆过滤器。
    • bf.exists key value,判断元素是否在布隆过滤器中。
    • bf.reserve key error_rate initial_size,设置布隆过滤器参数。
      • error_rate是错误率,默认 0.01,即百分之一。
      • initial_size是初始化大小,默认100。
      • 初始化大小尽可能满足实际长度,否则会导致错误率上升。
      • 当超过容量时,扩容需要将历史数据再添加一遍。
      • 当 key 存在时不可设置。
      • 错误率越低、元素数越多,占用内存越大。
缓存击穿
  • 定义:某个热点key过期,大量请求同时访问该key,导致数据库压力剧增。
  • 解决:
    • 互斥锁,缓存未命中时,先获取锁,获取成功则读取数据库,获取失败则等待后再读取缓存。
    • 逻辑过期,在物理过期前先逻辑过期刷新数据。
    • 预热,在系统启动时,提前加载缓存。
    • 热点数据延长过期时间。
缓存雪崩
  • 定义:同时大量请求无法在redis处理,导致数据库压力剧增直至崩溃。
  • QPS:redis几W,数据库几K,量级差10倍。
  • 解决:
    • 避免大量缓存同时过期:
      • 微调过期时间,TTL添加随机值(1到3分钟)。
      • 服务降级,非核心业务缓存失效返回预定义信息。
    • 避免缓存实例故障:
      • 健康检查,熔断或限流。
      • 利用集群提高可用性。

分布式锁

  • 基本方法
    • 获取锁:
      • 先 setnx 再 expire。
      • 原子操作:set key value ex seconds nx。
      • redis 2.6.12 版本开始支持用 set 设置过期时间和实现 not existed。
    • 释放锁:del命令
  • 误删问题:
    • 原因:多线程中,如果某线程加的锁被超时释放锁,则可能释放到其他线程的锁。
    • 解决:锁的值存入 UUID,释放锁时先判断锁标识,再释放锁。
    • 极端情况:判断锁标识与释放锁非原子性仍然可能导致误删,lua脚本可以解决原子性问题。
  • 可重入锁:
    • 定义:同一个线程外层加锁,内层再次加锁不会阻塞,加锁多少次就需要解锁多少次。
    • 实现:需使用计数器,lua脚本可以实现。
  • 实操:
    • go 可以使用 redsync 实现 redis 分布式锁。
    • java 可以使用 redisson 实现 redis 分布式锁。

消息队列

  • 使用list:
    • 基本操作:lpush 生产者入队,rpop 消费者消费。
    • 阻塞消费:brpop,阻塞等待。
    • 缺点:只支持单消费者。
  • 使用发布订阅:
    • 基本操作:subscribe 订阅,publish 发布。
    • 优点:支持多消费者,支持广播。
    • 缺点:不支持消息持久化,消息无法保证顺序。
    • 一般不建议使用。
  • 使用stream:
    • 5.0 版本起支持的功能。
    • 基本操作:xadd/xread等。
    • 具备消息中间件的基本能力。
    • 局限性:
      • 消息丢失:redis宕机,主从切换未完成同步时。
      • 消息积压时消耗内存。
    • 应用:适用于消息丢失不敏感且消息积压概率小的情况,否则使用专业的消息队列中间件。

集群架构

集群作用

  • 提高可用性,避免单点故障。
  • 提高吞吐能力。

集群模式

主从读写分离
  • 主从复制原理:
    • 采用异步方式。
    • 全量复制:将 RDB 发给从节点。
    • 增量复制:从节点向主节点请求增量数据。
  • 哨兵机制:监控与故障恢复,将从节点提升为主节点。
分片集群
  • 将数据分散到多个节点上,各个节点再实现主从。
  • 哈希槽:将key映射到16384个槽上,再分配到不同节点上。
  • 支持故障转移和数据迁移。

0 人点赞