为了让Redis在生产环境中发挥更高的性能,通常我们都会做一些优化配置。
一、优化内存相关参数
这里使用快照还原到单机版redis。先查看下 /etc/sysctl.conf 文件。
注意:编辑 /etc/sysctl.conf 文件,添加 相关参数之后,必须使用 # sysctl -p 使新的参数生效。
什么是 Overcommit 和 OOM?
Linux的内存分配采取的是一种更加积极的分配策略,所以,Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。
当Linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。
当oom-killer发生时,Linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。
1、设置 vm.overcommit_memory=1
编辑 /etc/sysctl.conf文件,在后面添加 vm.overcommit_memory=1
使用 # sysctl -p 使新的参数生效。查看:# sysctl vm.overcommit_memory
overcommit_memory是一个内核对内存分配的一种内存分配策略,可选值:0、1、2。
0(默认值): 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1(允许):表示内核允许分配所有的物理内存,而不管当前的内存状态如何。当然这个也不是无限大,还受到寻址空间的限制,32位系统最大可能只有4G,64位系统大概16T左右。
2(允许但禁止超售):表示内核允许分配超过所有物理内存和交换空间总和的内存。系统能够分配的内存不会超过 swap+实际物理内存*overcommit_ratio,该值可以通过 /proc/sys/vm/overcommit_ratio设置,默认50%。
分析:
在Redis的持久化时,这些数据就会写入硬盘,如果OverCommit=0默认值时,它会检查是否有足够的空闲内存空间来复制父进程的所有内存页,允许轻微的超售,相差太多就会报错(OOM,Out Of Memery),所有我们需要改为1。
2、设置 vm.swappiness=0
编辑 /etc/sysctl.conf文件,在后面添加 vm.swappiness=0
使用 # sysctl -p 使新的参数生效。查看:# sysctl vm.swappiness
使用 free -m 命令可查看内存使用情况和swap的大小:
分析:
swap分区的作用简单理解:
swap 分区通常被称为交换分区,这是一块特殊的硬盘空间,即当实际内存不够用的时候,操作系统会从内存中取出一部分暂时不用的数据,放在交换分区中,从而为当前运行的程序腾出足够的内存空间。也就是说,当内存不够用时,我们使用 swap 分区来临时顶替。这种“拆东墙,补西墙”的方式应用于几乎所有的操作系统中。
使用 swap 交换分区,显著的优点是,通过操作系统的调度,应用程序实际可以使用的内存空间将远远超过系统的物理内存。由于硬盘空间的价格远比 RAM 要低,因此这种方式无疑是经济实惠的。当然,频繁地读写硬盘,会显著降低操作系统的运行速率,这也是使用 swap 交换分区最大的限制。
swappiness的值的大小对如何使用swap分区是有着很大的联系的。
当swappiness为0的时候表示最大限度使用物理内存,然后才是 swap空间,
当swappines为100的时候,则表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。
在 Linux中,可以通过修改swappiness内核参数,降低系统对swap分区的使用,从而提高系统的性能。在CentOS、Red Hat、ubuntu等系统中,swappiness的默认值为60或者30,所以,修改swappiness=0,这样Redis的高速处理能力才能更好的发挥。
3、设置transparent_hugepage=never
Linux7 默认是开启透明大页功能的。
查看transparent_hugepage:# cat /sys/kernel/mm/transparent_hugepage/enabled
编辑 /etc/default/grub 文件,添加 transparent_hugepage=never
执行生效命令 # grub2-mkconfig -o /boot/grub2/grub.cfg
[root@centos7 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-957.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-957.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-81e66b2a70194a61a16b51fd03e4c665
Found initrd image: /boot/initramfs-0-rescue-81e66b2a70194a61a16b51fd03e4c665.img
done
然后重启:# reboot 查看transparent_hugepage,检查是否生效:
分析:
内存有一个最小的存储单位,大多数都是一个字节。内存用内存地址(memory address)来为每个字节的数据顺序编号。因此,内存地址说明了数据在内存中的位置。内存地址从0开始,每次增加1。这种线性增加的存储器地址称为线性地址(linear address)。我们用十六进制数来表示内存地址,比如0x00000003。
内存的一项主要任务,就是存储进程的相关数据。进程空间中主要的结构有:程序段、全局数据、栈和堆,这些存储结构在进程运行中所起到的关键作用。尽管进程和内存的关系如此紧密,但是,进程并不能直接访问内存。在Linux下,进程不能直接读写内存中地址为0x1位置的数据。进程中能访问的地址,只能是虚拟内存地址(virtual memory address)。操作系统会把虚拟内存地址翻译成真实的内存地址。这种内存管理方式,称为虚拟内存(virtual memory)。
每个进程都有自己的一套虚拟内存地址,用来给自己的进程空间编号。进程空间的数据同样以字节为单位,依次增加。从功能上说,虚拟内存地址和物理内存地址类似,都是为数据提供位置索引。进程的虚拟内存地址相互独立。因此,两个进程空间可以有相同的虚拟内存地址,如0x10001000。虚拟内存地址和物理内存地址又有一定的对应关系,对进程某个虚拟内存地址的操作,会被CPU翻译成对某个具体内存地址的操作。虚拟内存地址和物理内存地址的分离,给进程带来便利性和安全性。但是,虚拟内存地址和物理内存地址的翻译,又会额外耗费计算机资源。
在多任务的现代计算机中,虚拟内存地址已经成为必备的设计。那么,操作系统必须要考虑清楚,如何能高效地翻译虚拟内存地址。翻译虚拟内存地址其实就是记录对应关系,最简单的办法,就是把对应关系记录在一张表中。为了让翻译速度足够地快,这个表必须加载在内存中。不过,这种记录方式惊人地浪费内存。因此,Linux采用了分页(paging)的方式来记录对应关系。
分页(paging):就是以更大尺寸的单位页(page)来管理内存。在Linux中,通常每页大小为4KB。
查看页的大小:# getconf PAGE_SIZE
[root@centos7 ~]# getconf PAGE_SIZE
4096
Linux下的页分为两种类型:标准大页(Huge Pages)和透明大页(Transparent Huge Pages)。
1)标准大页(Huge Pages)是从Linux Kernel 2.6后被引入的。
目的是使用更大的内存页面(memory page size) 以适应越来越大的系统内存让操作系统可以支持现代硬件架构的大页面容量功能。
2)透明大页(Transparent Huge Pages)缩写为THP,这个是RHEL 6开始引入的一个功能。
这两者的区别在于大页的分配机制,标准大页管理是预分配的方式,而透明大页管理则是动态分配的方式。
透明大页块(Transparent Huge Pages):可以动态将系统默认内存叶块4kb,交换为Huge pages,在这个过程中,对于操作系统的内存的各种分配活动,需要各种内存锁,直接影响程序的内存访问性能,所以我们建议关掉它。
二、网络上的优化配置
1、设置net.core.somaxconn=65535
编辑 /etc/sysctl.conf文件,在后面添加:net.core.somaxconn=65535
使用 # sysctl -p 使新的参数生效。查看(默认值为128):# sysctl net.core.somaxconn
分析:
简单了解下TCP三次握手中 queue的知识:
第一次: 客户端 - - > 服务器
此时服务器知道了客户端要建立连接了
第二次: 客户端 < - - 服务器
此时客户端知道服务器收到连接请求了
第三次: 客户端 - - > 服务器
此时服务器知道客户端收到了自己的回应
到这里,,就可以认为客户端与服务器已经建立了连接。
在握手阶段存在两个队列:syns queue(半连接队列)和accept queue(全连接队列)。
1)客户端发送SYN给服务端进行第一次报文握手,
此时服务端将此请求信息放在半连接队列中并回复SYN+ACK给客户端。
2)客户端收到SYN+ACK,随即发出ACK给服务端;
此后,服务器收到ACK以后,这里有两种情况,如果:
1. 全队列未满:从半连接队列拿出此消息放入全队列中。
2. 全队列已满:处理方式和tcp_abort_on_overflow(cat /proc/sys/net/ipv4/tcp_abort_on_overflow)有关:
tcp_abort_on_overflow=0;表示丢弃该ACK;
tcp_abort_on_overflow=1;表示发送一个RST(重置标记Reset)给客户端,直接废弃掉这个握手过程。
在Redis配置文件中有一个tcp-backlog,默认值是511, tcp-backlog 就是影响这个accept queue队列的大小的一个配置。
同时,accept queue 队列的大小也受系统内核的 somaxconn 配置项影响,因为服务器最终生效的那个值是取它们两者的最小值。所以,我们必需修改内核的somaxconn值,让它至少大于511。这里把 somaxconn设置为65535,后面我们只需要修改 Redis中的 tcp-backlog 大小即可。
2、设置fs.file-max = 2000000和 ulimit 65535
编辑 /etc/sysctl.conf文件,在后面添加:fs.file-max = 2000000
使用 # sysctl -p 使新的参数生效。查看:# sysctl fs.file-max
ulimit 限制一个用户的所有shell能打开的最大数:
编辑 /etc/security/limits.conf文件,修改 * soft nofile/hard nofile 65535
在生产环境的服务器中,一般不用root启动Redis,肯定会给 Redis配置专有的用户(不设置密码保证安全,客户端不能登录),所以,在生产环境下需要把 * 改成对应的用户名,修改以后,需要重启才能生效。
分析:
操作系统中涉及到的参数有两个:max-file 和 ulimit -n
max-file:表示系统级别的能够打开的文件句柄的数量。是对整个系统的限制,并不是针对用户的。
ulimit -n:控制进程级别能够打开的文件句柄的数量。提供对shell及其启动的进程的可用文件句柄的控制。这是进程级别的。
对于服务器来说,file-max 和 ulimit都需要设置,否则会出现文件描述符耗尽的问题。
查看 max-file:# sysctl fs.file-max
查看 ulimit(Centos7默认是1024):# ulimit -Hn -Sn
# 未修改之前的默认值
[root@centos7 ~]# sysctl fs.file-max
fs.file-max = 95215
[root@centos7 ~]# ulimit -Hn -Sn
open files (-n) 1024
open files (-n) 1024
在Redis配置文件中有一个maxclients 配置项,默认值是10000,这里把操作系统的max-file 和 ulimit -n设置比较大,后面我们只需要修改 Redis中的 maxclients 大小即可。
3、了解与客户端相关的几个重要配置参数
#表示服务器在客户端空闲N秒后关闭链接。 0-表示不关
timeout 0
#设置挂起套接字请求队列大小,系统的somaxconn参数密切相关,tcp-backlog的值大小应该比somaxconn的值小,才有意义。
tcp-backlog 511
#限制了来自客户端的最大链接数,一旦超过了这个配置,客户端会收到报错信息
max-clients 10000
#设置为非0值,服务器会根据配置的时间为间隔,向网络设备发送ack信息,保持网络链接的活跃,如果没收到网络设备的响应,服务器会关闭对应的客户端的链接。
#默认是300
tcp-keepalive 300
例如:Juniper防火墙在客户端和它本身之间的连接空闲超过1800秒时会将连接切断。但是,客户端和服务器端都不会得到连接已断开的通知。因此,
从服务器的角度来看,连接仍然是活跃的。如果我们禁用了tcp-keepalive选项,服务器就不会释放连接。
从客户端的角度来看,一旦它发现连接断开就会重新连接到Redis服务器。这样,会导致连接数持续增加。
所以,为了避免此类连接问题的发生,保持默认的设置更为可取。
硬限制:指对资源节点和数据块的绝对限制,在任何情况下都不允许用户超过这个限制;
软限制:指用户可以在一定时间范围内超过软限制的额度。
1172 # client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
这个三个配置,是Redis服务器对客户端的输出缓冲区的设置参数,有了它们,Redis不会直接把命令的输出给客户端传过去,而是先放缓冲区里,然后一次性的发送过去。具体参数含义如下:
class : 客户端种类,normal、slave、pubsub。
– mormal:普通的客户端
– slave: 从库的复制客户端
– pub/sub: 发布与订阅的客户端的
hard limit:缓冲区大小的硬性限制。
soft limit::缓冲区大小的软性限制。
soft seconds: 缓冲区大小达到了(超过)soft limit 值的持续时间。
client-output-buffer-limit 参数限制分配的缓冲区的大小,防止内存无节制的分配。参数的默认值都为0,意思是不做任何限制。
4、修改内存策略 memory_policy 的值
查看memory 信息: >info memory 信息
关注这几个值:
userd_memory:表示以字节为单位的当前redis分配到的内存空间
maxmemory:表示redis获取的内存的限制,0表示没限制,即redis可使用主机上的所有可能的内存空间
maxmemory_policy:表示当redis的内存空间大小触及maxmemory的时候,所采用的策略,默认为noeviction。
我们把maxmemory的值设置得比userd_memory多一点的,用命令: >config set maxmemory 853450
用set命令Rredis数据库里添加新值,看到有报错 OOM。在添加新值,不报错了,为什么?
这跟 Redis配置文件中设置maxmemory_policy策略值有关。
然后在配置文件中修改maxmemory_policy策略值为: allkeys-lru ,就可以set新值了。
最后可以看到 allkeys-lru 策略,舍弃了最近不使用的key。
maxmemory不建议设置接近物理内存,因为要给其他进程预留一部分空间。
Redis大多数时候是作为缓存来用的,这时候务必要把memory_policy设置为noeviction以外的值。
三、创建普通用作作为启动 Redis的专用用户
在生产环境中,将系统中 root 运行的 Redis服务转为普通用户(不设置密码,不能客户端登录)启动,来提高安全性。
1)新建一个用户组和一个普通用户,名字自定义
# 新建用户组g_redis
[root@centos7 ~]# groupadd g_redis
# 新建用户u_redis并加入g_redis组中
[root@centos7 ~]# useradd u_redis -g g_redis
2)将 Redis 的配置文件复制一份到 u_redis 用户的家目录下
# 强制复制redis的配置文件到u_redis用户的家目录下
[root@centos7 ~]# cp -rf /usr/local/redis/redis.conf /home/u_redis/
3)修改 Redis 配置文件及创建相应的目录
修改 Redis 配置文件
[root@centos7 ~]# vim /home/u_redis/redis.conf
# 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
158 pidfile /home/u_redis/run/redis_6379.pid
# 指定本地数据库存放目录
263 dir /home/u_redis/redis
创建相应的目录
[root@centos7 ~]# mkdir -p /home/u_redis/run
[root@centos7 ~]# mkdir -p /home/u_redis/redis
4)将 u_redis 用户家目录下的所有文件所属者与所属组修改为:u_redis:g_redis
[root@centos7 ~]# ll /home | grep u_redis
drwx------ 4 u_redis g_redis 104 5月 12 22:12 u_redis
[root@centos7 ~]# ll /home/u_redis/
总用量 64
drwxr-xr-x 2 root root 6 5月 12 22:12 redis
-rw-r--r-- 1 root root 61861 5月 12 22:11 redis.conf
drwxr-xr-x 2 root root 6 5月 12 22:12 run
[root@centos7 ~]# chown -R u_redis:g_redis /home/u_redis
[root@centos7 ~]# ll /home/u_redis/
总用量 64
drwxr-xr-x 2 u_redis g_redis 6 5月 12 22:12 redis
-rw-r--r-- 1 u_redis g_redis 61861 5月 12 22:11 redis.conf
drwxr-xr-x 2 u_redis g_redis 6 5月 12 22:12 run
5)修改 Redis 的安装目录权限
[root@centos7 ~]# ll /usr/local/ | grep redis
drwxr-xr-x 4 root root 84 5月 12 11:03 redis
# 变更redis的安装目录权限为u_redis用户所有
[root@centos7 ~]# chown -R u_redis:g_redis /usr/local/redis
[root@centos7 ~]# ll /usr/local/ | grep redis
drwxr-xr-x 4 u_redis g_redis 84 5月 12 11:03 redis
6)切换普通用户 u_redis启动 Redis 服务 ok
[root@centos7 ~]# su u_redis
[u_redis@centos7 root]$ /usr/local/redis/bin/redis-server /home/u_redis/redis.conf
[u_redis@centos7 root]$ ps -ef | grep redis
root 7684 7189 0 22:16 pts/1 00:00:00 su u_redis
u_redis 7685 7684 0 22:16 pts/1 00:00:00 bash
u_redis 7705 1 0 22:17 ? 00:00:00 /usr/local/redis/bin/redis-server 192.168.198.20:6379
u_redis 7709 7685 0 22:17 pts/1 00:00:00 ps -ef
u_redis 7710 7685 0 22:17 pts/1 00:00:00 grep --color=auto redis
参考文章:
—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。