原文地址:Redis 脱坑指南
博客地址:http://www.extlight.com

一、前言

Redis 是一款 key-value 内存数据库。由于其上手快,执行效率高,拥有多种数据结构,支持持久化以及集群等功能和特点被众多互联网公司所使用。但是,如果使用和操作不当,会引起内存浪费,甚至系统宕机等严重后果。

二、要点分析

2.1 使用正确的数据类型

Redis 5 种数据类型中,string 类型最为常用,也最为简单。但是,能解决问题不代表使用了正确的数据类型。

例如,将一个用户(name,age,city)信息保存到 Redis 中,下边有三种方案:

方案1:使用 string 类型,每个属性当作一个 key

set user:1:name laowang
set user:1:age 40
set user:1:city shanghai
优点:简单直观,每个属性支持更新操作
缺点:使用过多的 key,占用的内存较大,同时用户信息的聚合性较差,管理和维护麻烦

方案2:使用 string 类型,将用户信息序列化成字符串保存

// 序列化用户信息
String userInfo = serialize(user)set user:1 userInfo
优点:简化存储步骤
缺点:序列化和反序列化存在一定开销

方案3:使用 hash 类型,每个属性使用一对 field-value,但只用一个 key

hmset user:1 name laowang age 40 city shanghai
优点:简单直观,合理使用可以减少内存空间

总结:尽量减少 Redis 中的 key。

2.2 警惕 Big Key

big key 一般指的是字符串类型 value 值非常大(大于10KB),或哈希、列表、集合、有序集合元素个数多(大于5000个)的 key。

big key 会对 Redis 造成很多负面影响:

内存不均:在集群环境下,big key 被分配到某个节点机器中,由于不知道被分配到哪个节点上且该节点内存占用大,不利于集群环境下内存的统一管理超时阻塞:由于 Redis 是单线程操作,操作 big key 比较耗时,容易造成阻塞过期删除:big key 不单读写慢,删除也慢,删除过期 big key 也比较耗时迁移困难:由于数据庞大,备份和还原也容易造成阻塞,操作失败

知道了 big key 的危害,我们如何判断和查询 big key 呢?其实,redis-cli 提供了 --bigkeys 参数,键入 redis-cli --bigkeys 即可查询出 big key

找到 big key 后,我们一般会将 big key 拆分成多个小 key 进行存储。这种做法似乎与 2.1 的总结相矛盾,但任何方案都有优缺点,衡量利弊取决于实际情况。

总结:尽量减少 Redis 中的 big key。

补充:如果想查看某个 key 所占用的内存空间,可以使用 memory usage 命令。注意:该命令是 Redis 4.0+ 才开始提供的,如想使用必须将 Redis 升级至 4.0+。

2.3 内存消耗

即便我们合适使用正确的数据类型保存数据,将 Big Key 拆分小 key,还是会出现内存消耗问题,那么 Redis 内存消耗是如何产生的呢?一般由以下 3 种情况产生:

业务不断发展,存储的数据不断增多(不可避免)无效/过期的数据没有及时处理(可优化)没有对冷数据进行降级(可优化)

在优化情况 2 之前,我们得先知道为什么会出现没有及时处理过期数据的问题,这得说到 Redis 提供的 3 种过期删除策略:

定时删除:对于每个设置了过期时间的 key 都会创建一个定时器,一旦达到过期时间就立即删除惰性删除:当访问一个 key 时,才判断该 key 是否已过期,如过期就删除定期删除:每隔一段时间扫描 Redis 中过期 key 的字典,并清除部分过期的 key

由于定时删除需要创建定时器,会占用的大量内存,同时精准删除大量 key 也会消耗大量 CPU 资源,因此 Redis 同时采用的是惰性删除和定时删除两种策略。如果客户端没有请求过期的 key 或定期删除线程没有扫描到并清除这个 key,该 key 就会一直占用着内存,导致内存浪费。

知道了内存消耗的原因后,我们可以很快地想出优化方案:手动删除

当使用完缓存后,缓存即使设置了过期时间,我们也要手动调用 del 方法/命令删除。如果不能当场删除,我们也可在代码中开启定时器定期删除这些过期的 key,相比较 Redis 的两种删除策略,手动清除数据要及时很多。

情况 3 的问题不算大,针对其优化的手段,我们可以调整 Redis 的淘汰策略。

2.4 多命令的执行

Redis 是基于一个 request, 一个 response 的同步请求服务。即当多个客户端向 Redis 服务端发送命令时,Redis 服务端只能接收和处理其中的一个客户端的命令,其他客户端只能等待 Redis 服务端处理好当前的命令并作出响应后才会继续接收和处理其他命令请求。

Redis 处理命令分 3 个过程:接收命令,处理命令,返回结果。由于处理的数据都是在内存中的,因此处理时长通常都是纳秒级别,非常快(big key 除外)。因此,大部分耗时的情况都发生在接受命令和返回结果上。当客户端发送多个命令给 Redis 服务器时,如果有一条命令处理时长很久,其他命令只能等待着,从而影响整体性能。

为了解决这类问题,Redis 提供了 pipeline(管道),客户端可以将多条命令放入 pipeline 中,然后一次性将 pipeline 的命令发给 Redis 服务端处理,当 Redis 服务端处理完毕后再一次性将结果返回给客户端。这样处理减少了客户端与 Redis 服务端的交互次数,从而减少了往返时间,提升了性能。

补充:

Redis pipeline 与原生批量命令对比:

原生批量命令是原子性,pipeline 是非原子性原生批量命令一次只能执行一种命令,pipeline 支持执行多种命令原生批量命令是服务端实现,pipeline 需要服务端和客户端实现

使用 Redis pipeline 的注意事项:

使用 pipeline 装载的命令数量不能太多pipeline 中的命令会按照缓冲的顺序执行,但是可能会穿插其他客户端发来的命令,即不保证时序性pipeline 执行中间某一指令出现异常,会继续执行后续的指令,即不保证原子性

2.5 缓存穿透

在项目中运用缓存,我们通常的设计思路如下图:

发送请求查询数据,查询规则是先查缓存,如果缓存没有数据再查询数据库,将查到的数据放入缓存最后返回数据给客户端。如果请求的数据是不存在的,最终每次请求都会请求到数据库中,这就是缓存穿透

缓存穿透存到很大的安全隐患,如果有人使用工具发送大量请求,请求一个不存在的数据,大量请求会流入到数据库上,导致数据库压力增大,可能会导致数据库宕机,进而影响整个应用的正常运行,导致系统瘫痪。

解决这类问题,重点在于减少对数据库的访问,通常有以下几种方案:

缓存预热:系统发布上线后,提前把相关的数据直接加载到缓存系统中设置默认值:如果请求最终落在数据库中,数据库也查不出数据,给缓存 key 设置一个默认值,放入缓存中,注意:由于这个默认值是无意义的,因此我们需要设置过期时间,减少内存占用布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个不存在的数据肯定会被 bitmap 拦截掉

2.6 缓存雪崩

缓存雪崩: 简单来说是指大量请求访问缓存数据但无法查询到,进而去请求数据库,导致数据库压力增大,性能下降,不堪重负宕机,从而影响整个系统正常运行,甚至系统瘫痪的现象。

比如,一个完整的系统由系统A,系统B,系统C 三个子系统组成,它们的数据请求链是系统A -> 系统B -> 系统C -> 数据库。如果缓存中没有数据,数据库宕机,系统C不能查询数据作出响应,只能处在重试等待的阶段,从而影响了系统B 和系统A。一处节点发生异常导致一连串的问题就像雪山的一阵风吹过引起雪崩的现象。

看到这里,可能有读者会疑惑,缓存穿透和缓存雪崩有什么区别呢?

缓存穿透侧重于请求的数据不在缓存中,从而去请求数据库,就好像直接透过缓存直接请求数据库。缓存雪崩侧重于大请求由于在缓存中查询不出数据,从而访问数据库导致数据库压力增大引起一系列异常。

要解决缓存雪崩问题,还是得先知道导致问题的原因:

Redis 自身出现问题热点数据集中失效

针对原因1,我们可以做主从,集群,尽量让请求都在缓存中查到数据,减少对数据库的访问

针对原因2,给缓存设置过期时间时,错开过期时间(如在基础时间上在增减一个随机值),避免缓存集中失效。同时,我们还可以设置本地缓存(如 ehcache),对接口进行限流或服务降级,也可以减少数据库的访问压力。

三、参考资料

Redis中的管道pipeline操作

Redis应用-布隆过滤器

Redis 脱坑指南相关推荐

  1. 技术人综合症脱坑指南

    We_Can_Do_It.jpg 上周遇到一设计师,改了几次设计稿,在电话中尥蹶子不干了.boss在边低声问:"设计师都这样?" 其实心高气傲的岂止是设计师?程序员,工程师,大部分 ...

  2. [专栏精选]Unity中编码Encoding脱坑指南

    本文节选自洪流学堂公众号技术专栏<大话Unity2018>,未经允许不可转载. 洪流学堂公众号回复专栏,查看更多专栏文章. 洪流学堂,让你快人几步.你好,我是郑洪智. 大智:"昨 ...

  3. Android 7.0脱坑指南

    本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发. Android适配系列: Android 6.0 的动态权限管理 Android 7.0脱坑指南 Android 8.0适配指北 ...

  4. STM8S系列单片机脱坑指南

    STM8S脱坑指南 你好!这是你第一次使用 STM8S003K3 .如果你想学习如何使用STM8S003K3, 可以速速滚蛋,因为这一点也不好玩.理由如下: 1.参考资料少 2.官方资源少且混乱 3. ...

  5. 大数据 SQL Boy 脱坑指南

    不可否认的是 SQL 是一个伟大的发明,它让增删改查的操作更加地便捷化,而且 SQL 的学习成本相对其他编程语言来说较低,被逼到会写 SQL 的运营和产品我都见过不少... 大数据行业跟 SQL 更是 ...

  6. 虚幻4脱坑指南——官网C++编程教程中第一人称设计游戏教程的若干问题及解决方法

    一.前言 我使用的虚幻4引擎版本为4.25.3,碰到的问题是针对官网C++编程教程中第一人称设计游戏教程出现的情况. 二.问题与实现 2.1.缺少引用的编译错误 如图1所示的步骤2.7中,将官网的代码 ...

  7. 基于Blinker的小爱同学语音控制【脱坑指南】

    基于Blinker的小爱同学语音控制ESP8266 前些天在使用esp8266时遇到很多小爱不能控制设备问题,现在来叙述我控制过程中所遇到的问题,以及到完整的小爱能成功控制Esp8266的过程. 1. ...

  8. CV脱坑指南(二):ResNet·downsample详解

    到这儿来-(feat.美丽的嫦娥姐姐 嗯经过了一周的实(mo)践(yu)之后,打算还是给ResNet出个续集 毕竟downsample这一块儿实在是挺费解的 其中ResNet出现的downsample ...

  9. 脱坑指南:Vant选择器没有确认按钮和标题?!的解决办法

    文章目录 问题描述 解决方法! 在van-picker标签中加入 show-toolbar!!! 问题描述 项目用了vant选择器,代码怎么看都觉得没有问题,就死活不出确认取消按钮和标题,找bug找到 ...

最新文章

  1. JavaEE程序员必读图书大推荐 .
  2. SprintBoot中如何构造Bean原理分析
  3. 自定义MyBatis
  4. React个Vue的对比
  5. Dancing_Links总结 【by AbandonZHANG】
  6. html object标签与java,html之object标签的classid收集
  7. 终于学会后空翻!历经多次NG,波士顿动力机器人再get新技能
  8. PyQt5 QComboBox 样例代码
  9. 工具的使用——谷歌浏览器(chrome) (二)
  10. 周二强新概念c语言答案,新编C语言程序设计(周二强版)课后习题练习4答案
  11. POJ 3468 A Simple Problem with Integers(线段树区间更新)
  12. Web---HTTP请求、重定向、转发和数据压缩
  13. nvarchar和varchar的区别
  14. 浅谈软件外包项目报价
  15. 采用UltraISO软碟通制作Dos启动盘教程
  16. 好教程推荐系列:收录常见的Qt面试题
  17. C++多线程std::async、std::future、std::packaged_task、std::promise
  18. oracle数据库监听说法正确,Oracle数据库错题合集
  19. 度量学习 度量函数 metric learning deep metric learning 深度度量学习
  20. Yolanda,Withings,PICOOC以及RyFit四款智能人体成分秤对比评测

热门文章

  1. 谈谈新的前端框架 Svelte 和现代前端框架的特点
  2. 跨时钟域传输——结绳法(芯动力mooc)
  3. 51单片机 蔬菜灌溉自动控制系统设计 程序 proteus 仿真 温湿度控制系统
  4. 37行代码实现一个简单的打游戏AI
  5. git 迁移代码库,将源代码库中代码迁移到一个新库中
  6. 聊城计算机教师资格证面试,2017年下半年教师资格证面试通过率有多少?
  7. sed命令替换指定字符后不固定字符串,文本信息的替换删除
  8. 那么多个KOL,哪一个才是最带货的?!
  9. 三种Cross-lingual模型 (XLM, XLM-R, mBART)详解
  10. 小程序影藏溢出的gif_推荐5个免费制作GIF动图的软件,国产动漫也能运用自如...