百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

如何将 Redis 的内存优化?

liebian365 2024-11-21 17:35 2 浏览 0 评论

作者 | 阿文

责编 | Elle

十一、双十二都是买买买的节奏,但是你知道吗?像京东、淘宝这样的电子商城是如何扛得住几亿的请求吗?

下面是一个电商网站的基本架构,他包括了一个前端入口和一个缓存集群以及数据库集群。

一般来说,web 服务作为前端入口,在 web 服务器内会把一些静态文件通过 CDN 分发到各个节点分摊服务器的请求压力。同时也能加速网站的访问。

当用户的一些查询请求,比如要查询一个商品的信息,会通过前端页面会经过缓存层,如果这些信息在缓存层就已经存在了,则直接返回给客户端,如果没有则去数据库集群查询,并经过缓存层缓存然后返回给客户端

这样设计的好处是可以大大减小数据库集群的压力。我们知道类似 MySQL 这种关系型数据库最大的瓶颈就是当出现像双十一这样的高并发请求时候会给 IO 带来巨大的压力,导致 IO 出现瓶颈。哪怕你的数据库优化做的再好,也不能改变这个基本事实。那么在数据库的上面加一道缓存层就可以大大缓解后端数据库的压力,通过类似 Redis 这样的内存型数据库将热点数据存储在内存中,可以大大提高读写效率和请求时延。

那么, 本文就来和大家分享下 Redis 内存的相关优化措施。

Redis 内存的消耗

知道如何优化 Redis 的内存,我们需要先了解 Redis 都有哪些地方需要消耗内存。

众所周知,Redis 默认会把内存存储在内存中,当然你可以通过一些持久化方案,例如 AOF 或 RDB 的方式来将内存中的数据写入到磁盘中,但是 Redis 默认读取数据还是从内存中读取,因为内存的速度要比磁盘的读写速度快很多倍, 那在 Redis 中是如何对内存的消耗进行统计的呢?我们可以这样查看与内存相关的参数:

127.0.0.1:6379> info memory
# Memory
used_memory:812712
used_memory_human:793.66K
used_memory_rss:6033408
used_memory_rss_human:5.75M
used_memory_peak:812712
used_memory_peak_human:793.66K
total_system_memory:1850044416
total_system_memory_human:1.72G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:7.42
mem_allocator:jemalloc-3.6.0

其中参数含义:

? used_memory 以字节的形式表示 Redis 分配器分配的内存总量,也就是存储在 Redis 存储中的所有数据的占用量。

? used_memory_rss 是从操作系统的角度显示 Redis 进程占用的物理内存总量。

? used_memory_peak 是内存使用的最大值,表示 used_memory 的峰值。

? total_system_memory 表示操作系统的最大内存。

? used_memory_lua 表示 Lua 引擎所消耗的内存大小。

? maxmemory Redis 可分配使用的最大内存量。

? maxmemory_policy 当 Redis使用超过最大分配内存采用什么方式来剔除旧数据,默认是 noeviction

? mem_fragmentation_ratio 表示 used_memory_rss/used_memory 比值,表示内存碎片率

? mem_allocator 表示 Redis 的内存分配器,默认是 jemalloc

在 info memory 中,你会发现有类似 used_memory_human、used_memory_rss_human 等参数带 human 的,这些都是以可读的形式显示对应参数的信息。

在这些参数中,作为维和相关开发人员,我们应该重点关注 used_memory_rss、used_memory、mem_fragmentation_ratio 这三个参数:

? 当 mem_fragmentation_ratio >1 时,说明used_memory_rss、used_memory 多出的部分内存并没有用于数据存储,而是被内存碎片所消耗,如果这两者相差很大,说明碎片率很严重。

? 当 mem_fragmentation_ratio < 1 时,这种情况通常是操作系统把 Redis Swap 到硬盘导致,出现这种情况要格外关注,由于硬盘的速度远远慢于内存,Redis 的性能会大幅下降。

内存消耗分类

介绍完了 Redis 的内存统计参数,我们来讲讲 Redis 的内存消耗主要是发生在哪些方面。Redis 的进程消耗内存主要包括:自身内存+对象内存+缓冲内存+内存碎片。Redis空进程自身内存消耗非常少,通常used_memory_rss在3MB左右, used_memory在800KB左右,一个空的Redis进程消耗内存可以忽略不计。

下面对这几个方面进行一一讲解:

对象内存

这块内存消耗最大,它存储着用户所有的数据 ,我们知道 Redis 存储的数据都是以 key-value 形式的数据类型,当你每次创建键值对时,至少要创建 2 个类型的对象,即key 和 value 对象。对象内存消耗我们可以简单理解为sizeof(keys)+sizeof(values)。

缓存内存

这块主要包括客户端缓存、复制积压缓存区、AOF 缓存区。

客户端缓存

对于客户端缓存,我们可以使用 client-output-buffer-limit 查看相关配置,默认情况下,这部分配置为

client-output-buffer-limit normal 0 0 0 # 客户端
client-output-buffer-limit replica 256mb 64mb 60 # 从客户端
client-output-buffer-limit pubsub 32mb 8mb 60 # 订阅客户端

对于普通客户端来说,限制为0,也就是不限制。因为普通客户端通常采用阻塞式的消息应答模式,何谓阻塞式呢?如:发送请求,等待返回,再发送请求,再等待返回。这种模式下,通常不会导致Redis服务器输出缓冲区的堆积膨胀;

对于Pub/Sub客户端(也就是发布/订阅模式),大小限制是8M,当输出缓冲区超过8M时,会关闭连接。持续性限制是,当客户端缓冲区大小持续60秒超过2M,则关闭客户端连接;

对于slave客户端来说,大小限制是256M,持续性限制是当客户端缓冲区大小持续60秒超过64M,则关闭客户端连接。

我们可以使用如下方式查看这三种客户端的配置

127.0.0.1:6379> CONFIG GET client-output-buffer-limit

1) "client-output-buffer-limit"

2) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60"

这里的 normal 0 0 0 表示普通客户端,值都为 0,表示 Redis 默认不做限制,一般普通客户端的内存消耗几乎可以忽略不计,但是如果有大佬的慢连接客户端接入就会存在问题,我们可以使用 maxclients 来限制最大客户端连接

127.0.0.1:6379> config get maxclients

1) "maxclients"

2) "10000"

slave 268435456 67108864 60 表示的是从客户端的连接,默认的限制是最大 256M 和 60s 内不超过 64M ,如需调整,可以使用如下方式设置,其中 1 表示最大限制,3 表示多少秒,2 表示多少秒内不能超过的最大限制

config set client-output-buffer-limit 'slave 1 2 3'

复制积压缓冲区

复制积压缓冲区是 Redis 2.8 版本以后提供的一个可重用的固定大小的缓冲区,目的是用于实现部分复制功能,它主要根据参数 repl-backlog-size 来控制,默认是 1MB,对于复制积压缓冲区整个主节点只有一个,所有的从节点共享此缓冲区,因此可以设置较大的缓冲区空间,如100MB,它可以有效避免全量复制。

AOF 缓冲区

这部分空间用于在Redis重写期间保存最近的写入命令 ,这部分的消耗主要取决于 AOF 的重写时间和写入量。,通常占用很小。

内存碎片

Redis默认的内存分配器采用jemalloc,jemalloc在64位系统中将内存空间划 分为:小、大、巨大三个范围:

? 小:8byte,192byte,256byte,...,512byte

? 大:[4KB,8KB,12KB,...,4072KB]

? 巨大:[4MB,8MB,12MB,...]

比如当保存5KB对象时jemalloc可能会采用8KB的块存储,而剩下的3KB 空间变为了内存碎片不能再分配给其他对象存储。内存碎片问题虽然是所有 内存服务的通病,但是jemalloc针对碎片化问题专门做了优化,一般不会存在过度碎片化的问题,正常的碎片率(mem_fragmentation_ratio)在1.03左右。

此外子进程也会存在一些内存消耗,这部分消耗主要是耗费在 AOF/RDB 重写时创建子进程的内存消耗,Redis执行fork操作产生的子进程内存占用量对外表现为与父进程相同, 理论上需要一倍的物理内存来完成重写操作。但Linux具有写时复制技术 (copy-on-write),父子进程会共享相同的物理内存页,当父进程处理写请求时会对需要修改的页复制出一份副本完成写操作,而子进程依然读取fork 时整个父进程的内存快照。

内存优化

我们从操作系统和 Redis 本身的优化来讲讲如何优化内存。

操作系统层面

1.vm.overcommit_memory

Linux操作系统对大部分申请内存的请求都回复 yes,以便能运行更多的程序。因为申请内存后,并不会马上使用内存,这种技术叫做 overcommit。vm.overcommit_memory用来设置内存分配策略,有三个可选值:

? 0 表示内核将检查是否有足够的可用内存,如果有足够的内存,内存申请通过,否则申请失败。

? 1 表示允许超量使用内存直到用完为止。

? 2 表示内核绝不过量使用内存,即系统整个内存地址空间不能超过 swap+50%的 RAM 值。

建议将值设置为 1,避免发生 fork 失败。

echo "vm.overcommit_memory=1" >> /etc/sysctl.conf

sysctl vm.overcommit_memory=1

Redis设置合理的maxmemory,保证机器有20%~30%的闲置内存。

2.swappiness

当物理内存不足时,可以将一部分内存页进行swap操作,但是swap 空间是由硬盘提供的,对于需要高并发、高吞吐的应用来说,磁盘IO通常会成为系统瓶颈。

在Linux中,并不是要等到所有物理内存都使用完才会使用到swap,系 统参数swppiness会决定操作系统使用swap的倾向程度。

swappiness的取值范 围是0~100,swappiness的值越大,说明操作系统可能使用swap的概率越 高,swappiness值越低,表示操作系统更加倾向于使用物理内存。默认是 60,它的含义如下:

? 0 Linux kernel 3.5+ 宁愿 OOM 也不用 swap,Linux kernel 3.4以下,宁愿 swap 也不用 OOM

? 1 Linux kernel 3.5+ 宁愿 swap 也不 OOM

? 60 默认值

可以如下方式设置该参数,{bestvalue}为swappiness 的值:

echo vm.swappiness={bestvalue} >> /etc/sysctl.conf

3.THP

Linux Kernel内存中的Transparent Huge Pages(THP)机制,默认会开启 THP,虽然开启THP可以降 低fork子进程的速度,但之后copy-on-write期间复制内存页的单位从4KB变 为2MB,如果父进程有大量写命令,会加重内存拷贝量,从而造成过度内存 消耗。

对于子进程的消耗,我们可以通过如下方式来进行优化:

? 设置sysctl vm.overcommit_memory=1允许内核可以分配所有的物理 内存,防止Redis进程执行fork时因系统剩余内存不足而失败。

? 关闭 THP,防止copy-on- write期间内存过度消耗。

echo never > /sys/kernel/mm/transparent_hugepage/enabled

在设置THP配置时需要注意:有些Linux的发行版本没有将THP放 到/sys/kernel/mm/transparent_hugepage/enabled中,例如Red Hat6以上的THP配 置放到/sys/kernel/mm/redhat_transparent_hugepage/enabled中。而有些版本的 Redis源码中 检查THP时,把THP位置写死:

FILE *fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled","r"); if (!fp) return 0;

所以在发行版中,虽然没有THP的日志提示,但是依然存在THP所带来 的问题:

echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled

4.OOM Killer

OOM killer会在可用内存不足时选择性地杀掉用户进程,OOM killer进程会为每个用户进程设置一个权值,这个权值越高,被 Kill 的概率就越高,反之概率越低。每个进程的权值存放在/proc/{progress_id}/oom_score中,这个值是受/proc/{progress_id}/oom_adj的控制,oom_adj在不同的Linux版本中最小值不同,设置方法:

echo {value} > /proc/${process_id}/oom_adj

对于Redis所在的服务器来说,可以将所有Redis的oom_adj设置为最低 值或者稍小的值,降低被OOM killer杀掉的概率:

for redis_pid in $(pgrep -f "redis-server")
do
echo -17 > /proc/${redis_pid}/oom_adj
done

Oom_adj 只是起到辅助作用,合理的分配和使用内存才是最重要的

4.ulimit

Linux中,可以通过ulimit查看和设置系统当前用户进程的资源数。其中ulimit-a命令包含的open files参数,是单个用户同时打开的最大文件个数:

[root@redis ~]# ulimit -a

core file size (blocks, -c) 0

data seg size (kbytes, -d) unlimited

scheduling priority (-e) 0

file size (blocks, -f) unlimited

pending signals (-i) 6972

max locked memory (kbytes, -l) 64

max memory size (kbytes, -m) unlimited

open files (-n) 1024

pipe size (512 bytes, -p) 8

POSIX message queues (bytes, -q) 819200

real-time priority (-r) 0

stack size (kbytes, -s) 8192

cpu time (seconds, -t) unlimited

max user processes (-u) 6972

virtual memory (kbytes, -v) unlimited

file locks (-x) unlimited

Redis允许同时有多个客户端通过网络进行连接,可以通过配置 maxclients来限制最大客户端连接数。对Linux操作系统来说,这些网络连接 都是文件句柄。

设置方法如下:

ulimit –Sn {max-open-files}

通常来说,ulimit的值要 >= maxclients 的值。

5.TCP backlog

Redis默认的tcp-backlog值为511,可以通过修改配置tcp-backlog进行调整:

echo 511 > /proc/sys/net/core/somaxconn

Redis 自身优化

1.设置内存上限

使用maxmemory参数限制最大可用内存,当超出内存上限maxmemory时使用LRU等删除策略释放空间以及防止所用内存超过服务器物理内存。

2.配置内存回收策略

Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,Redis支持6种策略,如下所示:

? noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM command not allowed when used memory,此 时Redis只响应读操作。

? volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。

? allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。

? allkeys-random:随机删除所有键,直到腾出足够空间为止。

? volatile-random:随机删除过期键,直到腾出足够空间为止。

? volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果 没有,回退到noeviction策略。

3.键值对优化

降低Redis内存使用最直接的方式就是缩减键(key)和值(value)的长度。在完整描述业务情况下,键值越短越好。值对象缩减比较复杂,应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。

4.共享对象池

共享对象池是指Redis内部维护[0-9999] [0-9999]的整数对象 池,用于节约内存。

但是共享对象池与maxmemory+LRU策略冲突,使用时需要注意。对于ziplist编码的值对象,即使内部数据为整数也无法使用共享对象池,因为ziplist使用压缩且内存连续的结构,对象共享判断成本过高。

5.字符串优化

字符串对象是Redis内部最常用的数据类型。所有的键都是字符串类 型,值对象数据除了整数之外都使用字符串存储。在使用过程中应当尽量优先使用整数,比字符串类型更节省空间。并且要优化字符串使用,避免预分配造成的内存浪费。使用ziplist压缩编码优化hash、list等结构,注重效率和空间的平衡,使用intset编码优化整数集合。使用ziplist编码的hash结构降低小对象链规模。

6.编码优化

Redis对外提供了多种数据类型,但是Redis内部对于不同类型的数据使用的内部编码不一样。内部编码不同将直接影响数据的内存占用和读写效率。

7.控制键的数量

当使用Redis存储大量数据时,通常会存在大量键,过多的键同样会消 耗大量内存。

声明:本文系作者投稿。

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: