如何进行redis优化?(史上最全)(代码片段)

架构师-尼恩 架构师-尼恩     2022-12-06     662

关键词:

推荐:尼恩Java面试宝典(史上最全 + 面试必备)详请点击此链接

尼恩Java面试宝典,33个最新pdf,含2000多页不断更新、持续迭代 具体详情,请点击此链接

如何进行redis优化?

来自于小伙伴的面试真题。

谈优化前我们首先要对Redis要有一个全局的认识,

Redis是单线程,Redis作为KV数据库包括访问框架,操作模块,索引模块,存储模块;

Redis的访问方式包括UNIX套接字和TCP;

操作模块和数据结构相关,包括string,list,set,Hash,Sorted Set;

索引模块是为了更迅速的通过key定位到value,Redis采用的索引方式是哈希表(其它常用的索引方式还包括B+树,字典树,跳表等);

存储模块主要是负责分配及持久化,Redis支持的持久化方式包括RDB(快照)以及AOF(日志);

尽量使用短的key

降低内存,减少哈希表的算法执行时间;

当然在精简的同时,不要完了key的“见名知意”。对于value有些也可精简,比如性别使用0、1。

不要存过大的数据;

过大的数据存取和删除都会更为耗时;

避免使用keys *

keys会导致查找的时间复杂度为0(n);

其它复杂度命令也要少用;Redis 命令参考 — Redis 命令参考有给出每个命令的时间复杂度;

keys *, 这个命令是阻塞的,即操作执行期间,其它任何命令在你的实例中都无法执行。

当redis中key数据量小时到无所谓,数据量大就很糟糕了。所以我们应该避免去使用这个命令。可以去使用SCAN,来代替。

在存到Redis之前先把你的数据压缩下

redis为每种数据类型都提供了两种内部编码方式,在不同的情况下redis会自动调整合适的编码方式。

设置 key 有效期

我们应该尽可能的利用key有效期。比如一些临时数据(短信校验码),过了有效期Redis就会自动为你清除!

设置过期时间,减少redis中的数据量,但要注意避免大量数据同时失效;

选择回收策略(maxmemory-policy)

当 Redis 的实例空间被填满了之后,将会尝试回收一部分key

根据你的使用方式,强烈建议使用 volatile-lru(默认) 策略——前提是你对key已经设置了超时。

但如果你运行的是一些类似于 cache 的东西,并且没有对 key 设置超时机制,可以考虑使用 allkeys-lru 回收机制 。

maxmemory-samples 3 是说每次进行淘汰的时候 会随机抽取3个key 从里面淘汰最不经常使用的(默认选项)

maxmemory-policy 六种方式 :
volatile-lru:只对设置了过期时间的key进行LRU(默认值)
allkeys-lru : 是从所有key里 删除 不经常使用的key
volatile-random:随机删除即将过期key
allkeys-random:随机删除
volatile-ttl : 删除即将过期的
noeviction : 永不过期,返回错误

减少不必要的连接;

能用连接池的情况下使用连接池;

使用bit位级别操作和byte字节级别操作来减少不必要的内存使用。

bit位级别操作:GETRANGE, SETRANGE, GETBIT and SETBIT
byte字节级别操作:GETRANGE and SETRANGE

尽可能地使用hashes哈希存储。

当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能。

想要一次添加多条数据的时候可以使用管道。

限制redis的内存大小(64位系统不限制内存,32位系统默认最多使用3GB内存)

数据量不可预估,并且内存也有限的话,尽量限制下redis使用的内存大小,这样可以避免redis使用swap分区或者出现OOM错误。(使用swap分区,性能较低,如果限制了内存,当到达指定内存之后就不能添加数据了,否则会报OOM错误。可以设置maxmemory-policy,内存不足时删除数据。)

SLOWLOG [get/reset/len]

slowlog-log-slower-than 它决定要对执行时间大于多少微秒(microsecond,1秒 = 1,000,000 微秒)的命令进行记录。
slowlog-max-len 它决定 slowlog 最多能保存多少条日志,当发现redis性能下降的时候可以查看下是哪些命令导致的。

使用pipline批量操作数据,进行性能优化

redis的管道功能在命令行中没有,但是redis是支持管道的,在java的客户端(jedis)中是可以使用的

示例代码

//注:具体耗时,和自身电脑有关(博主是在虚拟机中运行的数据)
/**
 * 不使用管道初始化1W条数据
 * 耗时:3079毫秒
 * @throws Exception
 */
@Test
public void NOTUsePipeline() throws Exception 
    Jedis jedis = JedisUtil.getJedis();
    long start_time = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) 
        jedis.set("aa_"+i, i+"");
    
    System.out.println(System.currentTimeMillis()-start_time);


/**
 * 使用管道初始化1W条数据
 * 耗时:255毫秒
 * @throws Exception
 */
@Test
public void usePipeline() throws Exception 
    Jedis jedis = JedisUtil.getJedis();

    long start_time = System.currentTimeMillis();
    Pipeline pipelined = jedis.pipelined();
    for (int i = 0; i < 10000; i++) 
        pipelined.set("cc_"+i, i+"");
    
    pipelined.sync();//执行管道中的命令
    System.out.println(System.currentTimeMillis()-start_time);

Instagram内存优化

Instagram可能大家都已熟悉,当前火热的拍照App,月活跃用户3亿。

Instagram所存图片3亿多时需要解决一个问题:

想知道每一张照片的作者是谁(通过图片ID反查用户UID),并且要求查询速度要相当的块,如果把它放到内存中使用String结构做key-value:

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试:1百万数据会用掉70MB内存,3亿张照片就会用掉21GB的内存。

最好是一台EC2的 high-memory 机型就能存储(17GB或者34GB的,68GB的太浪费了),想把它放到16G机型中还是不行的。

Instagram的开发者向Redis的开发者之一Pieter Noordhuis询问优化方案,得到的回复是使用Hash结构。具体的做法就是将数据分段,每一段使用一个Hash结构存储.

由于Hash结构会在单个Hash元素在不足一定数量时进行压缩存储,所以可以大量节约内存。

这一点在上面的String结构里是不存在的。而这个一定数量是由配置文件中的hash-zipmap-max-entries参数来控制的。

经过实验,将hash-zipmap-max-entries设置为1000时,性能比较好,超过1000后HSET命令就会导致CPU消耗变得非常大。

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试

1百万消耗16MB的内存。总内存使用也降到了5GB。

当然我们还可以优化,去掉mediabucket:key 前缀之后, 长度减少了12个字节。

HSET "1155" "315" "939"
HGET "1155" "315"
"939"

hash的应用

示例:我们要存储一个用户信息对象数据,包含以下信息:
key为用户ID,value为用户对象(姓名,年龄,生日等)如果用普通的key/value结构来存储,

主要有以下2种存储方式:

  1. 将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储

    缺点:增加了序列化/反序列化的开销,引入复杂适应系统(Complex adaptive system,简称CAS)修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护。

  2. 用户信息对象有多少成员就存成多少个key-value对
    虽然省去了序列化开销和并发问题,但是用户ID为重复存储。

Redis提供的Hash很好的解决了这个问题,

提供了直接存取这个Map成员的接口。Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值

内部实现

Redis Hashd的Value内部有2种不同实现,

Hash的成员比较少时,Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht)。

启动时WARNING优化

在我们启动redis时,默认会出现如下三个警告:

一、修改linux中TCP监听的最大容纳数量

WARNING: The TCP backlog setting of 511 cannot be enforced because 
/proc/sys/net/core/somaxconn is set to the lower value of 128.

在高并发环境下你需要一个高backlog值来避免慢客户端连接问题

注意Linux内核默默地将这个值减小到/proc/sys/net/core/somaxconn的值,所以需要确认增大somaxconn和tcp_max_syn_backlog两个值来达到想要的效果。
echo 511 > /proc/sys/net/core/somaxconn

注意:这个参数并不是限制redis的最大链接数。如果想限制redis的最大连接数需要修改maxclients,默认最大连接数为10000

二、修改linux内核内存分配策略

错误日志:WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. 
To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or 
run the command 'sysctl vm.overcommit_memory=1'

原因:

redis在备份数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为8G,这个时候也要同样分配8G的内存给child,如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降

所以内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)。
内存分配策略有三种
可选值:0、1、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1, 不管需要多少内存,都允许申请。
2, 只允许分配物理内存和交换内存的大小(交换内存一般是物理内存的一半)。

三、关闭Transparent Huge Pages(THP)

THP会造成内存锁影响redis性能,建议关闭

Transparent HugePages :用来提高内存管理的性能
Transparent Huge Pages在32位的RHEL 6中是不支持的
执行命令 echo never > /sys/kernel/mm/transparent_hugepage/enabled
把这条命令添加到这个文件中/etc/rc.local

性能分析方法:

1.慢操作日志:

CONFIG SET slowlog-log-slower-than 0 单位是微秒,若设置为0则相当于记录了所有的操作

CONFIG SET slowlog-max-len 10000000 最大慢日志长度

slowlog get 10 查看redis操作的执行耗时;

2.基准测试

redis-cli --intrinsic-latency 120 (120是测试时间)

基准测试要在系统无压力下进行,是为了排除硬件网络等本身就有较大的延迟,当执行延迟达到基准测试时间2倍及以上可以认为redis是变慢了;

记录一份checklist:

1.获取当前环境的基线性能

2.是否使用了慢查询?

用其它命令代替慢查询命令
3.是否对过期key设置了相同的过期时间?

4.是否使用了bigkey?

采用异步删除

5.AOF的配置级别是什么?是否符合业务场景?

避免IO影响性能,关于IO这块看是否可以使用SSD磁盘

6.Redis内存是否占用过大?使用swap了吗?

如果使用了swap可能就需要考虑增加系统内存了

7.Redis运行环境中是否启用了大页机制?

启用了则需要关闭,对持久化不友好,持久化是有了修改就需要大量复制

8.是否使用了主从集群?

启用了主从集群需要控制数据量大小为2G-4G,避免主从复制带来大量的开销;

9.是否使用了多核心CPU?

可以给Redis绑定物理核

参考资料

http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage
http://blog.nosqlfan.com/html/3379.html

https://blog.csdn.net/hi_software/article/details/123594462

时间轮(史上最全)(代码片段)

...f0c;涉及到100w级、1000W级、甚至亿级元素的过期问题,如何进行高性能的定时调度,是一个难题。注:本文从对海量调度任务场景中,高性能的时间轮算法,做了一个系统化、由浅入深的穿透式介绍,帮助... 查看详情

redis与db数据一致性解决方案(图解+秒懂+史上最全)(代码片段)

文章很长,而且持续更新,建议收藏起来,慢慢读!Java高并发发烧友社群:疯狂创客圈(总入口)奉上以下珍贵的学习资源:免费赠送经典图书:极致经典+社群大片好评《Java高并发三部曲... 查看详情

通过事件总线eventbus/asynceventbus进行java模块解耦(史上最全)(代码片段)

事件总线在进行JAVA模块解耦,价值巨大实际开发中,常常通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦,比如,在顶级开源组件hotkey的源码中,就多次用到EventBus/AsyncEventBus进行JAVA模块解耦所以,专门写... 查看详情

bytebuddy(史上最全)(代码片段)

ByteBuddy(史上最全)文章很长,建议收藏起来慢慢读!总目录博客园版为大家准备了更多的好文章!!!!推荐:尼恩Java面试宝典(持续更新+史上最全+面试必备)具体详情,... 查看详情

bytebuddy(史上最全)(代码片段)

ByteBuddy(史上最全)文章很长,建议收藏起来慢慢读!总目录博客园版为大家准备了更多的好文章!!!!推荐:尼恩Java面试宝典(持续更新+史上最全+面试必备)具体详情,... 查看详情

浏览器的工作原理你真的全知道嘛?史上最全图解其原理(代码片段)

...器究竟做了什么;我们希望了解平时常常听说的各种代码优化方案是究竟为什么能起到优化的作用;我们希望更细致地了解浏览器的渲染流程。浏览器的多进程架构一个好的程序常常被划分为几个相互独立又彼此配合的模块,浏... 查看详情

浏览器的工作原理你真的全知道嘛?史上最全图解其原理(代码片段)

...器究竟做了什么;我们希望了解平时常常听说的各种代码优化方案是究竟为什么能起到优化的作用;我们希望更细致地了解浏览器的渲染流程。浏览器的多进程架构一个好的程序常常被划分为几个相互独立又彼此配合的模块,浏... 查看详情

javascript代码技巧——史上最全类型判断(代码片段)

1.typeof(不能区分复杂类型)console.log(typeofbool);//booleanconsole.log(typeofnum);//numberconsole.log(typeofstr);//stringconsole.log(typeofund);//undefinedconsole.log(typeofnul);//objectconsole. 查看详情

javascript代码技巧——史上最全类型判断(代码片段)

1.typeof(不能区分复杂类型)console.log(typeofbool);//booleanconsole.log(typeofnum);//numberconsole.log(typeofstr);//stringconsole.log(typeofund);//undefinedconsole.log(typeofnul);//objectconsole. 查看详情

史上最全cocoapods安装及问题解决(代码片段)

...,废话不多说,下面开始安装吧。a.使用RVM对Ruby进行升级先查看现在的Ruby版本号输入命令:ruby-v安装RVMRVM:RubyVersionManager,Ruby版本管理器,包括Ru 查看详情

史上最全echarts可视化图表详解(代码片段)

...表的配置项包括每一个环节比较容易出现的问题来为大家进行详解。•安装echarts图表•使用echarts图表•echarts图表那些你一定会用到的配 查看详情

prometheus+grafana(史上最全)(代码片段)

...#xff08;elasticsearch+logstash+kibana)原理和实操(史上最全)高级开发必备,架构师必备一键打造本地Prometheus+Grafana实操环境:Prometheus+Grafana 查看详情

prometheus+grafana(史上最全)(代码片段)

...#xff08;elasticsearch+logstash+kibana)原理和实操(史上最全)高级开发必备,架构师必备一键打造本地Prometheus+Grafana实操环境:Prometheus+Grafana 查看详情

50道redis面试题史上最全,以后面试再也不怕问redis了

...存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过10万次读写操作,是已知性能最快的Key-Value... 查看详情

秒懂+史上最全:jvm进程java进程的用户空间与内核空间如何区分?如何区分java进程的内核态与用户态?(代码片段)

...锁(图解-秒懂-史上最全)3:Redis与MySQL双写一致性如何保证?(面试必备&#x 查看详情

springcloudgateway(史上最全)(代码片段)

...习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送经典图书:《Java高并发核心编程(卷1&# 查看详情

史上最全android性能优化方案解析

Android中的性能优分为以下几个方面:布局优化网络优化安装包优化内存优化卡顿优化启动优化……一.布局优化布局优化的本质就是减少View的层级。常见的布局优化方案如下:在LinearLayout和RelativeLayout都可以完成布局的情... 查看详情

史上最全android性能优化方案解析

Android中的性能优分为以下几个方面:布局优化网络优化安装包优化内存优化卡顿优化启动优化……一.布局优化布局优化的本质就是减少View的层级。常见的布局优化方案如下:在LinearLayout和RelativeLayout都可以完成布局的情... 查看详情