Redis之运维细节

一 Linux配置优化

1.1 内存分配控制

vm.overcommit_memory

Redis在启动的时候,可能会出现这样的日志:


什么是overcommit?Linux对大部分的申请内存的请求回复yes,以便能够运行更多的程序。申请内存后,并不会马上使用内存,这种技术叫做overcommit。如果Redis启动有上述日志说明vm.overcommit_memory=0,Redis提示设置为1.

vm.overcommit_memory用来设置内存分配策略,有三个可选值:

Redis之运维细节_第1张图片

日志中Background save代表就是bgsave和bgrewriteaof,如果当前可用内存不足,操作系统应该如何处理fork操作。如果vm.overcommit_memory=0,代表没有可用内存,就申请内存失败,对应到Redis就是执行fork失败,在Redis日志出现Cannot allocate memory

 

查看这个值:

cat /proc/sys/vm/overcommit_memory

0

设置这个值:

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

sysctl vm.overcommit_memory=1

 

最佳实践:

# 设置合理的maxmemory参数,保证机器有20%-30%的闲置内存

# 设置vm.overcommit_memory=1,防止极端情况下会造成fork失败

 

1.2swappiness

Swappiness对于操作系统来说比较重要,当物理内存不足,可以将一部分内存页进行交换操作,以解决燃煤直接。但是swap是由硬盘提供的,对需要高并发,高吞吐的应用来说,磁盘IO通常会成为系统瓶颈。

在Linux中,并不是要等到所有物理内存都使用完才会使用到swap,参数swappiness会决定操作系统使用swap的倾向程度。取值范围0-100,swappiness值越大,说明操作系统可能使用swap的概率越高,swappiness值越小,说明操作系统可能使用swap的概率越小,更倾向于使用物理内存。

其默认值是60.


Redis之运维细节_第2张图片

设置swappiness:

echo vm.swappiness=值 >> /etc/sysctl.conf

监控swap

# free命令查看内存使用情况:

Redis之运维细节_第3张图片

# vmstat: 查看系统相关性能指标,其中包含负载,CPU,内存,swap,IO先关属性,其中和swap有关的 指标是si 和 so,分别表示swap in 和 swap out

vmstat 秒数

Redis之运维细节_第4张图片

# 查看指定进程的swap使用情况

Linux中/proc/{pid}/存储指定进程相关的信息,其中/proc/{pid}/smaps记录了当前进程所对应内存映像信息。

 

1.3THP(Transparent Huge Pages)

在Redis启动的时候,如果发现有这个警告:


建议修改THP的相关配置,Linux 内核在2.6.38增加了THP的特性支持大内存页2MB的分配,默认开启。

开启的时候,可以降低fork子进程的速度,但fork之后,每一个内存页从原来4KB变为2MB,大幅度增加重写期间父进程的消耗。

同时每次写命令引起的复制内存页单位放大了512倍,会拖慢写操作的执行时间,导致大量写操作慢查询。因此Redis建议将此特性禁用。

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

为了重启THP配置依然有效,可以在/etc/rc.local中追加

vim /etc/rc.local

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

 

1.4OOM Killer

OOM Killer会在可用内存不足的情况下,杀掉用户进程,他会为每一个用户进程设置一个权值,这个权值越高,被杀掉的可能越大,每一个进程的权值存放在/proc/{pid}/oom_score中,这个值是受/proc/{pid}/oom_adj的控制的,当oom_adj设置为最小值时,该进程不会被OOM Killer杀掉:

echo {score} > /proc/{pid}/oom_adj

 

1.5 使用NTP

NTP: Network Time Protocal,网络时间协议,是一种保证不同机器时钟一致性的服务。如果集群或者高可用时间不同步,可能有时候会带来些问题。

yum install ntp -y

修改第一台/etc/ntp.conf

#server 0.centos.pool.ntp.org iburst

#server 1.centos.pool.ntp.org iburst

#server 2.centos.pool.ntp.org iburst

#server 3.centos.pool.ntp.org iburst

 

restrict 192.168.3.0 mask 255.255.255.0 nomodifynotrap

 

server 127.127.1.0     # local clock

fudge  127.127.1.0 stratum 10

 

service ntpd start

chkconfig ntpd on

 

其他机器

crontab -e

00 1 * * * root /usr/sbin/ntpdate 192.168.3.101>> /root/ntpdate.log 2 > &1

 

每一台机器必须开机都启动ntpd服务

chkconfig ntpd on

service ntpd start

 

1.6ulimit

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

Redis允许有多个客户端通过网络进行连接,可以通过配置maxclients来限制最大客户端连接数。

Redis启动的时候,经常可以看到以下日志:


建议把maxclients设置成10032,默认是10000用来处理客户端,而且内部还会使用最多32个文件描述符。

但是Redis进程因为没有权限无法将open files设成10032

当前系统open files是4096,你可以将maxclinet设置成4096-32 = 4064个,如果想设置更高的maxclinets,使用ulimit –n来设置

 

1.7TCP Backlog

默认值是511,如果Linux的tcp-backlog小于Redis设置的tcp-backlog,那么在redis启动的时候会看到如下日志:

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

 

查看方法:cat/proc/sys/net/core/somaxconn

修改它:

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

 

二flushdb/flushall误操作

当使用了flushdb或者flushall会将redis所有数据删除掉。如果有时候误删除了怎么办?

2.1AOF机制恢复

如果Redis启用了AOF机制,那么当你执行完flushall、flushdb的时候,AOF文件会记录你的命令。

这样我们就可以通过编辑AOF文件,然后把错误的命令删除后,然后恢复就可以了

但是有可能你在恢复的时候,会发生AOF重写,意味着会覆盖之前AOF文件,也就是以前的数据也丢失了,利用AOF来恢复就失效了。

所以误操作后,步骤如下:

# 调大AOF重写参数,也不能手动执行bgrewriteaof:

auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size

这样可以避免短时间内的AOF自动重写

config set auto-aof-rewrite-percentage 1000

config set auto-aof-rewrite-min-size 10000000000

# 紧接着打来AOF文件,去掉误操作的命令

# 检验AOF文件格式是否正确

/opt/app/redis/bin/redis-check-aof appendonly.aof

AOF analyzed: size=85545588, ok_up_to=85545588,diff=0

AOF is valid

 

2.2RDB恢复

对于RDB,我们应该区分是否是否开启了自动策略,如果是自动的,可能被重写了,那么就不好恢复了。如果不是自动的,即配置文件没有这样的配置:

save 900 1

save 300 10

save 60 10000

那么以前的数据就还在。只是有可能丢失部分数据。只要这时候有没有执行save或者bgsave这种操作,就可以。

 

所以AOF如果已经开启了,那么AOF来恢复是比较好的方案,但确实AOF没有启用,那也就只能使用RDB.

 

2.3 对于从节点而言,我们应该怎么做呢?

首先主节点如果误操作,首先主节点调大AOF重写参数,然后从节点调大AOF重写参数:

config set auto-aof-rewrite-percentage 1000

config set auto-aof-rewrite-min-size 10000000000

紧接着去掉主从节点flush相关的内容,从AOF文件中移除

*1

$8

Flushall

校验AOF数据

bin/redis-check-aof data/appendonly.aof

最后重新启动主从节点,加载数据

 

三 安全的redis

3.1Redis的密码机制

Redis提供了一个requirepass的配置为Redis提供密码功能,如果这样,客户端如果密码不对就不能执行命令。

客户端有两种提供密码的方式:

# 在redis-cli命令后加上-a参数

bin/redis-cli -h 192.168.3.200 -p 6379 -a 123abcAbc

# 进入redis-cli的交互式模式,敲入auth 密码

192.168.3.200:6379> auth 123abcABC

OK

一般情况下,密码最好最够复杂,64字节以上,房子暴力破解

如果是主从结构,主节点加了requirepass,那么从节点需要加上masterauth(master的requriepass配置)

 

3.2 伪装危险命令

Redis常见的危险命令包括:

keys: 如果键值较多,有可能阻塞Redis

flushall/flushdb: 数据全部被清除

save: 如果键值较多,存在阻塞的可能性

debug: debug reload会重启redis

config: config应该交给管理员用

shutdown: 关闭redis

 

如何重命名呢?

rename-command flushall cleanall

这样的话,你再执行flushall就没有用了

 

但是缺点就是,我们需要对客户端修改,比如jedis.flushall操作内部就是调用的flushall命令;而且rename-command不支持config set;如果AOF和RDB文件包含了rename-command之前的命令,Redis将无法启动

 

所以你一旦要设置,最好在第一次的时候就配置好,而且主从结构,从的配置应该和主保持一致,否则存在主从数据结构不一致的情况。

 

3.3 防火墙

一般来说,比较成熟的公司都会对有外网IP的服务器做端口的限制,例如只允许开放80端口,但是诸如存储服务器之类的无需对外开放。

 

3.4bind

3.5 定期备份数据

3.6 不使用默认端口

3.7 使用非root用户启动

 

四 处理bigkey

bigkey是指key对应的value所占的内存空间比较大,例如一个字符串类型value可以最大存到512M,一个list的value最多可以存储2^32-1各元素,如果按照数据结构来细分的话,可分为一般字符串类型bigkey和非字符串bigkey

字符串类型:一般认为超过10k的就是bigkey,但是这个值和具体的OPS相关

非字符串类型:体现在哈希,列表,集合类型元素过多

 

4.1bigkey的危害

# 内存空间不平衡:

# 超时阻塞:如果key教导,redis又是单线程,操作bigkey比较耗时,那么阻塞redis的可能性增大

# 网络负载较大:每次获取bigKey的网络流量较大,假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB的流量,对于普通千兆网卡,按照字节算128M/S的服务器来说可能扛不住。而且一般服务器采用单机多实例方式来部署,所以还可能对其他实例造成影响。

 

4.2 如何统计有哪些bigkey

我们可以通过命令bin/redis-cli–bigkeys统计bigkey的分布    。判断一个key是否为bigkey,只需要执行debug objectkey查看serializedlength属性,表示序列化之后的字节数。

4.3 删除bigkey
当发现bigkey想删除的时候,对于string类型,一般不存在阻塞,但是对于hash,list,set,zset就不好说了

private void delBigKey(String bigkey) {
    Jedis jedis = JedisUtils.getInstance().getJedis("192.168.3.200",6379);
    String cursor = null;
    while (true) {
        ScanResult<Map.Entry<String,String>> scanResult = jedis.hscan(bigkey, cursor, new ScanParams().count(100));
        // 扫描后获取新的游标
       
cursor = scanResult.getStringCursor();
        // 获取扫描结果
       
List<Map.Entry<String,String>> list = scanResult.getResult();
        if (CollectionUtils.isEmpty(list)) {
            continue;
        }
        String[] fields = getFieldsFrom(list);
        // 删除多个字段
       
jedis.hdel(bigkey,fields);
        // 游标为0为止
       
if (cursor.equals("0")) {
            break;
        }
    }
    jedis.del(bigkey);
}

private String[] getFieldsFrom(List<Map.Entry<String, String>> list) {
    List<String> fields = new ArrayList<String>();
    for(Map.Entry<String,String> entry : list){
        fields.add(entry.getKey());
    }
    return fields.toArray(new String[fields.size()]);
}

 

五 寻找热点key

热门新闻事件或是商品通常会给系统带来巨大的流量,对于存储这类信息的Redis来说却是一个巨大的挑战,以Redis 集群来说,它会造成整体流量的不均衡,个别节点出现OPS过大的情况。因此寻找热点key很重要。

5.1 客户端

在客户端设置全局字典(key和调用次数),每次调用Redis命令时,使用这个字典进行记录。

 

5.2 代理端

诸如Twemproxy和Codis都是基于代理的Redis分布式架构,所有客户端的请求都是通过代理端完成的,最适合做热点key统计

5.3Redis 服务端

使用monitor命令统计热点key,facebook开源的 redis-faina就是用python实现的一个工具:

redis-cli -h 192.168.3.200 -p 6379 -a 123abcABC monitor|head -n 10000| ./redis-faina.py

但是该方法有以下问题:

#monitor在高并发场景下,会存在内存暴增的和影响Redis的性能的隐患

# 只适合统计单机,集群的话需要会中统计

 

Redis之运维细节_第5张图片

六 配置

6.1 info

# info all: 查看所有

# info Server: 查看服务器信息

# info Clients: 查看客户端信息

# info Memory: 查看内存信息

# info Persistence: 查看持久化信息

# info Stats:查看统计信息

# info Replication: 查看复制信息

# info CPU: 查看CPU情况

# info Commandstats:查看命令统计信息

# info Cluster: 查看集群信息

# info Keyspace: 数据库key统计信息


转载自Redis开发与运维



你可能感兴趣的:(NoSQL数据库/redis)