Redis最佳实践

文章目录

  • 一. Redis键值设计
    • 1. 优雅的Key结构
    • 2. 拒绝BigKey
    • 3. 恰当的数据类型
  • 二. 批处理优化
    • 1. Pipeline
    • 2. 集群下的批处理
  • 三.服务端优化
    • 1. 持久化配置
    • 2. 慢查询
    • 3. 命令及安全配置
    • 4. 内存配置
  • 四. 集群最佳实践

一. Redis键值设计

1. 优雅的Key结构

Redis的key虽然可以自定义,但最好遵循下面的几个最佳实践约定:

  • 遵循基本格式:[业务名称]:[数据名]:[id]
  • 长度不超过44字节
  • 不包含特色字符

例如:登录业务login:user:10

  • 可读性强
  • 避免key冲突
  • 方便管理
  • 更节省内容:key是String类型,底层编码包含int、embstr和raw三种。embstr在小于44字节使用,采用连续内存空间,内存占用更小

在这里插入图片描述

2. 拒绝BigKey

什么是Bigkey
BigKey通常以key的大小和key中成员的数量来综合判定,例如:

  • key本身的数据量过大:一个String类型的key,它的值为5MB
  • key中的成员数过多:一个ZSET类型的key,它的成员数量为10000个
  • Key中成员的数据量过大:一个Hash类型的key,它的成员数量虽然只有1000个,但这些成员value值总大小为100MB

推荐值

  • 单个Key的value小于10KB
  • 对于集合类型的key,建议元素数量小于1000

在这里插入图片描述
BigKey的危害

  • 网络阻塞:对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在屋里机变慢
  • 数据倾斜:BigKey所在的Redis实例内存使用率远超过其他实例,无法使数据分片的内存资源达到均衡
  • Redis阻塞:对元素较多的hash、list、zset等做运算会耗时较久、使主线程被阻塞
  • CPU压力:对BigKey的数据序列化和反序列化会导致CPU的使用率飙升,影响Redis实例和本机其它应用

如何发现BigKey

  • Redis-cli --bigkeys:利用redis-cli提供的–bigkeys参数,可以遍历分析所有的key,并返回Key的整体统计信息与每个数据的Top1的big key

  • scan扫描:自己编程,利用scan扫描redis中的所有key,利用strlen、hlen等命令判断key的长度(此处不建议使用MEMORY USAGE)

在这里插入图片描述

  • 第三方工具:如Redis-RDB- Tools分析RDB快照文件,全面分析内存使用情况(github上面下载,有使用方法)
  • 网络监控:自定义工具,监控进出Redis的网络数据,超出预警值时主动报警
    如何删除BigKey
    BigKey占用内存较多,即便是删除这样的key也需要耗费很长时间,导致Redis主线程阻塞,引发一系列问题。
  • redis3.0 及以下版本:如果是集合类型,则遍历BigKey所有元素,先逐个删除子元素,最后删除key
  • redis4.0 以后:Redis在4.0后提供了异步删除命令:unlink

3. 恰当的数据类型

案例一:比如存储一个User对象,我们有三种存储方式:
方式一:Json字符串

user:1 {“name”:“jack”,“age”:21 }

优点:实现简单粗暴
缺点:数据耦合,不够灵活

方式二:字段打散

user:1:name “jack”
user:1:age 21

优点:可以灵活访问对象任意字段
缺点:占用空间大,没办法做到统一控制

方式三:hash

user:1 name Jack
user:1 JacK 21

优点:底层使用ZipList,空间占用小,可以灵活访问对象的任意字段
缺点:代码相对复杂
案例二:假如有hash类型的key,其中100万对feild和value,field是自增的id,这个key存在什么问题?如何优化?

Redis最佳实践_第1张图片

  1. hash的entry数量超过500时,会使用hash表而不是ZipList,内存占用较多
  2. 方案一:可以通过hash-max-entries配置entry上限。但是如果entry过多就会导致BigKey问题

Redis最佳实践_第2张图片
3. 方案二:拆分为String字符串

Redis最佳实践_第3张图片
这种方案存在的问题:

  • string结构底层没有太多内存优化,内存占用比较多
  • 想要批量获取这些数据比较麻烦
  1. 方案三:将原来大的hash拆分为小的hash,将id / 100作为key,将id%100作为field,这样每100个元素为一个hash

二. 批处理优化

1. Pipeline

单个命令的执行流程:客户端发送命令,命令通过网络传输到服务端,Redis服务端接收到命令后执行客户端的命令,再通过网络将结果返回给客户端,所以一次命令的响应时间=1次往反的网络传输耗时+1次Redis执行命令耗时。(命令很多时网络耗时会非常大)
N条命令批量执行:客户端一次批量发送N条命令,Redis收到命令后执行,然后一次性返回N条命令的执行结果,所以N次命令的响应时间=1次往返的网络传输耗时+N次redis执行命令耗时

Redis提供了很多Mxxx这样的命令,可以实现批量插入数据,例如mset和hmset

也要注意,不要在一次批处理中传输太多命令,否则单次命令占用带宽过多,会导致网络阻塞

MSET虽然可以批处理,但是却只能操作部分数据类型,因此如果有对复杂数据类型的批处理需要,建议使用PipeLine功能:

void TestPipeline(){
//创建管道
Pipeline pipeline=jdedis.pipelined();
for(int i=1;i<=100000;i++)
{
	pipeline.set("test:key_"+i,"value_"+i);
	if(i %1000=0)
	//每放入1000条数据,批量执行
	pipeline.sync();
}
}

总结

  1. 批处理的方案:
  • 原生的M操作
  • Pipeline批处理
  1. 注意事项:
  • 批处理不建议一次携带太多命令
  • Pipeline的多个命令之间不具备原子性

2. 集群下的批处理

如MSET或PipeLine这样的批处理需要在一次请求中携带多种命令,而此时如果Redis是一个集群,那批处理命令的多个key必须落在一个插槽中,否则就会导致执行失败(插槽不同可能在不同的节点上,而不同的节点则需要不同的连接,而批处理只能在一条链接上处理)

解决方案
Redis最佳实践_第4张图片

三.服务端优化

1. 持久化配置

Redis持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化请遵循下列建议:

  • 用来做缓存的Redis实例尽量不要开启持久化功能
  • 建议关闭RDB持久化功能,使用AOF持久化
  • 利用脚本定期在slave节点做RDB,实现数据备份
  • 设置合理的rewrite阈值,避免频繁使用bgrewrite
  • 配置no-appendfsync-on-rewrite=yes。禁止在rewrite期间做aof,避免因aof引起的阻塞
    部署有关的建议:
  • Redis实例的物理机要预留足够的内存,应对fork和rewrite
  • 当个Redis实例的内存上限不要太大,例如4G或5G。可以加速fork的速度、减少主从同步、数据迁移压力
  • 不要与CPU密集型应用部署在一起
  • 不要与高硬盘负载应用一起部署。例如:数据库、消息队列

2. 慢查询

简介:在Redis中,执行耗时超过某个阈值的命令,叫做慢查询
慢查询的阈值可以通过配置指定:

  • slowlog-log-slower-than:慢查询阈值,单位是毫秒。默认是10000,建议1000
    慢查询会被放入慢查询日志中,日志的上限有限,可以通过配置指定:
  • slowlog-max-len:慢查询日志(本质是一个队列)的长度。默认是128建议是100

修改上面两个配置可以使用:config set命令

config get slowlog-max-len
config get slow-log-slower-than
config set slow-log-slower-than 1000

查看慢查询日志列表:
slowlog len:查询慢查询日志长度
slowlog get[n]:读取n条慢查询日志
slowlog reset:清空慢查询列表

3. 命令及安全配置

漏洞重现看这篇文章

漏洞出现的核心原因:

  • Redis未有设置密码
  • 利用了Redis的config set命令动态修改Redis配置
  • 使用了ROOT账号权限启动Redis

为了避免这些漏洞,这里给出一些建议:

  • Redis一定要设置密码
  • 禁止线上使用下面命令:keys、flushall、config set等命令 。可以使用rename-command(更改命令的名称,别人就不知道了)
  • bind:限制网卡,禁止外网网卡访问
  • 开启防火墙
  • 不要使用root账户启动redis
  • 尽量不要有默认的端口

4. 内存配置

当redis内存不足时,可能导致频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要我们警惕,并快速定位到内存占用的原因。
Redis内存信息

内存占用 说明
数据内存 时Redis的主要的部分,存储Redis的键值信息。主要问题是BigKey问题、内存碎片问题
进程内存 Redis主进程本身的运行肯定需要占用内存,入代码、常量池等等;这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比可以忽略不计
缓冲区内存 一般包括客户端缓存、AOF缓存、复制缓冲区等。客户端缓冲区又包括输入缓冲区和输出缓冲区两。这部分内存占用波动比较大,不当使用bigkey,可能导致内存泄露

Redis提供了一些命令,可以查看到Redis目前的内存分配状态:

  • info memory
  • memory xxx

内存缓冲区常见的有三种:

  • 复制缓冲区:主从复制的repl_backlog_buf,如果太小可能导致频繁的全量复制,影响性能。通过repl_backlog_size来设置,默认1mb
  • AOF缓冲区:AOF刷盘之前的缓存区域,AOF执行rewrite的缓冲区。无法设置上限
  • 客户端缓冲区:分为输入缓冲区和输出缓冲区,输入缓冲最大1GB且不能设置。输出缓冲区可以设置
    info clients:查看redis客户端连接信息
    clients list:客户端连接详细信息

四. 集群最佳实践

集群虽然具备高可用特性,能自动实现故障恢复,但如果使用不当,也会存在一些问题:

  1. 集群完整性问题

在redis的默认配置中,如果发现任意一个插槽不可用,则整个集群会停止对外服务;
为了保证高可用性,这里建议将cluster-require-full-coverage配置为false

  1. 集群带宽问题

集群节点之间会不断的互相ping来确定集群中其它节点的状态,每次ping携带的信息至少包括:

  • 插槽信息
  • 集群状态信息

集群中节点越多集群状态信息数据量也越大,10个节点的相关信息可以达到1kb,此时每次集群互通需要的带宽会非常高
解决途径:

  1. 避免大集群,集群的节点数量不要太多,最好小于1000,如果业务庞大,则建立多个集群
  2. 避免在单个物理机中运行太多的redis实例
  3. 配置合适的cluster-node-timeout值
  1. 数据倾斜问题
  2. 客户端性能问题
  3. 命令的集群兼容性问题
  4. lua和事务问题

你可能感兴趣的:(Redis,redis,数据库,java)