数据库之Redis运维手册(私人整理)

● 前言

Redis(Remote Dictionary Server )即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,是当下最热门的 NoSQL 技术之一。Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。Redis可以用作数据库、缓存和消息中间 件MQ。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合 (sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间 (geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU 驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis应用场景:

内存存储,持久化,内存中是断电既失,所以持久化很重要(rdb,aof)
效率高,可以用于高速缓存
发布订阅系统
地图信息分析
计时器,计数器(浏览量)

Redis以快速著称,因它是基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈是根据 机器的内存和网络带宽来动态调节的,某些情况下可以单线程就满足业务需要,Reis所有的数据全部放在内存中的,所以说使用单线程去操作效率就是高的,避免了多线程的上下文切换开销。

官网:https://redis.io/
中文网:http://www.redis.cn/,http://www.redis.cn/commands.html

一、Redis架构

1)Redis 常用高可用架构有哪些:

● Redis Sentinel 集群 + 内网 DNS + 自定义脚本

优点缺点
秒级切换
脚本自定义,架构可控
对应用透明
维护成本略高
依赖 DNS,存在解析延时
Sentinel 模式存在短时间的服务不可用

●Redis Sentinel (哨兵)集群 + VIP + 自定义脚本
在这里插入图片描述
Sentinel(哨兵) 是用于监控redis集群中Master状态的工具,sentinel哨兵模式已经被集成在redis2.4之后的版本中。ntinel系统可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务下线时,自动将该master下的某个从服务升级为master服务替代已下线的master服务继续处理请求。

Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。Sentinel由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。

优点缺点
秒级切换
脚本自定义,架构可控
对应用透明
维护成本略高
Sentinel 模式存在短时间的服务不可用

●封装客户端直连 Redis Sentinel (哨兵)端口

优点缺点
服务探测故障及时
DBA 维护成本低
依赖客户端支持 Sentinel
Sentinel 服务器需要开放访问权限l
对应用有侵入性

●JedisSentinelPool,适合 Java

●PHP 基于 phpredis 自行封装

●Redis Sentinel 集群 + Keepalived/Haproxy

优点缺点
秒级切换
对应用透明
维护成本高
维护成本高
Sentinel 模式存在短时间的服务不可用

●Redis M/S + Keepalived

优点缺点
秒级切换
对应用透明
部署简单,维护成本低
需要脚本实现切换功能
存在脑裂

●Redis Cluster
在这里插入图片描述
P2P模式,无中心化。把 key 分成 16384 个 slot,每个实例负责一部分 slot。客户端请求若不在连接的实例,该实例会转发给对应的实例。通过Gossip协议同步节点信息。

优点缺点
组件 all-in-box,部署简单,节约机器资源
性能比 proxy 模式好
自动故障转移、Slot 迁移中数据可用
官方原生集群方案,更新与支持有保障
架构比较新,最佳实践较少
多键操作支持有限(驱动可以曲线救国)
为了性能提升,客户端需要缓存路由表信息
节点发现、reshard 操作不够自动化

示例:
在这里插入图片描述

●Twemproxy
在这里插入图片描述
该架构中,多个同构 Twemproxy(配置相同)同时工作,接受客户端的请求,根据 hash 算法,转发给对应的 Redis。

Twemproxytwo-em-proxy(双EM代理)是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。该方案很好的解决了单个Redis实例承载能力的问题。当然,Twemproxy本身也是单点,需要用Keepalived做高可用方案。通过Twemproxy可以使用多台服务器来水平扩张redis服务,可以有效的避免单点故障问题。虽然使用Twemproxy需要更多的硬件资源和在redis性能有一定的损失(twitter测试约20%),但是能够提高整个系统的HA。另,twemproxy实现了完整的 memcached ascii 和 redis 协议,不光可以代理redis,还可以代理memcached(官方解释:is a fast and lightweight proxy for memcachedand redis protocol)。

优点缺点
开发简单,对应用几乎透明
解决方案成熟
代理影响性能
LVS 和 Twemproxy 会有节点性能瓶颈
Redis 扩容非常麻烦
Twitter 内部已放弃使用该方案,新使用的架构未开源

●Codis

在这里插入图片描述

组件功用
ZooKeeper存放路由表和代理节点元数据
分发Codis-Config的命令
Codis-Config集成管理工具,有web界面
Codis-Proxy无状态代理,兼容Redis协议
对业务透明、
Codis-Redis基于2.8版本二次开发
加入slot支持和迁移命令
优点缺点
开发简单,对应用几乎透明
性能比 Twemproxy 好
有图形化界面,扩容容易,运维方便
代理影响性能
组件过多,需占用较多主机资源
修改了 Redis 代码,导致和官方无法同步,新特性跟进缓慢
开发团队准备主推基于 Redis 改造的 reborndb

2)Redis 常用应用场景

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件.。它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication), LUA脚本(Lua scripting), LRU驱动事件(LRU eviction), 事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel) 和自动 分区(Cluster)提供高可用性(high availability)。参见Redis指导。

Redis 作为一个高性能的缓存,一般应用在 Session 缓存、队列、排行榜、计数器、最近最热文章、最近最热评论、发布订阅等。Redis 适用于数据实时性要求高、数据存储有过期和淘汰特征的、不需要持久化或者只需要保证弱一致性、逻辑简单。

3)Redis故障排查思路

1》常见的运维故障

使用 keys * 把库堵死,——建议使用别名把这个命令改名
超过内存使用后,部分数据被删除——这个有删除策略的,选择适合自己的即可
没开持久化,却重启了实例,数据全掉——记得非缓存的信息需要打开持久化
RDB 的持久化需要 vm.overcommit_memory=1,否则会持久化失败
没有持久化情况下,主从,主重启太快,从还没认为主挂的情况下,从会清空自己的数据——人为重启主节点前,先关闭从节点的同步

2》Redis 故障的排查思路:

了解清楚业务数据流是怎么样的
结合 Redis 监控查看 QPS、缓存命中率、内存使用率等信息
确认机器层面的资源是否有异常
故障时及时上机,使用 redis-cli monitor 打印出操作日志,然后分析(事后分析此条失效)
和研发沟通,确认是否有大 Key 在堵塞(大 Key 也可以在日常的巡检中获得)
和组内同事沟通,确实是否有误操作
和运维同事、研发一起排查流量是否正常,是否存在被刷的情况

二、Redis维护

1、redis配置文件

cat 8000.conf

daemonize no
#[yes/no]默认值no,该参数用于定制redis服务是否以守护模式运行。

pidfile /var/run/redis.pid
#默认值/var/run/redis.pid,指定redis服务的进程号文件路径,以守护模式运行时需要配置本参数;  

port 8000
#默认值6379,指定redis服务的端口  

# bind 127.0.0.1  ::1
# bind 192.168.1.100 10.0.0.1
#绑定ip,默认是本机所有网络设备;如需开启在配置文件中添加此配置即可 
bind 0.0.0.0

tcp-backlog 511
#此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, TCP listen() backlog. 虽然listen有两个参数int listen(int s, int backlog); 但是第二个参数会被/proc/sys/net/core/somaxconn覆盖。比如nginx设置的是511,但是也会被这个覆盖成默认的128,所以要/etc/sysctl.conf中添加net.core.somaxconn = 2048 然后 sysctl -p ,就是说,如果软件设置大于linux配置,就是linux配置,软件设置小于linux,就用软件的,就是用最小的那个。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。

timeout 0
#客户端空闲n秒后断开连接;默认是 0 表示不断开。 

tcp-keepalive 0
#如果值非0,单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是60S。

#Linux内核里,值以秒记,相当于tcp_keepalive_time,要用两倍的这个时间才能杀死(画外音,也就是probes*intvl=如下的值了,详见EverNote搜索“linux 在线服务器优化配置”)

loglevel notice
###设置服务端的日志级别,有下列几种选择:  

#debug        记录详细信息,用于开发或调试;  

#verbose    提供很多有用的信息,但是又不像debug那么详尽,默认就是这一选项;  

#notice     适度提醒,多用于产品环境;  

#warning    仅显示重要的警告信息;  

logfile ""
#指定日志的输出路径,默认值stdout,表示输出到屏幕,守护模式时则输出到/dev/null;如果要输出日志到syslog中,可以启动syslog-enabled yes,默认该选项值为no。  

# syslog-enabled no 
#是否把log记到系统日志里。

# syslog-ident redis 
#标示是什么?

databases 16
#指定数据库的数量,默认为16个,默认使用的数据库是DB 0。

								###########RDB相关###########

#save <seconds> <changes>  ##指定多长时间刷新快照至磁盘,这个选项有两个属性值,只有当两个属性值均满足时才会触发;可以设置多种级别

save 900 1
#每900秒(15分钟)至少一次键值变更时被触发; 

save 300 10
#每300秒(5分钟)至少10次键值变更时被触发;

save 60 10000
#每60秒至少10000次键值变更时被触发;  

stop-writes-on-bgsave-error yes
#当持久化出现错误之后,是否继续提供写服务,这样也相当于一个报警。等后台保存继续工作后,redis就允许写了。

rdbcompression yes
#默认值yes,当dump数据库时使用LZF压缩字符串对象,节省磁盘消耗CPU,如果CPU资源比较紧张,可以设置为no,选择不压缩;  

rdbchecksum yes
# 是否校验rdb快照?CRC64校验值会放在文件尾部。会导致10%性能下降。关闭后,校验值用0填充

dbfilename dump.rdb   
#使用rdb持久化时保存文件名称

dir ./
#rdb持久化文件路径

													###########Redis cluster配置###########
cluster-enabled yes
如果配置yes则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例。

cluster-config-file nodes-6379.conf
虽然此配置的名字叫"集群配置文件",但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
 
cluster-node-timeout 15000
这是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。注意,任何一个节点在这个时间之内如果还是没有连上大部分的主节点,则此节点将停止接收任何请求。一般设置为15秒即可。

cluster-slave-validity-factor 10
如果设置成0,则无论从节点与主节点失联多久,从节点都会尝试升级成主节点。如果设置成正数,则cluster-node-timeout乘以cluster-slave-validity-factor得到的时间,是从节点与主节点失联后,此从节点数据有效的最长时间,超过这个时间,从节点不会启动故障迁移。假设cluster-node-timeout=5,cluster-slave-validity-factor=10,则如果从节点跟主节点失联超过50秒,此从节点不能成为主节点。注意,如果此参数配置为非0,将可能出现由于某主节点失联却没有从节点能顶上的情况,从而导致集群不能正常工作,在这种情况下,只有等到原来的主节点重新回归到集群,集群才恢复运作。

cluster-migration-barrier 1
主节点需要的最小从节点数,只有满足这个最小数限制,主节点失败时,它从节点才会进行迁移。

cluster-require-full-coverage yes
在部分key所在的节点不可用时,如果此参数设置为"yes"(默认值), 则整个集群停止接受操作;如果此参数设置为”no”,则集群依然为可达节点上的key提供读操作。


												###########主从相关###########

# 主从复制。用slaveof去复制另一份redis。
# 1)redis复制是异步的。当少于某个个数的从redis在线,可以让主redis拒绝写请求,如当从个数为1禁止写入。
# 2)如果复制进程暂停了一小会儿,slave可以进行重新进行部分同步,你可以设置一下复制backlog大小
# 3)是自动的,无需干预。

# slaveof <masterip> <masterport>  
#指定主端ip和端口,用于创建一个镜像服务  

# masterauth <master-password>  
#如果master配置了密码的话,此处也需做设置;  

slave-serve-stale-data yes
#默认值yes。当slave丢失与master端的连接,或者复制仍在处理,那么slave会有下列两种表现:  

#当本参数值为yes时,slave为继续响应客户端请求返回老数据,尽管数据已不同步甚至没有数据(出现在初次同步的情况下);  

#当本参数值为no时,slave会返回"SYNC with master in progreee"的错误信息INFO和SLAVEOF命令除外!;  

slave-read-only yes
#默认从Redis是只读模式2.6之后,redis默认slave都是read-only的,但是slave默认可以执行所有管理员命令。CONFIG,DEBUG等。可以用rename-command去重命名危险的命令,隐藏他们。

slave-read-only yes

# repl-ping-slave-period 10  ###默认值10,指定slave定期ping master的周期;  

# repl-timeout 60  ##默认值60,指定超时时间。注意本参数包括批量传输数据和ping响应的时间。


# 复制集同步策略:磁盘或者socket

# 新slave连接或者老slave重新连接时候不能只接收不同,得做一个全同步。需要一个新的RDB文件dump出来,然后从master传到slave。可以有两种情况:

# 1)基于硬盘(disk-backed):master创建一个新进程dump RDB,完事儿之后由父进程(即主进程)增量传给slaves。

# 2)基于socket(diskless):master创建一个新进程直接dump RDB到slave的socket,不经过主进程,不经过硬盘。

# 基于硬盘的话,RDB文件创建后,一旦创建完毕,可以同时服务更多的slave。基于socket的话, 新slave来了后,得排队(如果超出了repl-diskless-sync-delay还没来),完事儿一个再进行下一个。

repl-diskless-sync no
# disk较慢,并且网络较快的时候,可以用diskless。(默认用disk-based)

repl-diskless-sync-delay 5
#设置成0的话,传输开始ASAP

repl-disable-tcp-nodelay no
# SYNC完毕后,在slave的socket里关闭TCP_NODELAY。
# 如果是yes,reids发送少量的TCP包给slave,但可能导致最高40ms的数据延迟。
# 如果是no,那可能在复制的时候,会消耗 少量带宽。
# 默认我们是为了低延迟优化而设置成no,如果主从之间有很多网络跳跃。那设置成yes。

# repl-ping-slave-period 10    #Slave发送ping给master。默认10s

# repl-timeout 60
# 超时时间,包括从master看slave,从slave看master,要大于上边的repl-ping-slave-period

# repl-backlog-size 1mb
# 复制集后台backlog大小,越大,slave可以丢失的时间就越长。

 

# repl-backlog-ttl 3600
# 多久释放backlog,当确认master不再需要slave的时候,多久释放。0是永远不释放。

 slave-priority 100
#指定slave的优先级。在不只1个slave存在的部署环境下,当master宕机时,Redis Sentinel会将priority值最小的slave提升为master。需要注意的是,若该配置项为0,则对应的slave永远不会自动提升为master。

# min-slaves-to-write 3
# min-slaves-max-lag 10
# slave小于几个,网络lag大于几秒的时候,master停止接受write请求。默认对slave数目无限制,给0。网络延迟给10s

 								###########安全相关###########

# requirepass foobared
#指定一个密码,客户端连接时也需要通过密码才能成功连接;  
# 多数情况下无需密码鉴别slave。同时,由于redis处理速度太快,所以爆破速率可达150K/S。10万/S。所以如果你要设置密码,必须设置超强的密码。

 # 在一个shared环境里,可以对危险的命令,比如CONFIG,进行重命名:也可以用空字符串,达到完全屏蔽此命令的目的。
# 记录进AOF或者传给slave的重命名操作可能会引发问题。 ---AOF相对RDB的问题

 # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52  ##
#重定义命令,例如将CONFIG命令更名为一个很复杂的名字:  

# rename-command CONFIG ""  取消这个命令;   

							###########资源相关###########

# maxclients 10000  
##设置最大client连接数。默认10000一万个。如果redis没法控制最大文件数。则给到最低32。设置该参数值为0也表示不限制,直到redis无法创建新的进程为止,如果该参数指定了值,当并发连接达到指定值时,redis会关闭所有新连接,并返回'max number of clients reached'的错误信息;

# maxmemory <bytes>  
###设置redis最大可使用内存。当达到最大内存后,redis会尝试按照设置的回收策略删除键值。如果无法删除键值,或者保留策略设置为不清除,那么redis就会向发出内存的请求返回错误信息。当把redis做为一级LRU的缓存时本参数较为有用。  

#如果redis用内存超过了设置的限制,开始用maxmemory-policy配置的策略往外删数据,如果配置成了noeviction。所有write都会拒绝,比如set,lpush等。所有读请求可以接受。

# 主要用在把redis用在LRU缓存,或者用在一个内存吃紧又不能删除的策略上。

# 如果你有slave,你应该把最大内存别设置的太大,留一些系统内存给slave output buffers(如果是noeviction策略,就无需这样设置了)

# maxmemory-policy volatile-lru  
###默认值volatile-lru,指定清除策略,有下列几种方法:  

#volatile-lru -> remove the key with an expire set using an LRU algorithm  

#allkeys-lru -> remove any key accordingly to the LRU algorithm  

#volatile-random -> remove a random key with an expire set  

#allkeys->random -> remove a random key, any key  

#volatile-ttl -> remove the key with the nearest expire time (minor TTL)  

#noeviction -> don't expire at all, just return an error on write operations  

									#### 内存策略。######

# volatile-lru ->用LRU删除设置了ttl的key

# allkeys-lru ->用LRU删除任何key

# volatile-random ->随机删除有ttl的key

# allkeys-random ->随机删除任何key

# volatile-ttl ->删除即将ttl到期的key

# noeviction ->不删,有write的时候报错。

# 如下操作会返回错误

#       set setnx setex append

#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd

#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby

#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby

#       getset mset msetnx exec sort

# 默认是

# maxmemory-policy volatile-lru
# maxmemory-samples 3    
#默认值3,LRU和最小TTL策略并非严谨的策略,而是大约估算的方式,因此可以选择取样值以便检查。  
#LRU和最小TTL并不是最精确的,但是差不多了也。默认redis每次取3个key然后取最符合删除策略的删除。你可以配置这个数。越低,删除的东西就会越多。比如设置100个,就能删百分之一。

											###########APPEND相关###########

#ONLY模式的设置,默认情况下redis采用异步方式dump数据到磁盘上,极端情况下这可能会导致丢失部分数据(比如服务器突然宕机),如果数据比较重要,不希望丢失,可以启用直写的模式,这种模式下redis会将所有接收到的写操作同步到appendonly.aof文件中,该文件会在redis服务启动时在内存中重建所有数据。注意这种模式对性能影响非常之大。  

appendonly no
#默认值no,指定是否启用直写模式; 

appendfilename "appendonly.aof"
#直写模式的默认文件名appendonly.aof 

appendfsync everysec
##调用fsync()方式让操作系统写数据到磁盘上,数据同步方式,有下列几种模式:  

#always:每次都调用,比如安全,但速度最慢;  
#everysec:每秒同步,这也是默认方式;  
#no:不调用fsync,由操作系统决定何时同步,比如快的模式;  

no-appendfsync-on-rewrite no
#默认值no。当AOF fsync策略设置为always或everysec,后台保存进程会执行大量的I/O操作。某些linux配置下redis可能会阻塞过多的fsync()调用。

 

auto-aof-rewrite-percentage 100
#指定Redis重写aof文件的条件,默认为100,表示与上次rewrite的aof文件大小相比,当前aof文件增长量超过上次afo文件大小的100%时,就会触发background rewrite。若配置为0,则会禁用自动rewrite

auto-aof-rewrite-min-size 64mb
#指定触发rewrite的aof文件大小。若aof文件小于该值,即使当前文件的增量比例达到auto-aof-rewrite-percentage的配置值,也不会触发自动rewrite。即这两个配置项同时满足时,才会触发rewrite。

#过程:redis记住最后一次rewrite时aof文件大小(重启后没rewrite的话,就是启动时AOF文件的大小),如果现在AOF大小和上次的比例达到特定值就重写。也要指定最小AOF大小,防止到2倍:1M的时候也重写。

# 把percentage改成0,就是禁用重写

aof-load-truncated yes
# AOF文件可能在尾部是不完整的(上次system关闭有问题,尤其是mount ext4文件系统时没有加上data=ordered选项。只会发生在os死时,redis自己死不会不完整)。那redis重启时load进内存的时候就有问题了。

# 发生的时候,可以选择redis启动报错,或者load尽量多正常的数据。

# 如果aof-load-truncated是yes,会自动发布一个log给客户端然后load(默认)。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。

 

###########进阶配置###########

lua-time-limit 5000

#一个Lua脚本最长的执行时间,单位为毫秒,如果为0或负数表示无限执行时间,默认为5000

# 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。

# 当一个脚本超过了最大时限。只有SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了write,只能用第二个命令杀。

# 设置成0或者负值,时限就无限。

###########SHOW LOG###########

# 线程阻塞不能服务其他请求的时间长度。两个参数:第一个是时长(以微秒为单位!,是毫秒的千分之一。)。第二个是log的size,超过了,就会删除之前的log。

 slowlog-log-slower-than 10000
# 1000000是一秒。负值是所有请求都记log!下边是0.10S。100毫秒。

slowlog-max-len 128
# log长度的设置值是没限制。但是需要内存。

latency-monitor-threshold 0
# 用LATENCY打印redis实例在跑命令时的耗时图表。
# 只记录大于等于下边设置的值的操作。0的话,就是关闭监视。可以动态开启。直接运行CONFIG SET latency-monitor-threshold <milliseconds>

 notify-keyspace-events ""
# 可以通知pub/sub客户端关于key空间的变化。http://redis.io/topics/notifications
# 比如如果开着开关。一个client进行了DEL操作在“foo”key上在database0上。两个消息将会发布通过 pub/sub

# PUBLISH __keyspace@0__:foo del
# PUBLISH __keyevent@0__:del foo
# 大部分人不需要这个功能,并且还需要一定开销,所以默认关闭。

hash-max-ziplist-entries 512
#默认值512,当某个map的元素个数达到最大值,但是其中最大元素的长度没有达到设定阀值时,其HASH的编码采用一种特殊的方式(更有效利用内存)。本参数与下面的参数组合使用来设置这两项阀值。设置元素个数;  

hash-max-ziplist-value 64
#默认值64,设置map中元素的值的最大长度;

 list-max-ziplist-entries 512
#默认值512,与hash类似,满足条件的list数组也会采用特殊的方式以节省空间。  

list-max-ziplist-value 64
#默认值64 

set-max-intset-entries 512
#默认值512,当set类型中的数据都是数值类型,并且set中整型元素的数量不超过指定值时,使用特殊的编码方式;

 

zset-max-ziplist-entries 128
#默认值128,与hash类似

zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
#默认值yes,用来控制是否自动重建hash。Active rehashing每100微秒使用1微秒cpu时间排序,以重组Redis的hash表。重建是通过一种lazy方式,写入hash表的操作越多,需要执行rehashing的步骤也越多,如果服务器当前空闲,那么rehashing操作会一直执行。如果对实时性要求较高,难以接受redis时不时出现的2微秒的延迟,则可以设置activerehashing为no,否则建议设置为yes,以节省内存空间。  

 

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# client output buffer限制,可以用来强制关闭传输缓慢的客户端(比如redis pub的东西有比较慢的client无法及时sub)
# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

# class可以为以下:

# normal -> normal clients including MONITOR clients

# slave  -> slave clients

# pubsub -> clients subscribed to at least one pubsub channel or pattern

# 当hard限制到了会立即被关闭客户端。如果soft限制到了,会等soft秒。

# 比如硬限制是32m,soft是16m,10secs。到32m就立即断,或者在16m以上停止了10secs。

# 设置成0就是关闭。

hz 10
# redis内部调度(进行关闭timeout的客户端,删除过期key等等)频率,越大则调度频率越高。设置成100以上会对CPU造成大压力除非你对线上实时性要求很高。可以在1~500之间。

aof-rewrite-incremental-fsync yes
# 当child进程在rewrite AOF文件,如果这个选项是yes,那么这个file每32MB会写fsync()。这个是保证增量写硬盘而防止写硬盘时I/O突增。

配置建议:
1name=port ##满足可唯一标示redis实例的名称

2.pidfile /var/run/${name}.pid

3.port 6379 #不能重复

4.logfile /opt/logs/redis/${name}/stdout.log

5.dir /opt/workspace/redis/${name}

2、redis cluster命令

2.1、登录redis且查看集群状态:

在redis服务器上执行:redis-cli -c -h 127.0.0.1 -p 8000 #-c:开启reidis cluster模式,连接redis cluster节点时候使用。

127.0.0.1:8000> cluster info   		 ##查看集群的信息
cluster_state:ok
cluster_slots_assigned:16384     	 #已分配的槽
cluster_slots_ok:16384               #槽的状态是ok的数目
cluster_slots_pfail:0                #可能失效的槽的数目
cluster_slots_fail:0                 #已经失效的槽的数目
cluster_known_nodes:10           	 #集群中节点个数
cluster_size:5                       #集群中设置的分片个数
cluster_current_epoch:14         	 ##集群中的currentEpoch总是一致的,currentEpoch越高,代表节点的配置或者操作越新,改值表集群中最大的那个node epoch。
cluster_my_epoch:13               		#当前节点的config epoch,每个主节点都不同,一直递增, 其表示某节点最后一次变成主节点或获取新slot所有权的逻辑时间.
cluster_stats_messages_sent:6878
cluster_stats_messages_received:4298
127.0.0.1:8000> exit
2.2 检查Redis集群Node及主从情况

127.0.0.1:8000> cluster nodes

在这里插入图片描述
在这里插入图片描述
上图说明:

第一列:为节点ID,94dd7078970bfb730a03503c9867973cd4ecb2dd
第二列:节点的ip地址和端口号;
第三列:节点的角色(master,slave,myself)以及状态(pfail,fail),如果节点是一个从节点的话,那么跟在flags之后的将是主节点的节点ID;如果是mater节点的话,后跟0;
第四列:集群最近一次向节点发送ping命令之后,一直没收到回复的持续时间;
第五列:节点最近一次返回pong回复的时间;slaver常为0;
第六列:节点的配置纪元(config epoch)
第七列:本节点的网络连接情况
第八列:master节点目前包含的槽(slot的编号)

其他集群命令:

cluster meet <ip> <port>         将ip和port所指定的节点添加到集群当中,让它成为集群的一份子  
cluster forget <node_id>         从集群中移除node_id指定的节点
cluster replicate <node_id>    将当前节点设置为node_id指定的节点的从节点
cluster saveconfig                   将节点的配置文件保存到硬盘里面
cluster slaves <node_id>       列出该slave节点的master节点
cluster set-config-epoch         强制设置configEpoch 槽(slot)  
cluster addslots <slot> [slot ...]    将一个或多个槽(slot)指派(assign)给当前节点
cluster delslots <slot> [slot ...]     移除一个或多个槽对当前节点的指派 
cluster flushslots             移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点 
cluster setslot <slot> node <node_id>            将槽slot指派给node_id指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派 
cluster setslot <slot> migrating <node_id>      将本节点的槽slot迁移到node_id指定的节点中  
cluster setslot <slot> importing <node_id>      从node_id 指定的节点中导入槽slot到本节点 
cluster setslot <slot> stable                      取消对槽slot的导入(import)或者迁移(migrate) 键(key)  
cluster keyslot <key>                                       计算键key应该被放置在哪个槽上  
cluster countkeysinslot <slot>                         返回槽slot目前包含的键值对数量 
cluster getkeysinslot <slot> <count>              返回count个slot槽中的键
cluster myid       返回节点的ID
cluster slots       返回节点负责的slot
cluster reset      重置集群,慎用
2.3 Redis缓存清理

进入Redis根目录: /usr/local/redis/src下找到redis-cli,执行:

redis-cli -c -h 127.0.0.1 -p redis实例端口

127.0.0.1:8000>dbsize
(integer) 10
127.0.0.1:8000>flushall
127.0.0.1:8000>exit
127.0.0.1:8000>keys *
 1) "validCode:7d30500148a29011232ec33a1b15afc21"
 2) "RSMS_TOKEN:3231"
 3) "RSMS_ONE"
 4) "RSMS_TOKEN:3c0f879760dd72f491d9a46932774a21"
 5) "RSMS_TOKEN:cd692de2ee9217c012195ee5954aaa35"
 6) "RSMS_TOKEN:2a53321464569ae9306a44875e52adf3"
 7) "RSMS_TOKEN:8458768e753bee6e7f5ebf93fc967229"
 8) "RSMS_TOKEN:16311"
 9) "RSMS_TOKEN:278d18d6f83810fbbf622842d3bb7430"
10) "RSMS_TOKEN:d39fd0655f333f8e432e90a295c26699"
127.0.0.1:8000>del key "RSMS_ONE"
2.4 Redis启动/停止

进入redis根目录: /usr/local/redis/src下找到redis-server,执行:

./redis-server redis.conf  					##后面可跟你指定的任何配置文件
./redis-server (run the server with default conf)
./redis-server /etc/redis/6379.conf  ##指定的的配置
./redis-server --port 7777               ##指定的端口
./redis-server --port 7777 --slaveof 127.0.0.1 8888    ##指定同步的slaver
./redis-server /etc/myredis.conf --loglevel verbose     ##指定日志等级

./redis-cli shutdown   ##关停redis
2.5 探测redis服务是否可用

127.0.0.1:6379> ping

返回PONG说明正常

2.6 监控redis请求执行信息

127.0.0.1:8000> monitor ##生产环境慎用
OK
在这里插入图片描述

2.7 查看redis数据库统计信息
127.0.0.1:6379> info			#显示如下
# Server

redis_version:2.8.19 ###redis版本号

redis_git_sha1:00000000 ###git SHA1

redis_git_dirty:0 ###git dirty flag

redis_build_id:78796c63e58b72dc

redis_mode:standalone ###redis运行模式

os:Linux 2.6.32-431.el6.x86_64 x86_64 ###os版本号

arch_bits:64 ###64位架构

multiplexing_api:epoll ###调用epoll算法

gcc_version:4.4.7 ###gcc版本号

process_id:25899 ###服务器进程PID

run_id:eae356ac1098c13b68f2b00fd7e1c9f93b1c6a2c ###Redis的随机标识符(用于sentinel和集群)

tcp_port:6379 ###Redis监听的端口号

uptime_in_seconds:6419 ###Redis运行时长(s为单位)

uptime_in_days:0 ###Redis运行时长(天为单位)

hz:10

lru_clock:10737922 ###以分钟为单位的自增时钟,用于LRU管理

config_file:/etc/redis/redis.conf ###redis配置文件

# Clients

connected_clients:1 ###已连接客户端的数量(不包括通过从属服务器连接的客户端)

client_longest_output_list:0 ###当前连接的客户端中最长的输出列表

client_biggest_input_buf:0 ###当前连接的客户端中最大的输出缓存

blocked_clients:0 ###正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量 需监控

# Memory

used_memory:2281560 ###由 Redis 分配器分配的内存总量,以字节(byte)为单位

used_memory_human:2.18M ###以更友好的格式输出redis占用的内存

used_memory_rss:2699264 ###从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致

used_memory_peak:22141272 ### Redis 的内存消耗峰值(以字节为单位)

used_memory_peak_human:21.12M ###以更友好的格式输出redis峰值内存占用

used_memory_lua:35840 ###LUA引擎所使用的内存大小

mem_fragmentation_ratio:1.18 ###used_memory_rss 和 used_memory 之间的比率

mem_allocator:jemalloc-3.6.0

###在理想情况下, used_memory_rss 的值应该只比 used_memory 稍微高一点儿。当 rss > used ,且两者的值相差较大时,表示存在(内部或外部的)内存碎片。内存碎片的比率可以通过 mem_fragmentation_ratio 的值看出。

当 used > rss 时,表示 Redis 的部分内存被操作系统换出到交换空间了,在这种情况下,操作可能会产生明显的延迟。

# Persistence

loading:0 ###记录服务器是否正在载入持久化文件

rdb_changes_since_last_save:0 ###距离最近一次成功创建持久化文件之后,经过了多少秒

rdb_bgsave_in_progress:0 ###记录了服务器是否正在创建 RDB 文件

rdb_last_save_time:1420023749 ###最近一次成功创建 RDB 文件的 UNIX 时间戳

rdb_last_bgsave_status:ok ###最近一次创建 RDB 文件的结果是成功还是失败

rdb_last_bgsave_time_sec:0 ###最近一次创建 RDB 文件耗费的秒数

rdb_current_bgsave_time_sec:-1 ###如果服务器正在创建 RDB 文件,那么这个域记录的就是当前的创建操作已经耗费的秒数

aof_enabled:1 ###AOF 是否处于打开状态

aof_rewrite_in_progress:0 ###服务器是否正在创建 AOF 文件

aof_rewrite_scheduled:0 ###RDB 文件创建完毕之后,是否需要执行预约的 AOF 重写操作

aof_last_rewrite_time_sec:-1 ###最近一次创建 AOF 文件耗费的时长

aof_current_rewrite_time_sec:-1 ###如果服务器正在创建 AOF 文件,那么这个域记录的就是当前的创建操作已经耗费的秒数

aof_last_bgrewrite_status:ok ###最近一次创建 AOF 文件的结果是成功还是失败

aof_last_write_status:ok

aof_current_size:176265 ###AOF 文件目前的大小

aof_base_size:176265 ###服务器启动时或者 AOF 重写最近一次执行之后,AOF 文件的大小

aof_pending_rewrite:0 ###是否有 AOF 重写操作在等待 RDB 文件创建完毕之后执行

aof_buffer_length:0 ###AOF 缓冲区的大小

aof_rewrite_buffer_length:0 ###AOF 重写缓冲区的大小

aof_pending_bio_fsync:0 ###后台 I/O 队列里面,等待执行的 fsync 调用数量

aof_delayed_fsync:0 ###被延迟的 fsync 调用数量

# Stats

total_connections_received:8466 ###服务器已接受的连接请求数量

total_commands_processed:900668 ###服务器已执行的命令数量

instantaneous_ops_per_sec:1 ###服务器每秒钟执行的命令数量

total_net_input_bytes:82724170

total_net_output_bytes:39509080

instantaneous_input_kbps:0.07

instantaneous_output_kbps:0.02

rejected_connections:0 ###因为最大客户端数量限制而被拒绝的连接请求数量

sync_full:2

sync_partial_ok:0

sync_partial_err:0

expired_keys:0 ###因为过期而被自动删除的数据库键数量

evicted_keys:0 ###因为最大内存容量限制而被驱逐(evict)的键数量。

keyspace_hits:0 ###查找数据库键成功的次数。

keyspace_misses:500000 ###查找数据库键失败的次数。

pubsub_channels:0 ###目前被订阅的频道数量

pubsub_patterns:0 ###目前被订阅的模式数量

latest_fork_usec:402 ###最近一次 fork() 操作耗费的毫秒数

# Replication

role:master ###如果当前服务器没有在复制任何其他服务器,那么这个域的值就是 master ;否则的话,这个域的值就是 slave 。注意,在创建复制链的时候,一个从服务器也可能是另一个服务器的主服务器

connected_slaves:2 ###2个slaves

slave0:ip=192.168.65.130,port=6379,state=online,offset=1639,lag=1

slave1:ip=192.168.65.129,port=6379,state=online,offset=1639,lag=0

master_repl_offset:1639

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:1638

# CPU

used_cpu_sys:41.87 ###Redis 服务器耗费的系统 CPU

used_cpu_user:17.82 ###Redis 服务器耗费的用户 CPU

used_cpu_sys_children:0.01 ###后台进程耗费的系统 CPU

used_cpu_user_children:0.01 ###后台进程耗费的用户 CPU

# Keyspace

db0:keys=3101,expires=0,avg_ttl=0 ###keyspace 部分记录了数据库相关的统计信息,比如数据库的键数量、数据库已经被删除的过期键数量等。对于每个数据库,这个部分都会添加一行以下格式的信息

只看其中一部分:info Replication

重新统计:config resetstat

2.8 查看和修改配置
redis-cli INFO | grep ^db				//看到Redis实例的所有数据库,而不会启动 redis-cli
redis 127.0.0.1:6379> config get dir   //取redis安装目录,config get *:查看所有配置
127.0.0.1:6379> config get databases	//查看当前数据库数量,默认16个,0-15,Redis数据库的数量是固定的,并在配置文件中进行相关设置,如上文所述。
1) "databases"
2) "16"
127.0.0.1:6379>set key value 		//创建/设置k-v对
127.0.0.1:6379>get key				//查看key的值
127.0.0.1:6379> dbsize				//查看当前数据库中key的数目
127.0.0.1:6379> exists name
(integer) 1							//检查给定key 是否存在,若key 存在,返回1 ,否则返回0
127.0.0.1:6379> expire name 5		//为给定key 设置生存时间,当key 过期时(生存时间为0 ),它会被自动删除,设置成功返回1 
127.0.0.1:6379> KEYS * 			//查看当前数据库中所有键
127.0.0.1:6379> KEYS t* 			//查看当前数据库中以 t 开头的数据,*表重复0到多
127.0.0.1:6379> KEYS t? 		//查看当前数据库中以t开头后面包含任意1位的数据?(重复0到1)
127.0.0.1:6379> KEYS t?? 			//查看当前数据库中以 t 开头且开头后面包含任意两位的数据
127.0.0.1:6379> KEYS h[ae]llo 	//匹配hello 和hallo ,但不匹配hillo 。特殊符号用 "\" 隔开
127.0.0.1:6379> info keyspace			//列出定义了某些键的数据库
127.0.0.1:6379> EXISTS key				//查看指定key是否存在
127.0.0.1:6379> set key value			//set: 存放数据,命令格式为
127.0.0.1:6379> get key					//get: 获取数据,命令格式为
127.0.0.1:6379> del key				//删除指定的key
127.0.0.1:6379> config set requirepass 123456  //配置密码
127.0.0.1:6379> auth 123456		//需要重新输入新密码登录验证,否则所有操作不可用
127.0.0.1:6379> config get requirepass  //查看密码
127.0.0.1:6379>EXPIRE key second		//设置可以的过期时间s;
127.0.0.1:6379>ttl key					//查看key的过期时间,以秒为单位,返回给定key 的剩余生存时间(TTL, time to live),当key 不存在时,返回-2 ;当key 存在但没有设置剩余生存时间时,返回-1 ;否则,以秒为单位,返回key 的剩余生存时间。
127.0.0.1:6379>PTTL key			//以毫秒为单位返回key 的剩余生存时间
127.0.0.1:6379>type key			   //查看当前key的类型,none (key 不存在)\string (字符串)\list (列表)\set (集合)\zset (有序集)\hash (哈希表
127.0.0.1:6379>STRLEN key			//查看key的值的长度
127.0.0.1:6379>APPEND key value 	//追加key的值,如果key不存在 就相当于 set key
127.0.0.1:6379>incr key			//开启可以自增功能
127.0.0.1:6379>incrby key num		//自增多少个key
127.0.0.1:6379>decr key			//开启可以自减功能
127.0.0.1:6379>decrby key num 		
127.0.0.1:6379>GETRANGE key start end		//截取字符串范围
127.0.0.1:6379>GETRANGE key 0 3			//截取0-3个字符串
127.0.0.1:6379>GETRANGE key 0 -1			//获取全部字符串
127.0.0.1:6379>SETRANGE key offset value   //从offset后的第几位替换字符
127.0.0.1:6379>setex key seconds value 	//设置过期时间
127.0.0.1:6379>setnx key value 			//不存在设置(分布式锁中常常用到)
127.0.0.1:6379>setnx key  					//如果这个key存在 创建失败
127.0.0.1:6379>setnx key  					//如果不存在 创建成功 
127.0.0.1:6379>mset k1 v1 k2 v2 k3 v3		//创建多个键值,key 存在就不创建;设置多个值要么同时成功,要么同时失败  原子性操作
127.0.0.1:6379>mset user:1:name zhangsan user:1:age 2		//设置user:1对象 值为json字符串来保存一个对象,如同set user:1{name:zhangsan,age:2}
127.0.0.1:6379> mget id1:name id1:age		//一次获得多个key的value
1) "heeello"
2) "25"
127.0.0.1:6379>getset key value			//#如果不存在,返回nill设置值,如果存在值,返回原值后更新值
127.0.0.1:6379> mv name 1					//将0中的key移动到1数据库中,移过去就只存在db1里了;移动成功返回1 ,失败则返回0
127.0.0.1:6379> RANDOMKEY					//从当前数据库中随机返回(不删除) 一个key,当数据库非空时,返回一个key 。当数据库为空时,返回nil
127.0.0.1:6379> RENAME key newkey		//将key 改名为newkey,当key 和newkey 相同,或者key 不存在时,返回一个错误。当newkey 已经存在时,RENAME 命令将覆盖旧值
127.0.0.1:6379>info Persistence			//查看持久化信息
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1653577094
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

#哨兵模式:sentinel,默认端口26379;使用该模式时,客户端不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。

localhost:6379> role
1) "master"
2) (integer) 0
3) (empty array)
localhost:6379> client list  //查看当前连接到redis的传话信息:id,来源ip,连接的db
id=601 addr=172.1.18.26:46794 laddr=172.1.18.28:6379 fd=10 name= age=2556527 idle=2 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=0 qbuf-free=20474 argv-mem=0 multi-mem=0 rbs=1024 rbp=43 obl=0 oll=0 omem=0 tot-mem=22272 events=r cmd=get user=default redir=-1 resp=2
id=602 addr=172.1.18.27:46374 laddr=172.1.18.28:6379 fd=9 name= age=2556468 idle=2 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=0 qbuf-free=20474 argv-mem=0 multi-mem=0 rbs=1024 rbp=43 obl=0 oll=0 omem=0 tot-mem=22272 events=r cmd=get user=default redir=-1 resp=2
id=630 addr=172.1.18.28:35274 laddr=172.1.18.28:6379 fd=8 name= age=1394473 idle=2 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=0 qbuf-free=20474 argv-mem=0 multi-mem=0 rbs=1024 rbp=43 obl=0 oll=0 omem=0 tot-mem=22272 events=r cmd=get user=default redir=-1 resp=2
id=653 addr=127.0.0.1:36194 laddr=127.0.0.1:6379 fd=11 name= age=84 idle=0 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=26 qbuf-free=20448 argv-mem=10 multi-mem=0 rbs=1024 rbp=0 obl=0 oll=0 omem=0 tot-mem=22298 events=r cmd=client|list user=default redir=-1 resp=2
localhost:6379>  info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:0d1f232b6a10d224afddb85a4f960f07d15d2c86
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
localhost:6379> set foo hello  //测试数据同步
OK
127.0.0.1:6379> get foo
"hello"
#重新登录到其他端口,无法删除和修改,提示不能在只读从服务器上写
127.0.0.1:7001> del foo
(error) READONLY You can't write against a read only replica.
127.0.0.1:7001> set foo1 hi
(error) READONLY You can't write against a read only replica.

#启用哨兵
#master修改redis.conf

sed -i 's/^bind 127.0.0.1/bind 0.0.0.0/g' redis.conf
sed -i 's/protected-mode yes/protected-mode no/g' redis.conf
sed -i 's/appendonly no/appendonly yes/g' redis.conf
sed -i "/# requirepass foobared/a requirepass 123456" redis.conf
sed -i "/# masterauth <master-password>/a masterauth 123456" redis.conf

#slave修改redis.conf

sed -i 's/^bind 127.0.0.1/bind 0.0.0.0/g' redis.conf
sed -i 's/protected-mode yes/protected-mode no/g' redis.conf
sed -i 's/appendonly no/appendonly yes/g' redis.conf
sed -i "/# replicaof <masterip> <masterport>/a replicaof 172.8.18.28 6379" redis.conf
sed -i "/# requirepass foobared/a requirepass 123456" redis.conf
sed -i "/# masterauth <master-password>/a masterauth 123456" redis.conf

#cat sentinel.conf
port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp
#sentinel monitor mymaster 172.8.18.28 6379 2
sentinel auth-pass mymaster 123456 172.8.18.28 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
#启动哨兵
redis-sentinel sentinel.conf

#sentinel 事件说明
·       +reset-master :主服务器已被重置。
 
·       +slave :一个新的从服务器已经被 Sentinel 识别并关联。
 
·       +failover-state-reconf-slaves :故障转移状态切换到了 reconf-slaves 状态。
 
·       +failover-detected :另一个 Sentinel 开始了一次故障转移操作,或者一个从服务器转换成了主服务器。
 
·       +slave-reconf-sent :领头(leader)的 Sentinel 向实例发送了 [SLAVEOF](/commands/slaveof.html) 命令,为实例设置新的主服务器。
 
·       +slave-reconf-inprog :实例正在将自己设置为指定主服务器的从服务器,但相应的同步过程仍未完成。
 
·       +slave-reconf-done :从服务器已经成功完成对新主服务器的同步。
 
·       -dup-sentinel :对给定主服务器进行监视的一个或多个 Sentinel 已经因为重复出现而被移除 —— 当 Sentinel 实例重启的时候,就会出现这种情况。
 
·       +sentinel :一个监视给定主服务器的新 Sentinel 已经被识别并添加。
 
·       +sdown :给定的实例现在处于主观下线状态。
 
·       -sdown :给定的实例已经不再处于主观下线状态。
 
·       +odown :给定的实例现在处于客观下线状态。
 
·       -odown :给定的实例已经不再处于客观下线状态。
 
·       +new-epoch :当前的纪元(epoch)已经被更新。
 
·       +try-failover :一个新的故障迁移操作正在执行中,等待被大多数 Sentinel 选中(waiting to be elected by the majority)。
 
·       +elected-leader :赢得指定纪元的选举,可以进行故障迁移操作了。
 
·       +failover-state-select-slave :故障转移操作现在处于 select-slave 状态 —— Sentinel 正在寻找可以升级为主服务器的从服务器。
 
·       no-good-slave :Sentinel 操作未能找到适合进行升级的从服务器。Sentinel 会在一段时间之后再次尝试寻找合适的从服务器来进行升级,又或者直接放弃执行故障转移操作。
 
·       selected-slave :Sentinel 顺利找到适合进行升级的从服务器。
 
·       failover-state-send-slaveof-noone :Sentinel 正在将指定的从服务器升级为主服务器,等待升级功能完成。
 
·       failover-end-for-timeout :故障转移因为超时而中止,不过最终所有从服务器都会开始复制新的主服务器(slaves will eventually be configured to replicate with the new master anyway)。
 
·       failover-end :故障转移操作顺利完成。所有从服务器都开始复制新的主服务器了。
 
·       +switch-master :配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关心的信息。
 
·       +tilt :进入 tilt 模式。
 
·       -tilt :退出 tilt 模式。

配置修改:
临时设置:config set
永久设置:config rewrite,将目前服务器的参数配置写入redis conf.

127.0.0.1:6379> CONFIG get requirepass  //命令查看是否设置了密码验证:
默认情况下 requirepass 参数是空的,这就意味着你无需通过密码验证就可以连接到 redis 服务。
配置密码:
127.0.0.1:6379> CONFIG set requirepass "ziqiangxuetang.com"
OK
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) "ziqiangxuetang.com"  //客户端连接 redis 服务就需要密码验证,否则无法执行命令。
下次登录时就需要输入密码:
127.0.0.1:6379> AUTH "ziqiangxuetang.com"
OK
127.0.0.1:6379> SET mykey "Test value"
OK
127.0.0.1:6379> GET mykey
"Test value"
2.9 批量执行操作

1)利用telnet:

(echo -en "ping\r\nset key abc\r\nget key\r\n";sleep 1) | nc 127.0.0.1 6379

2)使用pipeline:

vim insert.dat:

set a b
set 1 2
set h w
set f u

执行命令:cat insert.bat | ./redis-cli --pipe,或者如下脚本:

#!/bin/sh
host=$1
port=$;
password=$3

cat insert.dat | ./redis-cli -h $host -p $port -a $password --pipe
2.10 Redis数据库常用操作

1)选择数据库:select db-index,默认连接的数据库所有是0,默认数据库数是16个。返回1表示成功,0失败
2)flushdb:删除当前选择数据库中的所有 key。生产上已经禁止。
flushall: 删除所有的数据库。生产上已经禁止。
3)模拟宕机:redis-cli debug segfault
4)模拟hang机:redis-cli -p 6379 DEBUG sleep 30
5) 重命名命令:rename-command FLUSHALL “” ##必须重启。
6)设置密码:config set requirepass [passw0rd]
7)验证密码:auth passw0rd
8)性能测试命令:redis-benchmark -n 100000
9) 获取慢查询:SLOWLOG GET 10 ##结果为查询ID、发生时间、运行时长和原命令
10)查看日志
日志位置在/redis/log下,redis.log为redis主日志,sentinel.log为sentinel监控日志。
11)重复执行命令:redis-cli -r 100 -i 1 info stats | grep instantaneous_ops_per_sec
其中:-r 选项重复执行一个命令指定的次数。
-i 设置命令执行的间隔。

2.11 Redis数据导出(备份)和恢复
redis-cli -h 192.168.44.16 -p 6379 --rdb 6379.rdb

说明: --rdb:获取指定redis实例的rdb文件,保存到本地。

其他几个有用的参数:

- -slave:模拟slave从master上接收到的commands。slave上接收到的commands都是update操作,记录数据的更新行为。
- -pipe:发送原始的redis protocl格式数据到服务器端执行。
另外:nux下默认的换行是\n,windows系统的换行符是\r\n,redis使用的是\r\n.

1)创建当前数据库的备份

redis 127.0.0.1:6379> SAVE ##在 redis 安装目录中创建dump.rdb文件
或者:使用命令 BGSAVE,该命令在后台执行
127.0.0.1:6379> BGSAVE

Background saving started

2)恢复:
如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。

redis数据结构:
在这里插入图片描述

3、redis性能优化

3.1、提高 Redis 内存数据库的性能,可参考以下措施:

根据不同业务选择数据类型,有必要时对数据结构进行审核,减少数据冗余
精简键名和键值,控制键值的大小
使用前缀管理好 key
使用 scan 代替 keys,将遍历 Redis DB 中所有 key 的操作放到客户端来做
避免使用 O(N) 复杂度的命令
配置使用 ziplist 来优化 list
合理配置 maxmemory
数据量大的情况,做好 key 和 value 的压缩
利用管道,批量处理命令
根据不同业务选择短链接或者长链接
定期使用 redis-cli --big-keys 检测大 Key

3.2、Linux配置优化

1)内存分配控制:vm.overcommit_memory

Redis启动时,其日志可能会报因vm.overcommit_memory=0,建议修改设置为1,vm.overcommit_memory=0,代表没有可用内存,就会申请内存失败,对应到Redis就是执行fork失败,在Redis日志出现Cannot allocate memory;

执行

cat /proc/sys/vm/overcommit_memory
echo "vm.overcommit_memory=1" >>/etc/sysctl.conf  #或
sysctl vm.overcommit_memory=1  #设置合理的maxmemory参数,保证机器有20%-30%的闲置内存,置vm.overcommit_memory=1,防止极端情况下会造成fork失败

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

在这里插入图片描述
2)配置Swappiness

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

echo vm.swappiness=>> /etc/sysctl.conf
cat /proc/{pid}/smaps	##查看当前进程所对应内存映像信息,也可借助vmstat观察si(swap in)和so的值

3)THP(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
#永久修改
vim /etc/rc.local
echo never >/sys/kernel/mm/transparent_hugepage/enabled

4)OOM Killer优化

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

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

5)合理配置ulimit

ulimit –a 可查看打开文件的参数,是单个用户可以同时打开的最大文件个数。Redis允许有多个客户端通过网络进行连接,可以通过配置maxclients来限制最大客户端连接数。建议把maxclients设置成10032,默认是10000用来处理客户端,而且内部还会使用最多32个文件描述符。但是Redis进程因为没有权限无法将open files设成10032

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

6)TCP 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
cat ./redis/redis.conf |grep tcp-backlog
echo 511 > /proc/sys/net/core/somaxconn

7)处理bigkey

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

字符串类型:一般认为超过10k的就是bigkey,但是这个值和具体的OPS相关
非字符串类型:体现在哈希,列表,集合类型元素过多

主要危害:

1》内存空间不平衡:
2》超时阻塞:如果key教导,redis又是单线程,操作bigkey比较耗时,那么阻塞redis的可能性增大
3》网络负载较大:每次获取bigKey的网络流量较大,假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB的流量,对于普通千兆网卡,按照字节算128M/S的服务器来说可能扛不住。而且一般服务器采用单机多实例方式来部署,所以还可能对其他实例造成影响。

检查bigkey,执行:bin/redis-cli–bigkeys来统计bigkey的分布;判断一个key是否为bigkey,只需要执行debug objectkey,查看serializedlength属性,表示序列化之后的字节数。

处理:

当发现bigkey想删除的时候,对于string类型,一般不存在阻塞,但是对于hash,list,set,zset就不好说了,可在代码里添加逻辑删除操作

8)寻找处理热点key

在Redis 服务端,使用monitor命令统计热点key,facebook开源的 redis-faina就是用python实现的一个工具。

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

注意:monitor在高并发场景下,会存在内存暴增的和影响Redis的性能的隐患;只适合统计单机,集群的话需要汇总统计,生产场景慎用。建议措施:

在这里插入图片描述

3.3 调整伪装redis危险命令

Redis常见的危险命令包括:

keys: 如果键值较多,有可能阻塞Redis
flushall/flushdb: 数据全部被清除
save: 如果键值较多,存在阻塞的可能性
debug: debug reload会重启redis
config: config应该交给管理员用
shutdown: 关闭redis

重命名:rename-command flushall cleanall,但是需要同时对客户端也要修改,比如jedis.flushall操作内部就是调用的flushall命令;而且rename-command不支持config set;如果AOF和RDB文件包含了rename-command之前的命令,Redis将无法启动;所以你一旦要设置,最好在第一次的时候就配置好,而且主从结构,从的配置应该和主保持一致,否则存在主从数据结构不一致的情况。

4、Redis 数据恢复

如果Redis启用了AOF机制,那么当你执行完flushall、flushdb的时候,AOF文件会记录你的命令。这样我们就可以通过编辑AOF文件,然后把错误的命令删除后,然后就可以恢复了。但是有可能你在恢复的时候,会发生AOF重写,意味着会覆盖之前AOF文件,也就是以前的数据也丢失了,利用AOF来恢复就失效了。所以误操作flushall、flushdb后,步骤如下:

//调大AOF重写参数,避免短时间内的AOF自动重写
config set auto-aof-rewrite-percentage 1000
config set auto-aof-rewrite-min-size 10000000000
//打开AOF文件,去掉误操作的命令
//检验AOF文件格式是否正确,执行
./bin/redis-check-aof aof文件

5、Redis升级安装:Redis-stable最新稳定版

redis-cli shutdown #关停原先redis
wget http://download.redis.io/releases/redis-stable.tar.gz
tar -zxvf redis-stable.tar.gz
cd  redis-stable
make
make install PREFIX=/usr/local/redis-stable
#启动
/usr/local/redis-stable/src/redis-server /opt/redis-stable/redis.conf &
./redis-cli -a 密码
#版本查看
./redis-cli --version
./redis-server --version
./redis-server -v
#自动部署脚本

#!/bin/bash
function install_redis () {
        echo '开始安装'
        cd /opt
        #安装工具和环境
        yum install -y gcc
        if [ ! -f "redis-stable.tar.gz" ]; then
                #下载源码
                wget http://download.redis.io/releases/redis-stable.tar.gz
        fi
        #解压
        tar -zxvf redis-stable.tar.gz
        #进入目录
        cd  redis-stable
        #编译源码
        make
        #安装到/opt/redis目录
        make install PREFIX=/usr/local/redis-stable
 
        #拷贝配置文件
        #cp -f /usr/local/redis-7.0.4/redis.conf /usr/local/redis-stable/conf/redis.conf
        #修改内容
        echo '修改配置文件内容'
        #日志文件位置
        sed -i 's@logfile.*@logfile /opt/redis-stable/logs/redis.log@' /usr/local/redis-stable/redis.conf
        #保护模式
        sed -i 's#protected-mode yes#protected-mode no#g' /usr/local/redis-stable/redis.conf
        #后台运行
        sed -i 's#daemonize no#daemonize yes#g' /usr/local/redis-stable/redis.conf
        #修改绑定
        sed -i 's#^bind 127.0.0.1#bind 0.0.0.0#g' /usr/local/redis-stable/redis.conf
        #修改密码
        sed -i 's/.*requirepass.*foobared/requirepass 123456/' /usr/local/redis-stable/redis.conf
        #修改端口
        sed -i 's/port 6379/port 16379' /usr/local/redis-stable/redis.conf
        #修改数据存放路径
        sed -i 's#dir ./#dir /usr/local/redis-stable/data#g' /usr/local/redis-stable/redis.conf
		
        mkdir -p /usr/local/redis-stable/data
        mkdir -p /usr/local/redis-stable/logs
		
		echo "Starting Redis server..."
        /usr/local/redis-stable/src/redis-server /usr/local/redis-stable/redis.conf &
		
        echo "安装完成"
        ps -ef |grep redis
 
}
install_redis

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后,如java使用jedispool连接redis服务,修改redis.properties属性配置文件,将密码端口等信息填写进去

#ip地址
redis.host=192.168.25.131
#端口号
redis.port=6379
#如果有密码
redis.password=123456
#客户端超时时间单位是毫秒 默认是2000
redis.timeout=3000
#数据库,默认的是0
redis.database=0
#最大空闲数
maxIdle=300
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
maxActive=1000
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
maxTotal=1000
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
maxWait=1000
#在空闲时检查有效性, 默认false
testOnBorrow=false
#连接耗尽是否阻塞,false代表抛异常,true代表阻塞直到超时,默认为true
blockWhenExhausted=false

#连接的最小空闲时间 默认1800000毫秒(30分钟)
minEvictableIdleTimeMillis=300000
#每次释放连接的最大数目,默认3
numTestsPerEvictionRun=1024
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
timeBetweenEvictionRunsMillis=30000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个,数据量大的时候建议关闭
testWhileIdle=true

#编写applicationContext-redis.xml,
#将jedi spool纳入spring管理,加载属性配置文件(外面包一层JedisClientPool只是为了如果使用集群方式的话就不需要改业务代码了,只需要改配置就行,使用了策略模式)
#查看jedisPool的源码可发现如果想要设置密码只有两种构造方法可以选用,这里我们选择如下这种构造方法在spring配置文件中进行配置:

public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port,int timeout, final String password, final int database) {
		this(poolConfig, host, port, timeout, password, database, null);
}

#上面的构造方法我们得知如果jedispool使用密码的话需要配置poolConfig,host,port,timeout,password,database等属性,我们在spring的xml文件中进行配置,并加载上面已经写好的配置文件就行了


<!--连接redis单机版,创建了一个JedisClientPool的bean,创建这个bean需要一个jedisPool属性-->
<bean class="cn.e3mall.common.jedis.JedisClientPool" id="jedisClientPool">
	<property name="jedisPool" ref="jedisPool"></property>
</bean>
<!--JedisPool这个bean的构造方法需要多个参数-->
<bean class="redis.clients.jedis.JedisPool" id="jedisPool" >
	<constructor-arg name="host" value="${redis.host}"></constructor-arg>
	<constructor-arg name="port" value="${redis.port}"></constructor-arg>
	<constructor-arg name="password" value="${redis.password}"></constructor-arg>
	<constructor-arg name="timeout" value="${redis.timeout}"></constructor-arg>
	<constructor-arg name="database" value="${redis.database}"></constructor-arg>
	<constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.JedisPoolConfig" id="jedisPoolConfig">
	<property name="maxIdle" value="${maxIdle}" />
	<property name="maxTotal" value="${maxActive}" />
	<property name="maxWaitMillis" value="${maxWait}" />
	<property name="testOnBorrow" value="${testOnBorrow}" />
	<property name="blockWhenExhausted" value="${blockWhenExhausted}" />
</bean>

#jedisPool接口以及实现类,编写jedispool的接口以及实现类:

#接口:

package cn.e3mall.common.jedis;

import java.util.List;

public interface JedisClient {

	String set(String key, String value);
	String get(String key);
	Boolean exists(String key);
	Long expire(String key, int seconds);
	Long ttl(String key);
	Long incr(String key);
	Long hset(String key, String field, String value);
	String hget(String key, String field);
	Long hdel(String key, String... field);
	Boolean hexists(String key, String field);
	List<String> hvals(String key);
	Long del(String key);
}

#实现类:

package cn.e3mall.common.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class JedisClientPool implements JedisClient {

	private JedisPool jedisPool;

	public JedisPool getJedisPool() {
	return jedisPool;
	}

	public void setJedisPool(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}

	@Override
	public String set(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.set(key, value);
		jedis.close();
		return result;
	}

	@Override
	public String get(String key) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get(key);
		jedis.close();
		return result;
	}

	@Override
	public Boolean exists(String key) {
	Jedis jedis = jedisPool.getResource();
	Boolean result = jedis.exists(key);
	jedis.close();
	return result;
	}

	@Override
	public Long expire(String key, int seconds) {
	Jedis jedis = jedisPool.getResource();
	Long result = jedis.expire(key, seconds);
	jedis.close();
	return result;
	}

	@Override
	public Long ttl(String key) {
	Jedis jedis = jedisPool.getResource();
	Long result = jedis.ttl(key);
	jedis.close();
	return result;
	}

	@Override
	public Long incr(String key) {
	Jedis jedis = jedisPool.getResource();
	Long result = jedis.incr(key);
	jedis.close();
	return result;
	}

	@Override
	public Long hset(String key, String field, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hset(key, field, value);
		jedis.close();
		return result;
	}

	@Override
public String hget(String key, String field) {
Jedis jedis = jedisPool.getResource();
String result = jedis.hget(key, field);
jedis.close();
return result;
}

@Override
public Long hdel(String key, String... field) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hdel(key, field);
jedis.close();
return result;
}

@Override
public Boolean hexists(String key, String field) {
Jedis jedis = jedisPool.getResource();
Boolean result = jedis.hexists(key, field);
jedis.close();
return result;
}

@Override
public List<String> hvals(String key) {
Jedis jedis = jedisPool.getResource();
List<String> result = jedis.hvals(key);
jedis.close();
return result;
}

	@Override
	public Long del(String key) {
	Jedis jedis = jedisPool.getResource();
	Long result = jedis.del(key);
	jedis.close();
	return result;
	}
}

#测试类:
package cn.e3mall.jedis;

import cn.e3mall.common.jedis.JedisClient;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @author sunqizheng
* @Title: TestJedisClient
* @ProjectName ttmall
* @Description: TODO
* @date 2018/9/1917:21
*/
public class TestJedisClient {
@Test
public void TestJedisClient() throws Exception{
//初始化一个spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//从容器中获得JedisClient对象,(拿到接口的对象)
JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
jedisClient.set("mytest","jedisClient1");
String string = jedisClient.get("mytest");
System.out.println(string);
}
}

四、附录

4.1 redis-benchmark

它是redis官方的一个压力测试工具!

//测试100个并发 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

常用参数如下所示:
在这里插入图片描述

4.2 Redis 数据类型的内存模型

1)String类型
在这里插入图片描述

2)List类型

list 列表相当于java中list 集合;在redis里面,可以把list当做:栈、队列、阻塞队列!所有的list命令都是用l开头的,Redis不区分大小命令

特点:元素有序 且可以重复
在这里插入图片描述
eg1:LPUSH 将一个值或者多个值,插入到列表头部 (左):LPUSH key value

127.0.0.1:6379> LPUSH name one
(integer) 1
127.0.0.1:6379> LPUSH name rwo
(integer) 2
127.0.0.1:6379> LPUSH name three # 获取list中值
(integer) 3
(empty array)
127.0.0.1:6379> LRANGE name 0 3
1) "three"
2) "rwo"
3) "one"
127.0.0.1:6379> 

eg2:RPUSH 将一个值或者多个值,插入到列表位部 (右):RPUSH key value

127.0.0.1:6379> lpop key  #从最左边移除一个元素
127.0.0.1:6379> rpop key  #从最右边移除一个元素
127.0.0.1:6379> lindex key index  #通过下标获取list的值
127.0.0.1:6379> llen 		#获取list的长度
127.0.0.1:6379> llen key
127.0.0.1:6379> LREM key count element  #移除list指定的值,count:指定移除个数

3)Set类型

特点: Set类型 /Set集合: 元素无序 不可以重复
在这里插入图片描述
4)ZSet类型

特点: 可排序的set集合 排序 不可重复

ZSET:可排序SET sortSet

在这里插入图片描述
5) hash类型

特点: value 是一个map结构,存在key value,无序的
在这里插入图片描述
更多参看Redis详解

4.3 Redis AOF持久化机制

在Redis中,为避免内存中数据丢失,Redis 需要持久化,目前主要有两大机制,即 AOF(Append Only File)日志和 RDB 快照。Redis 数据持久化AOF默认开启,每当有一个修改数据库的命令被执行时,服务器就将命令写入到 appendonly.aof 文件中,该文件存储了服务器执行过的所有修改命令,因此,只要服务器重新执行一次 .aof 文件,就可以实现还原数据的目的,这个过程又被形象地称之为“命令重演”。相比主流关系型数据库的WAL,即数据库的写前日志(Write Ahead Log, WAL),AOF 日志正好相反,可认为它是"写后日志",即 Redis 是先执行命令,把数据写入内存,然后才记录AOF日志,且Redis 在向 AOF 里面记录日志的时,并不会先去对这些命令进行语法检查,因为正确执行的才写入aof,从而避免出现记录错误命令的情况,另外带来的好处就是,不会阻塞当前的写操作,但可能会给下一个操作带来阻塞风险。这是因为,AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。

1) 写入机制

Redis 在收到客户端修改命令后,先进行相应的校验,如果没问题,就立即将该命令存追加到 .aof 文件中,也就是先存到磁盘中,然后服务器再执行命令。这样就算遇到了突发的宕机情况情况,也只需将存储到 .aof 文件中的命令,进行一次“命令重演”就可以恢复到宕机前的状态。

在上述执行过程中,有一个很重要的环节就是命令的写入,这是一个 IO 操作。Redis 为了提升写入效率,它不会将内容直接写入到磁盘中,而是将其放到一个内存缓存区(buffer)中,等到缓存区被填满时才真正将缓存区中的内容写入到磁盘里。

2) 重写机制

Redis 在长期运行的过程中,aof 文件会越变越长。如果机器宕机重启,“重演”整个 aof 文件会非常耗时,导致长时间 Redis 无法对外提供服务。因此就需要对 aof 文件做一下“瘦身”运动。

为了让 aof 文件的大小控制在合理的范围内,Redis 提供了 AOF 重写机制,手动执行:bgrewriteaof命令,开始重写 aof 文件,如下所示:

127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

通过上述操作后,服务器就会生成一个新的 aof 文件,该文件具有以下特点:

1>新的 aof 文件记录的数据库数据和原 aof 文件记录的数据库数据完全一致;
2>新的 aof 文件会使用尽可能少的命令来记录数据库数据,因此新的 aof 文件的体积会小很多;
3>AOF 重写期间,服务器不会被阻塞,它可以正常处理客户端发送的命令。

为什么重写机制可以把日志文件变小呢? 这是因重写机制类似具备“多变一”功能,即可旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。如下示例所示:

在这里插入图片描述
与AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。它会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

而这期间,主线程仍然可以处理新来的操作。此时,如果有写操作,对正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。这样即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。而对于新的 AOF 重写日志,新的写操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 日志文件。
在这里插入图片描述

3) 自动触发AOF重写

Redis 为自动触发 AOF 重写功能,提供了相应的配置策略。可通过修改 Redis 配置文件,让服务器自动执行 BGREWRITEAOF 命令。如下所示:

#默认配置项
auto-aof-rewrite-percentage 100 #触发重写所需要的 aof 文件体积百分比,只有当 aof 文件的增量大于 100% 时才进行重写,也就是大一倍。比如,第一次重写时文件大小为 64M,那么第二次触发重写的体积为 128M,第三次重写为 256M,以此类推。如果将百分比值设置为 0 就表示关闭 AOF 自动重写功能。
auto-aof-rewrite-min-size 64mb #表示触发AOF重写的最小文件体积,大于或等于64MB自动触发。

4) AOF策略配置

为降低服务器宕机,redis aof日志丢失命令的数量,即越早地把命令写入到磁盘中,发生意外时丢失的数据就会越少,我们可以通过redis.conf配置项 appendfsync来配置AOF持久化策略,有三个可选值:

Always(同步写回):每个写命令执行完,立马同步地将日志写回磁盘;服务器每执行一个命令,就调用一次 fsync 函数,将缓冲区里面的命令写入到硬盘;但 fsync 是磁盘 IO 操作,所以相对很慢!如果 Redis 执行一条指令就要 fsync 一次(Always),那么 Redis 高性能将严重受到影响。
Everysec(每秒写回):每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;这种模式下,服务器出现故障,最多只丢失一秒钟内的执行的命令数据,生产中通常都使用它作为 AOF 配置策略,这样Redis 每隔 1s 左右执行一次 fsync 操作( Everysec),既保持了高性能,也让数据尽可能的少丢失。
No(操作系统控制的写回):每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘,不会主动调用 fsync 函数。这种策略存在许多不确定性,不建议使用。

在这里插入图片描述

总结:

在这里插入图片描述

5)AOF和RDB对比:
在这里插入图片描述
当2种文件持久化机制同时具备时,应优先通过 appendonly.aof 恢复数据,最大程度地保证数据的安全性。

  • 10
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羌俊恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值