Redis使用规范及调优策略_风情客家__的博客-程序员宝宝

技术标签: 使用规范  Redis  调优策略  

参考文章:Redis性能调优策略

redis调优的实战经验 - 大叔据 - 博客园

优化 Redis 的使用策略

其它资料参考:

本文根据redis的info命令查看redis的内存使用情况以及state状态,来观察redis的运行情况以及需要作出的相应优化。

一、Redis使用规范

1.1 Redis Key 的命名策略

Redis 是 K-V 形式的缓存数据库,每一个需要缓存的 Object 都需要唯一的 Key 来标识。但是,我们日常在做开发的时候,经常会出现一个公司或者部门之间共用一个 Redis 集群的情况。所以,这就有可能会造成 Key 冲突,引发数据被覆盖的问题(即使是同一个部门,也可能存在不同的研发人员使用了同名的 Key)。

  • 根据业务名称命名 - 不建议

一个项目中,相似的业务是很常见的,所以,如果不同的研发人员使用了同名的 Key,就会导致系统的 bug。同时,如果不同的项目组在共同使用一个 Redis 集群,也会发生这样的问题。例如:

127.0.0.1:6379> set name qinyi
OK
# 同一个项目中的不同业务使用了同名的 key (不同的研发人员共同开发造成)
127.0.0.1:6379> lpush name qinyi imooc
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379>
  • 在业务名称之前加上项目的名称 - 不建议

这样做可以大概率的避免不同的项目引起的 Key 冲突问题,但是,仍然不能避免同一个项目中的不同业务。例如:

# a、b 两个项目在使用 Redis 时,各自以项目的名称为 Key 的前缀
127.0.0.1:6379> set a-name qinyi
OK
127.0.0.1:6379> set b-name imooc
OK
# 仍不能避免同一个项目中的不同业务使用了同名的 key
127.0.0.1:6379> lpush a-name qinyi imooc
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379>
  • Key = 项目名称-业务名称-时间戳 - 建议

这种命名 Key 的方式是比较好的,相比于上一种方式,多增加了一个时间戳标识。这里的含义就是与不同的开发人员定义相同时间戳的概率很低很低。具体时间戳的精度可以根据业务的实际情况选择,比如:天、小时等等。例如:

# Key 中添加天级的时间戳来避免 Key 冲突的问题
127.0.0.1:6379> set a-name-20190314 qinyi
OK
127.0.0.1:6379> lpush a-name-20190315 qinyi imooc
(integer) 2
127.0.0.1:6379>

1.2 过期时间的设定

在使用 Redis 时,绝大多数情况都需要给 Key 设定过期时间,这个是非常有必要的。原因如下:

  • 作为缓存,加快程序的执行速度。但是缓存的 Object 很可能是会经常变化的,不要长期驻留在 Redis 中
  • 当 Redis 的内存使用超过一个阈值(可以自定义)时,会触发 Redis 的缓存淘汰策略,此时的 LRU 过程是非常慢的。极大的影响 Redis 的性能

Redis有四个命令可以设置键的存活时间或过期时间: 

  • expire:用于设置key还剩余多少秒过期
  • pexpire:用于设置key还有多少毫秒过期
  • expireat:用于设置key的过期时间(到期的具体秒数时间戳)
  • pexpireat:用于设置key的过期时间(到期的具体毫秒时间戳)

所以,在保存 KV 时,最好是设定过期时间。例如:

127.0.0.1:6379> set name qinyi
OK
127.0.0.1:6379> expire name 30
(integer) 1
127.0.0.1:6379> ttl name
(integer) 25
127.0.0.1:6379>

关于 Redis 设定过期时间(这里包含过期键的删除)需要知道:

  • Redis 对于设定过期时间的键是怎么保存的
  • Redis 键的过期删除策略

1.3 数据结构的选择

Redis 常用的数据结构一共有五种:string、hash、list、set、zset(sorted set)。可以发现,大多数场景下使用 string 都可以去解决问题。但是,这并不一定是最优的选择。下面,简单说明下它们各自的适用场景

  • string:单个的缓存结果,不与其他的 KV 之间有联系
  • hash:一个 Object 包含有很多属性,且这些属性都需要单独存储。注意:这种情况不要使用 string,因为 string 会占据更多的内存
  • list:一个 Object 包含很多数据,且这些数据允许重复、要求有顺序性
  • set:一个 Object 包含很多数据,不要求数据有顺序,但是不允许重复
  • zset:一个 Object 包含很多数据,且这些数据自身还包含一个权重值,可以利用这个权重值来排序

另外,Redis 还提供了一些特别的数据结构,用于一些特殊的场景。例如:Geo、HyperLogLog

  • Geo:LBS 相关的应用,可以参考:Redis Geo
  • HyperLogLog:允许存在误差的基数统计,可以参考:Redis HyperLogLog 

1.4 持久化机制的选择

Redis 提供了两种持久化机制:RDB 和 AOF

  • RDB:在指定的时间间隔内将内存中的数据快照写入磁盘,实际操作过程是 fork 一个子进程,先将数据写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储
  • AOF:以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录

关于这两种持久化方式的优点不做解释,可以根据其实现形式得出结论。下面,分析下它们各自的缺点。

RDB 的缺点

  • 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么 RDB 将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
  • 由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟

AOF 的缺点

  • 对于相同数量的数据而言,AOF 文件通常要大于 RDB 文件。RDB 在恢复大数据时的速度比 AOF 的恢复速度要快
  • 由于同步策略的不同,AOF 在运行效率上往往会慢于 RDB

总结:

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(AOF),还是愿意写操作频繁的时候,不启用备份来换取更高的性能

1.5 不要使用 Redis 存储单 Key 大数据

这里所说的大数据,不是说不使用 Redis 存储更多的数据,而是说单个 KV 很大的情况。这里最核心的原因是:Redis 是单进程单线程的应用。这种工作方式在操作过程上避免了线程切换的耗时,而且不需要考虑线程安全,无需获取锁、释放锁等操作,是非常简单、直接的。但是:

  • Redis 的单线程工作机制,无法用多核并行处理。如果遇到 IO 量大的操作,出现等待 IO 的情况会增多,即使 Redis 底层用的是 IO 多路复用技术,IO 的事件句柄也是要占用资源的
  • Redis 的持久化需要将数据保存下来,KV 的数据量很大,也会影响持久化的执行

二、Redis调优策略

2.1 memory参数

#used_memory=实际缓存占用的内存+Redis自身运行所占用的内存(如元数据、lua)。
#这个值是由Redis使用内存分配器分配的内存,不包括内存碎片浪费的内存。
used_memory:13409011624 

#从操作系统上显示已经分配的内存总量。
used_memory_rss:13740019719  

#内存使用的峰值大小
used_memory_peak:13409011624  

#系统总内存
total_system_memory:33567678464  

#Lua脚本引擎所使用的内存大小。
used_memory_lua:37888  

#最大可用内存(可配置,默认为total_system_memory)
maxmemory:0  

#淘汰机制,noneviction为禁止淘汰数据
maxmemory_policy:noeviction  

#内存碎片率
mem_fragmentation_ratio:1.02;  

#编译时指定的Redis内存分配器,可以是libc、jemalloc、tcmalloc。
mem_allocator:jemalloc-4.0.3;

#自启动起Redis服务处理命令的总数 
total_commands_processed:3500  

2.2 used_memory 过大导致的问题

2.2.1 引发内存交换

当Redis内存使用率超过可用内存(maxmemory可配置)的95%时,操作系统会进行内存与swap空间数据交换。 把内存中旧的或不再使用的内容写入硬盘上即Swap分区,以便腾出新的物理内存给新页或活动页(page)使用。 在硬盘上进行读写操作要比在内存上进行读写操作,时间上慢了近 5个数量级,内存是0.1μs单位、而硬盘是10ms。如果Redis进程上发生内存交换,那么Redis和依赖Redis上数据的应用会受到严重的性能影响。

2.2.2 rdb持久化风险

在没有开启持久化的情况下, redis宕机或者内存使用率超过95%会有丢数据的风险。若使用快照(rdb)持久化,Redis会fork一个子进程把当前内存中的数据完全复制一份写入到硬盘上( fork使用的内存和redis当前使用的内存会一样多)。因此若是当前使用内存 超过可用内存的45%时触发快照功能,那么此时进行的内存交换会变的非常危险(可能会丢失数据)。 倘若在这个时候实例上有大量频繁的更新操作,问题会变得更加严重。

2.3 避免used_memory 过大

  • 尽可能的使用Hash数据结构

因为Redis在储存小于100个字段的Hash结构上,其存储效率是非常高的。所以 在不需要集合(set)操作或list的push/pop操作的时候,尽可能的使用Hash结构。比如,在一个web应用程序中,需要存储一个对象表示用户信息,使用单个key表示一个用户,其每个属性存储在Hash的字段里,这样要比给每个属性单独设置一个key-value要高效的多。 通常情况下倘若有数据使用string结构,用多个key存储时,那么应该转换成单key多字段的Hash结构。 如上述例子中介绍的Hash结构应包含,单个对象的属性或者单个用户各种各样的资料。Hash结构的操作命令是HSET(key, fields, value)和HGET(key, field),使用它可以存储或从Hash中取出指定的字段。

  • 设置key的过期时间

一个减少内存使用率的简单方法就是, 每当存储对象时确保设置key的过期时间。倘若key在明确的时间周期内使用或者旧key不大可能被使用时,就可以用Redis过期时间命令(expire,expireat, pexpire, pexpireat)去设置过期时间,这样Redis会在key过期时自动删除key。 假如你知道每秒钟有多少个新key-value被创建,那可以调整key的存活时间,并指定阀值去限制Redis使用的最大内存。

  • 回收key

在Redis配置文件Redis.conf中, 通过设置“maxmemory”属性的值可以限制Redis最大使用的内存,修改后重启实例生效。 也可以使用客户端命令config set maxmemory 去修改值,这个命令是立即生效的,但会在重启后会失效,需要使用config rewrite命令去刷新配置文件。

  1. 若是启用了Redis快照功能,应该设置“maxmemory”值为系统可使用内存的45%,因为快照时需要一倍的内存来复制整个数据集,也就是说如果当前已使用45%,在快照期间会变成95%(45%+45%+5%),其中5%是预留给其他的开销。
  2. 如果没开启快照功能,maxmemory最高能设置为系统可用内存的95%
  • 淘汰策略

当内存使用 达到设置的最大阀值时,需要选择一种 key的回收策略,可在Redis.conf配置文件中修改“maxmemory-policy”属性值。

  • 若是Redis数据集中的key都设置了过期时间,那么“volatile-ttl”策略是比较好的选择。
  • 但如果key在达到最大内存限制时没能够迅速过期,或者根本没有设置过期时间。那么设置为“allkeys-lru”值比较合适,它允许Redis从整个数据集中挑选最近最少使用的key进行删除(LRU淘汰算法)

Redis还提供了一些其他淘汰策略,如下: 

volatile-lru:使用LRU算法从已设置过期时间的数据集合中淘汰数据。

volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰。

volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰。

allkeys-lru:使用LRU算法从所有数据集合中淘汰数据。

allkeys-random:从数据集合中任意选择数据淘汰

no-enviction:禁止淘汰数据。

通过设置maxmemory为系统可用内存的45%或95%(取决于持久化策略)和设置“maxmemory-policy”为“volatile-ttl”或“allkeys-lru”(取决于过期设置),可以比较准确的限制Redis最大内存使用率, 在绝大多数场景下使用这2种方式可确保Redis不会进行内存交换。倘若你担心由于限制了内存使用率导致丢失数据的话,可以设置noneviction值禁止淘汰数据。

2.4 used_memory_rss 过大解决办法

当mem_fragmentation_ratio远大于1时即used_memory_rss/used_memory(稍大于1正常),说明redis中存在大量的内存碎片,一个比较好的解决办法就是重启redis,这里需要注意的是 如果用的是aof持久化,那么重启之前要进行rewriteaof操作,否则会无效。还有可以指定Redis使用的内存分配器,一般管理员不推荐,麻烦而且要重新编译。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/justlpf/article/details/115904962

智能推荐

Oracle Java SE Development Kit 8u301最新版下载地址_allway2的博客-程序员宝宝_jdk8u301下载

Java SE Development Kit 8u301This software is licensed under theOracle Technology Network License Agreement for Oracle Java SE Product / File Description File Size Download Linux ARM 64 RPM Package 59.15 MB jdk-8u3.

MySQL 别名_指剑的博客-程序员宝宝_mysql 别名

可以给字段 表达式 函数 以及表 取别名语法:select 字段名 函数别名 from 表名;例如 :select bookname name from book;##这里是将name设置为bookname的别名;为多个字段起别名:select 函数1 函数1别名,函数2 函数2别名 from b表名;例如:select number num,price money ...

root 身份登录“鉴定故障”解决方法!_java_doom的博客-程序员宝宝

1、先用普通用户登陆,在shell里登陆root(输入命令: su 输入root密码)。2、然后编辑 vi /etc/pam.d/gdm(当 然你也可以用其他编辑器),注释掉“authrequired pam_succeed_if.so user != root quiet”这行,在前面加上“#”即可,保存。3、同理,编辑/etc/pam.d/gdm-password,注释掉“aut...

爬虫高性能相关_weixin_30379973的博客-程序员宝宝

一 背景知识 爬虫的本质就是一个socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,那只能等待爬取一个结束后才能继续下一个,效率会非常低。需要强调的是:对于单线程下串行N个任务,并不完全等同于低效,如果这N个任务都是纯计算的任务,那么该线程对cpu的利用率仍然会很高,之所以单线程下串行多个爬虫任务低效,是因为爬虫任务是明显的IO密集...

客户端工具MySQL使用_ღ᭄小艾ヅ࿐的博客-程序员宝宝

目录:一、客户端工具MySQL使用1.案例:2.案例:3.案例:4.扩展了解(默认的4个数据库信息):二、客户端工具mysqladmin使用1.案例:一、客户端工具MySQL使用mysql:mysql命令行工具,一般用来连接访问mysql数据库1.案例:连接192.168.74.10服务器上的mysql数据库(用户名:lin密码:123456)mysql -h 192.168.74.10 -P 3306 -ulin -p ‘123456’2.案例:根据不同的套接字连接不同的数

linux查看系统cpu和内存使用状况的方法_期待一片自己的蓝天的博客-程序员宝宝

在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要。在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况。运行 top 命令后,CPU 使用状态会以全屏的方式显示,并且会处在对话的模式 -- 用基于 top 的命令,可以控制显示方式等等。退出 top 的命令为 q (在 top 运行中敲 q 键一次)。top命令是Linux下常用的性能

随便推点

php 表单搜索,wordpress的搜索表单searchform.php_郭海昊的博客-程序员宝宝

在主题中,通过如下代码,就能调出搜索表单了:-------------Display search form using searchform.php Theme file.https://codex.wordpress.org/Function_Reference/get_search_form如果主题里没有searchform.php的话,会使用wordpress内置的函数get_search...

微信小程序 蓝牙(点击出现弹窗,下拉选择蓝牙,点击即可传输数据)_辉——删繁就简的博客-程序员宝宝_蓝牙小程序

不少博主都写过蓝牙小程序,但都是类似于蓝牙调试助手,使用起来很不方便,(多页面跳转,一个个自己手动选择uuid,serviceid等,给初学者带来了很大的困扰,或者直接写死,不知道咋改)按照正常的逻辑,蓝牙其实就是点击个图标,弹出来搜索到的周围蓝牙,然后点击蓝牙,之后就可以正常传输数据就成。在完成自己的第一个小程序时候,就用到了类似的功能。记录下,希望给大家参考。实验效果:刚点击进去的图片点击蓝牙测试按钮触发了检测自己设备蓝牙是否打开,搜索周围蓝牙,以及监听函数等。在弹窗里滑动选择自己需要的蓝牙,进

完整的哲学家进食代码_zhuqianUESTC的博客-程序员宝宝_哲学家就餐代码

//#include "stdafx.h"#include //HADNDLE#include //#include //time(0)//#include #include "iostream"using namespace std;const unsigned int N=2; //哲学家数目const int THINKING=1; //标记当前哲学

代理(Proxy)_请叫我木丁西的博客-程序员宝宝

代理(Proxy)      对指定接口的某一个方法进行功能扩展,可以使用代理代理API|--java.lang.reflect.Proxy         static Object newProxyInstance(  ClassLoader loader,    当前使用的类加载器  Class[] interfaces,   目标对象实现的接口类型  Inv

【cocos2d-x】游戏构成要素②----使用多个层_QilongPan的博客-程序员宝宝

接下来 先讲解怎么在一个CCScene中使用多个CCLayer先写三个类Bottomlayer,UIlayer,MainScene ,代码如下:MainScene :在本例中主要用于生成CCScene对象Bottomlayer:在本例中本层可以实现独立的拖动,旋转UIlayer:在本例中本曾保持禁止/*  * MainScene.h  *  *  Created

推荐文章

热门文章

相关标签