在日常工作中,有一些细小问题恰巧是我们忽略的,或者是遇到一些问题不知如何实践,今天我们就探讨下,服务化配置。 我们从持久化配置、慢查询配置、命令和安全配置以及内存配置着手。
持久化配置
持久化配置,在redis中有rdb和aof两种持久化配置,各有利弊,各有开销,那么究竟如何配置才是最佳呢?这需要分情况的。
用来做缓存的redis实例,尽量不要开启持久化配置
因为在缓存没了,可以有其他方式查回来,缓存主要是用于查询的。所以这部分数据持久化可以通过其他方式来保障,比如我们的数据库。
在日常中可以有两种实例,一种是安全性高的,比如分布式锁,比如秒杀库存等等,可以开启持久化。一种是安全性不是那么高的,比如用户信息等等,这种就不要开启持久化浪费性能了。
非要开启,选择RDB还是AOF?
这边建议,是AOF。
因为rdb最大的问题是数据安全性,它的频率并不高,一般来说,rdb一般间隔60s,或者10s等等,这期间出问题了,数据都会丢失。
如果你设置10s以下间隔,那问题更加严重,因为rdb是基于fork的,在开启子线程的时候,主线程也会卡主的,如果你频繁开启,这不是掐着脖子自杀么。而且在数据量大的时候,也会浪费大量的磁盘io。所以不建议频繁的去开启子线程。
而aof呢,它是每秒刷盘的方式去持久化,安全性就得到的保障。虽然它的体积会增大,但是我们有手段去'瘦身',而且磁盘相对于数据,才值几个钱。
但是aof并非只是开启就完事了,aof进行io操作的时候,有一定程序占用cpu资源的,而且达到一定数量会进行bgrewrite,所以要合理设置rewrite阈值,避免频繁bgrewrite。
而且在rewrite的时候禁止aof操作,因为主线程在执行aof的时候,并非是直接落盘的,而是先进入到缓冲区当中,然后再由一个线程同步到磁盘,如果主进程发现上一次同步到磁盘的时间已经大于了2s中,则可以说明数据量比较大,已经阻塞了。 所以可以通过 no-appendfsync-on-rewrite=yes
,来设置,如果正在rewrite的时候不能做aof,虽然数据会丢失,但是性能得到了保障。当然,这也要业务,是追求数据安全,还是性能保障。
那么rdb,还有啥用?
在集群下,使用脚本定期在slave节点做rdb,实现数据备份。
rdb最大的作用感觉就是在集群中的从节点做备份,因为从节点一般是读操作,读操作是不是重新开辟一块缓存的,直接读取主进程共享的区域,所以哪怕在fork的过程中也是没有影响。
开启持久化部署相关
redis实例不要和cpu密集型应用部署在一起。
比如一些应用需要做大量运算,则不建议部署一起的。
不要和高硬盘负载应用一起部署。
比如数据库、mq等等,因为开启持久化,需要大量磁盘io的。
慢查询
如同mysql,redis也是有慢查询的,慢查询:
在redis执行耗时超过某个阈值的命令,称为慢查询。这个命令,可以读或者写。
如:
客户端发送命令到服务redis,redis执行命令。但是我们知道redis是单线程的,如果在执行命令的时间过长,又有新的命令来了如何处理呢?
redis有一个队列来维护这些命令,如果前面的还在执行,后面的就在队列里面排队吧,如果前面那家伙一直在执行,那么redis的主线程就阻塞了,这就是慢查询。
所以要找到他,解决它。
配置
配置慢查询定义
# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
slowlog-log-slower-than 10000
这个是微秒,默认是超过10毫秒的,都是慢查询。一般来说redis来说我们都是微秒级别的。这个值不能再大了,可以再小点。
# There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128
这是个根据配置的慢查询,找到的慢查询数据放到一个日志中,这个日志的长度。因为日志本身是一个队列。
查看慢查询
先插入10w条数据进去,然后执行keys *,肯定是慢查询了。
124.223.47.250_6379:0>slowlog len
"1"
124.223.47.250_6379:0>slowlog get 1
1) 1) "0"
2) "1697427542"
3) "18833"
4) 1) "keys"
2) "*"
5) "113.91.42.174:9995"
6) ""
124.223.47.250_6379:0>slowlog reset
"OK"
124.223.47.250_6379:0>slowlog len
"0"
如上,我们有三个命令:
- slowlog len :查询慢查询日志长度
- slowlog get [n]:读取n条慢查询日志
1):表示日志编号,如上的0,表示第一个。
2):表示的日志加入的时间戳
3):表示慢查询的耗时,单位是毫秒。
4):表示具体哪个命令
5):表示是哪个客户端发起的
解决慢查询
keys *
这种是自带慢查询属性的,一般情况下要禁用它。
其他的按照实际情况来,比如一些bigkey的计算等等,要拆分。
具体业务,具体分析。总之就是,避免慢查询。
命令及安全配置
这篇文章,讲解了如何通过redis来进行服务器攻击,将本机的公钥送到服务器中,从而实现本机连接redis服务器。虽然前提是,可以通过redis来访问服务器。
Redis未授权访问配合SSH key文件利用分析
漏洞是: Redis暴露在公网(即绑定在0.0.0.0:6379,目标IP公网可访问),并且没有开启相关认证和添加相关安全策略情况下可受影响而导致被利用。
安全配置建议
- 配置bind选项,限定可以连接Redis服务器的IP,修改Redis默认端口6379
- 配置认证,也就是AUTH,设置密码,密码会以明文方式保存在Redis配置文件中
- 配置rename-command 配置项 “RENAME_CONFIG”,这样即使存在未授权访问,也能够给攻击者使用config 指令加大难度
- 禁止使用如下命令,可以使用rename-command禁用
- keys
- flushall
- flushdb
- config set
Example:
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 比如把config这个命令设置成 b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG "" #置为空表示这个命令不能用了,禁用
- 开启防火墙
- 不要使用root账户启动redis
内存配置
内存,redis最宝贵的东西,因为内存不是磁盘,内存是有限的。我们向redis写东西,如果内存占满了则无法写入了。当内存不足的时候,所以只能导致key频繁被删除,响应时间变长,qps不稳定等等问题。当内存使用率达到90%以上,就需要警惕,并赶紧定位并解决内存占用的原因。
redis内存类型
数据内存
redis主要的部分,redis存储的键值信息等等都是在这里,常见的问题就是bigkey问题,这就是我们选择的数据结构不对或者是一些业务分配不合适导致的。
或者是内存碎片问题,它是内存分配的过程中产生的,当redis申请内存的时候,比如大小是10个字节,内存可能就会按照2的N次方,分配16个字节。那么其中6个字节就浪费了,这就是内存碎片。但内存碎片重启后就可以恢复了。
进程内存
Redis主进程本身运⾏肯定需要占⽤内存,如代码、常量池等等;这部分内存⼤约⼏兆,在⼤多数⽣产环境中与Redis数据占⽤的内存相⽐可以忽略。
缓冲区内存
一般包括客户端缓冲区、AOF缓冲区、复制缓冲区等。客户端缓冲区又包括输入缓冲区和输出缓冲区两种。这部分内存占用波动较大,不当使用BigKey,可能导致内存溢出。这也是我们需要关注的。
查看相关命令
- info memory
124.223.47.250_6379:0>info memory
"# Memory
used_memory:1194832
used_memory_human:1.14M
used_memory_rss:8482816
used_memory_rss_human:8.09M
used_memory_peak:12253408
....
集群最佳实践
redis集群,确实有非常多的优势,比如自动故障恢复,数据量分片存储大等等,但是也带来很多问题:
完整性问题
比如:默认配置中,有一项要不要保障插槽全覆盖,如果发现任意一个插槽都不可用,则整个集群都会停止对外服务,比如1个集群五个节点,每个节点都有对应的插槽,但一旦有一个插槽有问题,则五个节点都不能用过了。redis可能是出于数据完整性考虑,但我们考虑的是业务可用性。所以这里不建议为yes:
# However sometimes you want the subset of the cluster which is working,
# to continue to accept queries for the part of the key space that is still
# covered. In order to do so, just set the cluster-require-full-coverage
# option to no.
#
cluster-require-full-coverage no
如果说配置了no,如果数据落在了不健康的插槽的节点上面,则会提示connection refused
,那么我们根据错误信息修改即可,但是整个redis是可用的,不会瘫痪。
带宽问题
集群是没有哨兵的,所以每个节点和节点之间,主节点和主节点,主节点和各自的从节点都是通过不断的ping来确定集群中节点的状态,所以每次ping至少携带有插槽信息、集群状态信息等等。
集群越多,携带的信息量越到,如果大于10个节点,每次集群ping需要的代码就会非常高,一般解决的途径是:
- 避免大集群,集群节点数不要太多,不要少于1000,如果业务庞大,则建立多个集群。如果这么高的,要考虑业务拆分,大业务拆小业务,大集群拆小集群。
- 避免在单个物理机中运行太多redis实例,就类似于演示的时候,一台物理机跑了10个实例,都算多了,自己的带宽占用的很多。
- 配置合适的
cluster-node-timeout
值,因为这个值越低,频率就越高,带宽占用越多。
数据倾斜问题
比如出现了bigkey,或者命名hash后一样的时候,都会出现数据倾斜,从而导致部分负担过重,部分又很轻松。
客户端性能
一旦做了集群,则客户端都需要配置所有的节点,然后去做节点的选择,读写分离的判断,插槽的判断等等。
命令的兼容性问题
比如热点数据预热,就不能通过普通的pipeline、mset等等,因为集群要求同一条命令必须落在同一个槽,这就带来一些问题。不能不在客户端,增加一些处理。
事务问题
事务其实就是保证原子性问题,在集群模式下,其实是没办法保障事务的。
总结
目前单体redis,主从redis已经能达到千万级别的QPS,并且也具备很强的高可用特征。根据自身业务,不要过度设计,如果主从能够满足业务需求的情况下,能不用集群,就不用集群吧。