Redis 很屌,不懂使用规范就糟蹋了
这可能是最中肯的 Redis 使用规范了
码哥,昨天我被公司 Leader 批评了。
我在单身红娘婚恋类型互联网公司工作,在双十一推出下单就送女朋友的活动。
谁曾想,凌晨 12 点之后,用户量暴增,出现了一个技术故障,用户无法下单,当时老大火冒三丈!
经过查找发现 Redis 报 Could not get a resource from the pool。
获取不到连接资源,并且集群中的单台 Redis 连接量很高。
于是各种更改最大连接数、连接等待数,虽然报错信息频率有所缓解,但还是持续报错。
后来经过线下测试,发现存放 Redis 中的字符数据很大,平均 1s 返回数据。
码哥,可以分享下使用 Redis 的规范么?我想做一个唯快不破的真男人!
通过 Redis 为什么这么快?这篇文章我们知道 Redis 为了高性能和节省内存费劲心思。
所以,只有规范的使用 Redis,才能实现高性能和节省内存,否则再屌的 Redis 也禁不起我们瞎折腾。
Redis 使用规范围绕如下几个纬度展开:
- 键值对使用规范;
- 命令使用规范;
- 数据保存规范;
- 运维规范。
键值对使用规范
有两点需要注意:
- 好的
key命名,才能提供可读性强、可维护性高的 key,便于定位问题和寻找数据。 value要避免出现bigkey、选择高效的序列化和压缩、使用对象共享池、选择高效恰当的数据类型(可参考《Redis 实战篇:巧用数据类型实现亿级数据统计》)。
key 命名规范
规范的 key命名,在遇到问题的时候能够方便定位。Redis 属于 没有 Scheme的 NoSQL数据库。
所以要靠规范来建立其 Scheme 语意,就好比根据不同的场景我们建立不同的数据库。
敲黑板
把「业务模块名」作为前缀(好比数据库 Scheme),通过「冒号」分隔,再加上「具体业务名」。
这样我们就可以通过 key 前缀来区分不同的业务数据,清晰明了。
总结起来就是:「业务名:表名:id」
比如我们要统计公众号属于技术类型的博主「码哥字节」的粉丝数。
set 公众号:技术类:码哥字节 100000
码哥,key 太长的话有什么问题么?
key 是字符串,底层的数据结构是 SDS,SDS 结构中会包含字符串长度、分配空间大小等元数据信息。
字符串长度增加,SDS 的元数据也会占用更多的内存空间。
所以当字符串太长的时候,我们可以采用适当缩写的形式。
不要使用 bigkey
码哥,我就中招了,导致报错获取不到连接。
因为 Redis 是单线程执行读写指令,如果出现bigkey 的读写操作就会阻塞线程,降低 Redis 的处理效率。
bigkey包含两种情况:
- 键值对的
value很大,比如value保存了2MB的String数据; - 键值对的
value是集合类型,元素很多,比如保存了 5 万个元素的List集合。
虽然 Redis 官方说明了 key和string类型 value限制均为512MB。
防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过 5000。
码哥,如果业务数据就是这么大咋办?比如保存的是《金ping梅》这个大作。
我们还可以通过 gzip 数据压缩来减小数据大小:
/**
* 使用gzip压缩字符串
*/
public static String compress(String str) {
if (str == null || str.length() == 0) {
return str;
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
return new sun.misc.BASE64Encoder().encode(out.toByteArray());
}
/**
* 使用gzip解压缩
*/
public static String uncompress(String compressedStr) {
if (compressedStr == null || compressedStr.length() == 0) {
return compressedStr;
}
byte[] compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);;
String decompressed = null;
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressed);
GZIPInputStream ginzip = new GZIPInputStream(in);) {
byte[] buffer = new byte[1024];
int offset = -1;
while ((offset = ginzip.read(buffer)) != -1) {
out.write(buffer, 0, offset);
}
decompressed = out.toString();
} catch (IOException e) {
e.printStackTrace();
}
return decompressed;
}
集合类型
如果集合类型的元素的确很多,我们可以将一个大集合拆分成多个小集合来保存。
使用高效序列化和压缩方法
为了节省内存,我们可以使用高效的序列化方法和压缩方法去减少 value的大小。
protostuff和 kryo这两种序列化方法,就要比 Java内置的序列化方法效率更高。
上述的两种序列化方式虽然省内存,但是序列化后都是二进制数据,可读性太差。
通常我们会序列化成 JSON或者 XML,为了避免数据占用空间大,我们可以使用压缩工具(snappy、 gzip)将数据压缩再存到 Redis 中。
使用整数对象共享池
Redis 内部维护了 0 到 9999 这 1 万个整数对象,并把这些整数作为一个共享池使用。
即使大量键值对保存了 0 到 9999 范围内的整数,在 Redis 实例中,其实只保存了一份整数对象,可以节省内存空间。
需要注意的是,有两种情况是不生效的:
Redis 中设置了
maxmemory,而且启用了LRU策略(allkeys-lru 或 volatile-lru 策略),那么,整数对象共享池就无法使用了。这是因为 LRU 需要统计每个键值对的使用时间,如果不同的键值对都复用一个整数对象就无法统计了。
如果集合类型数据采用 ziplist 编码,而集合元素是整数,这个时候,也不能使用共享池。
因为 ziplist 使用了紧凑型内存结构,判断整数对象的共享情况效率低。
命令使用规范
有的命令的执行会造成很大的性能问题,我们需要格外注意。
生产禁用的指令
Redis 是单线程处理请求操作,如果我们执行一些涉及大量操作、耗时长的命令,就会严重阻塞主线程,导致其它请求无法得到正常处理。
KEYS:该命令需要对 Redis 的全局哈希表进行全表扫描,严重阻塞 Redis 主线程;
应该使用 SCAN 来代替,分批返回符合条件的键值对,避免主线程阻塞。
FLUSHALL:删除 Redis 实例上的所有数据,如果数据量很大,会严重阻塞 Redis 主线程;
FLUSHDB,删除当前数据库中的数据,如果数据量很大,同样会阻塞 Redis 主线程。
加上 ASYNC 选项,让 FLUSHALL,FLUSHDB 异步执行。
我们也可以直接禁用,用rename-command命令在配置文件中对这些命令进行重命名,让客户端无法使用这些命令。
慎用 MONITOR 命令
MONITOR 命令会把监控到的内容持续写入输出缓冲区。
如果线上命令的操作很多,输出缓冲区很快就会溢出了,这就会对 Redis 性能造成影响,甚至引起服务崩溃。
所以,除非十分需要监测某些命令的执行(例如,Redis 性能突然变慢,我们想查看下客户端执行了哪些命令)我们才使用。
慎用全量操作命令
比如获取集合中的所有元素(HASH 类型的 hgetall、List 类型的 lrange、Set 类型的 smembers、zrange 等命令)。
这些操作会对整个底层数据结构进行全量扫描 ,导致阻塞 Redis 主线程。
码哥,如果业务场景就是需要获取全量数据咋办?
有两个方式可以解决:
- 使用
SSCAN、HSCAN等命令分批返回集合数据; - 把大集合拆成小集合,比如按照时间、区域等划分。
数据保存规范
冷热数据分离
虽然 Redis 支持使用 RDB 快照和 AOF 日志持久化保存数据,但是,这两个机制都是用来提供数据可靠性保证的,并不是用来扩充数据容量的。
不要什么数据都存在 Redis,应该作为缓存保存热数据,这样既可以充分利用 Redis 的高性能特性,还可以把宝贵的内存资源用在服务热数据上。
业务数据隔离
不要将不相关的数据业务都放到一个 Redis 中。一方面避免业务相互影响,另一方面避免单实例膨胀,并能在故障时降低影响面,快速恢复。
设置过期时间
在数据保存时,我建议你根据业务使用数据的时长,设置数据的过期时间。
写入 Redis 的数据会一直占用内存,如果数据持续增多,就可能达到机器的内存上限,造成内存溢出,导致服务崩溃。
控制单实例的内存容量
建议设置在 2~6 GB 。这样一来,无论是 RDB 快照,还是主从集群进行数据同步,都能很快完成,不会阻塞正常请求的处理。
防止缓存雪崩
避免集中过期 key 导致缓存雪崩。
码哥,什么是缓存雪崩?
当某一个时刻出现大规模的缓存失效的情况,那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。
运维规范
- 使用 Cluster 集群或者哨兵集群,做到高可用;
- 实例设置最大连接数,防止过多客户端连接导致实例负载过高,影响性能。
- 不开启 AOF 或开启 AOF 配置为每秒刷盘,避免磁盘 IO 拖慢 Redis 性能。
- 设置合理的 repl-backlog,降低主从全量同步的概率
- 设置合理的 slave client-output-buffer-limit,避免主从复制中断情况发生。
- 根据实际场景设置合适的内存淘汰策略。
- 使用连接池操作 Redis。
最后,欢迎在留言区分享一下你常用的使用规范,我们一起交流讨论。
好文推荐
Redis 持久化篇:AOF 与 RDB 如何保证数据高可用
Redis 实战篇:巧用 Bitmap 实现亿级海量数据统计
Redis 很屌,不懂使用规范就糟蹋了的更多相关文章
- [Redis]Redis 概述及基本使用规范.
1 nosql的简介 1.1 nosql简介 随着互联网Web2.0网站的兴起,传统的关系数据库在应付Web2.0网站,特别是超大规模和高并发的SNS类型的Web2.0纯动态网站已经显得力不从心,暴露 ...
- Redis 内存优化神技,小内存保存大数据
大家好,我是「码哥」,大家可以叫我靓仔. 这次码哥跟大家分享一些优化神技,当你面试或者工作中你遇到如下问题,那就使出今天学到的绝招,一招定乾坤! 如何用更少的内存保存更多的数据? 我们应该从 Redi ...
- redis使用规范文档 20170522版
运维redis很久了,一直是口头给rd说各种要求,尝试把这些规范总结成文档 摘选一些可能比较通用的规则如下: 强制:所有的key设置过期时间(最长可设置过期时间10天,如有特殊要求,联系dba说明原因 ...
- redis规范(实用)
redis功能强大,数据类型丰富,再快的系统,也经不住疯狂的滥用.通过禁用部分高风险功能,并挂上开发的枷锁,业务更能够以简洁.通用的思想去考虑问题,而不是绑定在某种实现上. Redis 根据不同的用途 ...
- redis开发使用规范
redis开发使用规范 1.冷热数据分离,不要将所有数据全部都放在Redis中 根据业务只将高频热数据存储到Redis中[QPS大于5000],对于低频冷数据可以使用mysql等基于磁盘的存储方式. ...
- Redis之Sentinel高可用安装部署
背景: 之前通过Redis Sentinel 高可用实现说明和Redis 复制.Sentinel的搭建和原理说明大致已经介绍了sentinel的原理和实现,本文再次做个简单的回顾.具体的信息见前面的两 ...
- Redis的安装以及在项目中使用Redis的一些总结和体会
第一部分:为什么我的项目中要使用Redis 我知道有些地方没说到位,希望大神们提出来,我会吸取教训,大家共同进步! 注册时邮件激活的部分使用Redis 发送邮件时使用Redis的消息队列,减轻网站压力 ...
- 前端(HTML/CSS/JS)-HTML编码规范
一.HTML编码规范 1. img标签要写alt属性 根据W3C标准,img标签要写alt属性,如果没有就写一个空的.但是一般要写一个有内容的,根据图片想要表达的意思,因为alt是在图片无法加载时显示 ...
- redis安装配置文件配置
环境: 虚拟机redhat5.5安装redis4.0.2 虚拟机IP:192.168.60.130 reids端口:6379 安装步骤不详述了(可参考: 亲密接触Redis-第一天), 大致步骤如下: ...
随机推荐
- Vue3学习(六)之使用Vue3进行数据绑定及显示列表数据
一.写在前面 说来还是比较惭愧的,从周二开始事就比较多,周三还电脑坏了,然后修电脑等等一些杂事,忙的团团转,因为周二.周三自己走的过多了,导致不敢直腰,周四卧床一天. 之前都听说<陈情令> ...
- 【Java虚拟机9】类加载器之命名空间详解
前言 前面介绍类加载器的时候,介绍了一下命名空间这个概念.今天就通过一个例子,来详细了解一下[类加载器的命名空间].然后通过这个例子,我们可以总结一下双亲委托模型的好处与优点. 例1(不删除class ...
- Convolutional Neural Network-week2编程题1(Keras tutorial - 笑脸识别)
本次我们将: 学习到一个高级的神经网络的框架,能够运行在包括TensorFlow和CNTK的几个较低级别的框架之上的框架. 看看如何在几个小时内建立一个深入的学习算法. 为什么我们要使用Keras框架 ...
- SpringMvc 中 FrameworkServlet 覆盖 service 的有点。
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Se ...
- [技术博客] 通过ItemTouchHelper实现侧滑删除功能
通过ItemTouchHelper实现侧滑删除功能 一.效果 二.具体实现 demo中演示的这种左滑删除的效果在手机APP中比较常用,安卓也为我们提供了专门的辅助类ItemTouchHelper来帮助 ...
- 8.18考试总结[NOIP模拟43]
又挂了$80$ 好气哦,但要保持优雅.(草 T1 地衣体 小小的贪心:每次肯定从深度较小的点向深度较大的点转移更优. 模拟一下,把边按链接点的子树最大深度排序,发现实际上只有上一个遍历到的点是对当前考 ...
- 力扣 - 剑指 Offer 57 - II. 和为s的连续正数序列
题目 剑指 Offer 57 - II. 和为s的连续正数序列 思路1(双指针/滑动窗口) 所谓滑动窗口,就是需要我们从一个序列中找到某些连续的子序列,我们可以使用两个for循环来遍历查找,但是未免效 ...
- Jquery校验中国身份证号码是否正确
在项目中使用表单时经常会涉及到身份证号码是否正确的校验,下面看看应该中国二代身份证号码应该怎么用Jquery校验呢? 二代身份证校验码的计算方法 二代身份证由17位数字和一位校验码组成,那么校验方法是 ...
- P1231 教辅的组成(最大流)
P1231 教辅的组成 这个题一看便知是网络流量,(三分图??滑稽..) 就一个小细节,如果我们仅仅将所有的点分成三部分跑网络流的话会有点小问题.. 因为这可能导致一本书被重复利用,就是有两条流经过同 ...
- 安装配置多个版本JDK
前言:JDK有多个版本,有时为了开发需要切换不同的版本,在一部电脑上安装多个JDK,只需要按以下配置,每次即可轻松使用.以下环境为Windows10 安装JDK 安装JDK8 配置环境变量 需要配置J ...