浅谈Redis

前言

Redis数据是内存读写,基于Reactor模式和优化的数据结构,使得速度很快,存储的是KV键值对数据。

一、基本数据类型

1.1 String(字符串)

最简单也是最常用的数据结构,底层实现为简单动态字符串(Simple Dynamic String,SDS)。
SDS有五种实现方式:SDS_TYPE5、SDS_TYPE8、SDS_TYPE16、SDS_TYPE32、SDS_TYPE64,Redis会根据初始化的长度决定使用哪种类型,从而减少内存的使用。
使用场景:常规数据缓存、用户单位时间的计数、页面单位时间的计数、分布式锁。

1.2 List(列表)

双向链表,list的pop操作是原子性的。
使用场景:消息队列,秒杀缓存商品队列,list做分页查询。

1.3 Set(集合)

无序唯一的集合。
使用场景:去重的场景,实现交集、并集、差集的操作。

1.4 Hash(散列表)

对象的每个字段都单独存储,可获取部分字段信息。
使用场景:存储对象。

1.5 Zset(有序集合)

有序唯一的集合,通过跳表实现。
使用场景:排行榜。

二、持久化机制

2.1 持久化目的

  • 重用数据,比如重启机器。
  • 数据同步,Redis集群的主从节点通过RDB文件同步数据。

2.2 持久化方式

  • RDB(快照):redis.conf默认配置中,触发条件为1/5/15分钟,其中如果有10000/10/1个key发生变化,则会再次触发。
    RDB是Redis由子线程执行,并不会阻塞主线程。
  • AOF(追加):每一条写命令,写入缓冲区中,再子线程每秒写入文件中。
  • 混合:RDB+AOF

三、内存管理

Redis通过过期字典,保存数据的过期时间。字符串通过setex设置过期时间,其他方法依靠expire命令。

3.1 过期策略

  • 定期删除:每隔一段时间,抽取一批key,检查过期则删除。
  • 惰性删除:取出key时,检查过期则删除。
  • 混合:定期+惰性

3.2 淘汰机制

过期策略依然会存在漏网之鱼,需要内存淘汰机制,避免OutOfMemory。主要为LRU(最近最少使用),LFU(使用频率最少),随机。

8中数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集中,淘汰最近最少使用的数据。
  • volatile-ttl:从已设置过期时间的数据集中,淘汰即将要过期的数据。
  • volatile-random:从已设置过期时间的数据集中,淘汰任意数据。
  • allkeys-lru:当内存不足以容纳新数据时,淘汰最近最少使用的数据。(最常用的)
  • allkeys-random:从数据集中,淘汰任意数据。
  • no-eviction:禁止淘汰数据。
  • volatile-lfu:从以设置过期时间的数据集中,淘汰使用频率最少的数据。
  • allkeys-lfu:当内存不足以容纳新数据时,淘汰使用频率最少的数据。

四、性能优化

4.1 批量操作减少网络传输

一个Redis命令的执行,可以简化为4步:发送命令,命令排队,命令执行,返回结果。
使用批量操作,可以减少第1、4步的网络IO。

  • 原生批量操作命令
    mget(获取多个指定key的值)、mset(设置多个指定key 的值)、hmget(获取哈希表中多个指定字段的值)、hmset(设置多个field-value)、sadd(添加多个元素)
  • pipeline
    对于不支持批量操作的命令,可以利用pipeline(流水线),将一批Redis命令封装成一组,一次性的提交到Redis服务器。
  • Lua脚本
    批量操作多条命令。

4.2 大量key集中过期

定期删除,是由主线程执行,如果突然遇到大量key过期的话,客户端请求会被阻塞。
解决方案:

  • key的过期时间随机设置。
  • 开启lazy-free,删除操作交给子线程。

4.3 大key

一个key对应的value所占内存比较大,就可以看作是大key。

查找大key方式:

  • Redis自带参数--bigkeys。
  • 开源工具分析RDB文件,如redis-rdb-tools、rdb_bigkeys。
  • 公有云的Redis分析服务。

处理大key方法:

  • 分割大key:将大key分割成多个小key,需要修改业务代码。
  • 手动清理:使用unlink命令异步删除。
  • 采用合适的数据结构
  • 开启lazy-free

4.4 热key

一个key的访问次数比较多,就可以看作的热key。
处理热key会占用大量的资源,可能会影响其他请求。如果热key的请求量超过了Redis的处理能力,Redis就会宕机。

查找热key方式:

  • Redis自带参数--hotkeys。
  • 使用monitor命令。
  • 开源项目分析,如京东零售的hotkey。
  • 根据业务情况预估。
  • 公有云的Redis分析服务。

解决rekey方法:

  • 读写分离。
  • 使用Redis Cluster,将热点数据分散存储再多个Redis节点上。
  • 二级缓存,将热key存放一份到JVM本地内存中(可以用Caffeine)。

4.5 慢查询命令

Redis中大部分命令都是O(1)的时间复杂度,也有少部分命令是O(n),如:
keys *:返回所有符合规则的key。
hgetall:返回一个Hash中所有的键值对。
lrange:返回List中指定范围内的元素。
smembers:返回Ser中的所有元素。
sinter/sunion/sdiff:甲酸多个Set的交集/并集/差集。

时间复杂度在O(n)以上的命令,如:
zrange/zrevrange:返回Sorted Set中指定排名范围内的所有元素。
zremrangebyrank/zremrangebyscore:移除Sorted Set中指定排名/范围内的所有元素。

查找慢查询方式:

  • 在redis.conf文件中,使用参数slowlog-log-slower-than设置耗时命令的阈值,使用slowlog-max-len设置最大记录条数,这样就记录了慢查询日志(slow log),通过slowlog get命令获取日志。

4.6 内存碎片

产生内存碎片的原因:

  • Redis存储数据时,向操作系统申请的内存空间,可能大于实际需要的空间。
  • 频繁修改Redis中的数据。

查看碎片方式:

  • 使用info memory命令

清理碎片方法:

  • Redis自带的内存整理,activedefrag配置为yes

4.7 主从同步

  • 增量同步:master维护一个缓冲区,记录slave复制的偏移量,当slave发来同步请求时,master发送偏移量之后的写命令。
  • 全量同步:slave向master发送同步请求,master生成RDB文件,并将执行命令期间的写操作存到缓冲区,将RDB+缓冲区一起发送给slave。

五、常见问题

5.1 缓存穿透

大量请求的key不合理,既不在缓存中,也不在数据库中。

解决方法:

  • 参数校验:对不合理的、不符合格式的参数,直接返回错误信息。
  • 布隆过滤器:将数据存放在bloom中,判断不存在是100%。

5.2 缓存击穿

大量请求的key是热key,不在缓存中,但在数据库中。通常是过期造成的,也可能是未发现的热key。

解决方法:

  • 热key永不过期,或者过期时间比较长。
  • 热key提前预热。
  • 请求数据库时,先获取互斥锁,保证只有一个请求会落到数据库上。

5.3 缓存雪崩

缓存在同一时间大面积失效,导致大量请求直接落到数据库上。

解决方法:

  • Redis集群。
  • 过期时间随机设置。
  • 设置二级缓存。

5.4 缓存和数据库的一致性

遇到写请求时,先更新DB,然后删除cache。

如果更新DB成功,而删除cache失败,那么增加cache更新重试机制:如果Redis当前不可用导致的失败,可以隔一段时间重试;如果多次失败,将失败的key存入队列中,等Redis可用之后,在将key删除。

5.5 Redis阻塞

造成阻塞的原因:

  • O(n)命令。
  • 手动save生成RDB快照。
  • AOF日志记录阻塞(主线程执行完命令之后再记录日志到磁盘)。
  • AOF刷盘阻塞。
  • AOF文件重写阻塞。
  • 大key阻塞。
  • 查找大key命令。
  • 删除大key。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容