关于 Redis 运维 系列其他文章的传送门
关于 Redis 开发 系列其他文章的传送门

分布式系统的 CAP、BASE 理论¶
CAP 理论¶
CAP:Consistency、Availablity、Partition tolerence
- C:一致性,多个数据节点上数据一致;
- A:可用性,用户发出请求后的有限时间范围内返回结果;
- P:分区容错性,network partition,网络发生分区后,服务是否依然可用;
一个分布式系统不可能同时满足 C、A、P 三个特性,最多可同时满足其中两者;
而对于分布式系统,满足分区容错性几乎是必须的,所以可用的其实只有 AP 和 CP。
BASE 理论¶
BASE:BA,S,E,基于 CAP 演化而来。
- BA(Basically Available):基本可用;
- S(Soft state):软状态/柔性事务,即状态可以在一个时间窗口内是不同步的;
- E(Eventually consistency):最终一致性;
是由 eBay 架构师提出的。BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网分布式系统实践的总结,是基于 CAP 定律逐步演化而来。其核心思想是即使无法做到强一致性,但每个应用都可以根据自身业务特点,才用适当的方式来使系统打到最终一致性
什么是 Redis?¶
Redis 是一个开源(BSD许可)的内存数据结构存储,可用作数据库,缓存和消息队列。
支持的数据结构:如字符串,散列,列表,集合,有序集合,位图,超级日志,具有半径查询和流式的地理空间索引。

Redis 内置了复制、Lua scripting、LRU、事务和不同级别的磁盘持久性,并通过 Redis Sentinel 提供高可用性,使用 Redis Cluster 实现自动分区。
Redis 是单进程程序,CPU 并不是瓶颈,支持将数据持久化。
插播:伯克利推出了号称世界最快的 KVS 数据库 Anna,能秒杀 Redis 和 Cassandra,伯克利又要搞事情,希望在以后看到 Anna 的相关实现落地。
一、Redis 程序介绍¶
CentOS 的 Base 仓库已经收录了 Redis,直接使用 yum 就可以安装。
1 | yum install redis |
- 默认端口:
6379
程序文件:
/etc/redis.conf:配置文件;/usr/bin/redis-server:主程序;/usr/lib/systemd/system/redis.service:Unit File 文件;/usr/bin/redis-cli:命令行接口;/var/lib/redis:默认存储位置;/usr/bin/redis-sentinel:主从集群程序;/etc/redis-sentinel.conf:主从集群配置文件;/usr/lib/systemd/system/redis-sentinel.service:主从集群的 Unit File 文件;
二、Redis 的常用命令¶
redis-cli:Redis 是 C/S 架构,使用客户端工具连接 Redis 的 Server 端,默认连接本地的 127.0.0.1:6379;
-h:指定连接地址;-p:端口;-a:密码;-r <n> <COMMAND>:执行指定的命令 N 次,不进入交互模式直接执行命令;-i:使用-r时,每个命令等待 N 秒;-db:定义默认使用的数据库;
与连接相关的命令¶
CLIENT LIST:列出所有连接的客户端;列出的信息中,如果最后的cmd为cmd=replconf则一般为 slave 节点;CONFIG KILL:关闭客户端连接;
可在运行时修改参数:
CONFIG GET:查看一个参数;CONFIG SET:定义一个参数;CONFIG REWRITE:在命令行中配置的参数也可以同步至配置文件中;CONFIG RESETSTAT:计数器归零;
Resid 支持创建多个数据库,但是和 MySQL 不同,Redis 的数据库不是自定义名字的,而是按编号排序,默认 0-15 共 16 个,0 号库为默认库,如果要切换数据库则使用 SELECT。
Redis 的命令按照不同的功能分为不同的类别,
使用 help 可以查看帮助,包括命令格式等信息。输入 help 后使用 TAB 键可自动补全分类列表,比如 @generic、@string 等就是不同的分类。
下面按照分类一一解释常用命令。
@connection¶
AUTH:输入认证密码;ECHO:同echo;PING:探测服务器存活与否;SELECT:切换数据库;
@generic¶
DEL:删除一个 key;EXISTS:验证一个键是否存在;KEYS:以指定模式匹配查找所有的键;TTL:查看一个键的存活时间;MOVE:把一个键移动至其他数据库;
…
@string¶
SET:设置键值;GET:获取键值;EXISTS:判断键值存在与否;INCR:数值型数据自增;DECR:数值型数据自减;SETNX:定义当值不存在时才创建;SETEX:定义过期时间;MGET:一次获取多个键;MSET:一次设置多个键;
@list¶
Redis 中的列表其实就是一个数组,里边有很多元素,对列表中的元素可以操作的方式有:
- 修改已有元素的值;
- 新增元素项;
- 移除一个已有的元素项;
其中 移除 元素有两种方法:
- 从首部或尾部开始删除元素,称为 POP(弹出);
- 以列表首部为 L,尾部为 R,那么:
LPOP就代表从左侧删除;RPOP就代表从右侧删除;
- 以列表首部为 L,尾部为 R,那么:
- 指定元素删除。
而 新增 元素项也可以从首部或尾部挤进来,称为 PUSH,以列表首部为 L,尾部为 R,那么:
LPUSH:从左侧插入;RPUSH:从右侧插入;
组合起来使用的话:
LPUSH + RPOP 的方式就叫 队列;特点是元素先进先出,可以作为 消息队列;
LPUSH + LPOP 的方式就叫 堆栈,特点是元素先进后出。
LPOP:从列表的左侧删除;RPOP:从列表的右侧删除;LPUSH:从列表的左侧插入;RPUSH:从列表的右侧插入;LPUSHX:如果元素不存在才从左侧创建;RPUSHX:如果元素不存在才从右侧创建;LSET:直接设定某一元素的值;LLEN:判定整个列表中有多少元素;LINDEX:取出某个指定元素的值;LINDEX:指定索引信息;LINSERT:在指定位置插入元素;LRANGE:取一个指定的范围;LREM:删除指定的元素,可批量;
示例:
1 | 127.0.0.1:6379> LPUSH alpha a b c d e f g # 写在最左侧的 a 先入栈,越早入栈的在数列中越靠右 |
@hash¶
hash 就是关联数组,和列表数组不同,列表数组中元素的索引位置是固定的,而关联数组中元素的索引是按照 key 来定位,每一个元素都自带索引和数据,都是一个 K/V,而且 key 是多级的,每个 key 底下可以包含其他子 key。
为了以示区别,每个键值数据的标识称为 key,key 里边的每个子键叫一个字段(field),每个字段可以有自己的值。
HSET:定义一个键值;HMSET:一次定义值的多个字段;HLEN:查看共有多少个字段;HGET:查看指定字段的值;HMGET:查看多个字段的值;HKEYS:列出所有字段的键;HVALS:列出所有字段的值;HGETALL:列出所有字段的键和值;HDEL:删除定义的字段;HEXESTS:判断某个字段是否存在;HSETNX:如果不存在就新增一个字段的值;
示例:
1 | 127.0.0.1:6379> HMSET student_1 id 1 name xiaoming age 5 label TaiJiQuan # 定义一个 key 为 student_1,还有内部的各种子 key 以及对应的 value |
@set¶
Redis 也支持集合,配置命令:
SADD:向集合中添加元素;SCARD:获取所有元素个数;SINTER:做交集;SINTERSTORE:做交集并存储在一个新键中;SUNION:做并集;SUNIONSTORE:做并集并存储在一个新键中;SDIFF:做差集;SDIFFSTOPRE:做差集并存储在一个新键中;SISMEMBER:判断集合内部是否有这个成员;SMEMBERS:获取所有成员;SMOVE:移动指定成员;SPOP:随机弹出成员;SRANDMEMBER:随机获取一个成员;SREM:删除一个成员;
示例:
1 | 127.0.0.1:6379> SADD colors_1 red blue green black # 添加集合 1 |
@sorted_set¶
在集合的基础上,还有顺序集合,指的是将集合中的元素按照顺序排列。
ZADD:向集合中添加元素,需要指明 score(得分),分数高的排名靠前;ZCARD:显示所有元素个数;ZCOUNT:指定一个最大值和最小值,统计在此范围之间的成员数量;ZINCRBY:增加已排序集合中成员的得分;ZINTERSTORE:做交集,并存储在一个新键中;ZLEXCOUNT:按照字母表排序中的成员数;ZRANGE:按索引显示指定成员的位置;ZRANGEBYLEX:按指定字母表区间显示匹配的值;ZRANGEBYSCORE:按指定分数显示匹配的值;ZRANK:显示集合索引中成员的排序位置;ZREM:删除指定的成员;ZSCORE:查看指定成员多少分;
示例:
1 | 127.0.0.1:6379> ZADD china 100 beijing 90 sichuan 80 tianjing # 添加一个顺序集合 |
@pubsub¶
全称为 Publish Subscribe,也就是发布订阅;发布者从频道扇出消息,订阅者门接收。
SUBSCRIBE:订阅频道,可定义多个;PSUBSCRIBE:基于模式批量订阅频道;UNSUBSCRIBE:取消订阅;PUNSUBSCRIB:基于模式批量取消订阅;PUBSUB:查看订阅频道的状态信息;PUBLISH:发布者向指定频道发信息;
示例:

三、Redis 的配置文件¶
Redis 的配置文件和自身的客户端工具相似,也是按照功能分为不同的配置段,每段以 ### 分割,就像下面这样。
1 | [root@node1 ~]# grep -i ^## /etc/redis.conf |
下面对每段中常用的配置一一介绍,配置文件默认在:/etc/redis.conf。
NETWORK 网络¶
bind 0.0.0.0:定义监听的地址;port:定义监听的端口;tcp-backlog:定义后援队列长度,即等待队列;unixsocket /tmp/redis.sock:如果只在本机使用,建议启用基于 socket 的通信;unixsocketperm 700:定义 socket 文件的权限;timeout 0:定义连接的空闲等待超时时间,0 为不断开;tcp-keepalive 300:定义 TCP 连接的空闲等待超时时间。
SECURITY 安全¶
Redis 账户只有 root,没有权限分级的概念,也不能创建其他的账户。所以只要能连接到 Redis 的客户端都具有管理权限。不过好在 Redis 的配置文件中默认开启了一个叫“保护模式”的选项 protected-mode yes。
但是必须满足以下两个条件才会启用保护模式,缺一不可:
- 没有使用
bind指令明确的指明要监听的地址; - 没有启用
password;
requirepass PASSWORD:定义一个在客户端连接时必须输入的密码,默认没有启用;使用客户端连接后使用 AUTH $PASSWORD 来验证密码;
Command renaming 命令重命名:
为防止一些危险的命令被误操作,比如删库,可以给命令重新定义名字,例如:
rename-command CONFIG ef94762706d8829d0a37631196422f21af0b8a75
GENERAL 常规¶
darmonize no:要不要运行为守护进程,CentOS6 上一般为yes,CentOS7 上一般为no;loglevel notice:定义日志级别;logfile /var/log/redis/redis.log:定义日志文件存储路径;databases 16:定义当前 Redis 最多支持都少个数据库,默认为 16;-1表示为不予限制;
LIMTIS 限制¶
maxclients 10000:最大并发连接数;maxmemory <bytes>:Redis 使用的最大内存,一定要设置,否则可能会发生 OOM;maxmemory-policy:定义当内存用满后使用哪种淘汰数据的策略。- 注意:当 Redis 作为缓存时,数据可以被淘汰删除;但是作为数据库时,万万不可随便删除;
- 可用参数如下:
volatile-lru:Redis 中的键有的定义了过期时间,有的没有定义过期时间,那么对定义了过期时间的键,基于 lru 进行内存淘汰;alikeys-lru:对所有的键,无论是否定义了过期时间,都基于 lru 进行内存淘汰;volatile-random:对定义了过期时间的键,基于 random 进行内存淘汰;alikeys-randow:对所有的键,无论是否定义了过期时间,都基于 random 进行内存淘汰;volatile-ttl:对定义了过期时间的键,离过期时间越近的优先淘汰;缺点是过期时间基数大的和基数小的会放一起比较,假如一个 1000s 另一个 10s,五秒钟后一个还剩 995s,另一个只剩 5s,那么被淘汰的是 5s。noeviction:此值为默认,不淘汰现有的任何数据;
maxmemory-samples 5:定义淘汰内存时的采样范围;淘汰时每次在内存中只随机采样 5 个,然后选一个进行淘汰,可加快速度;
SLOW LOG 慢查询日志¶
slowlog-log-slower-than 10000:单位微妙 μs,定义慢查询日志的超时时间,默认 10ms;slowlog-mas-len 128:慢查询日志记录的最大条目;
ADVANCED 高级¶
hash-max-ziplist-entries 512:Redis 在使用 hash 方式存储时,每个键最多使用多少个字段;hash-max-ziplist-value 64:每个字段最大使用多少字节;
Redis 是内存型存储,I/O 速度非常快,但是和客户端之间的通信是通过网卡一个包一个包发送的,速度比较慢,所以 Redis 没必要在原地等待网卡一个一个发送,可以把数据扔进内存中的缓冲区,让网卡慢慢取,自己去忙别的。
client-output-buffer-limit normal 0 0 0:定义缓冲区的限制规则,硬限制、软限制、宽限期client-output-buffer-limit slave 256mb 64mb 60:对于从服务器类型的客户端,定义缓冲区限制规则。client-output-buffer-limit pubsub 32mb 8mb 60:当客户端把 Redis 作为消息队列时的缓冲区限制规则;
四、Redis 的数据持久化¶
Redis 的数据是保存在内存中的,那一断电岂不是就全没了?
当然不会,Redis 是支持将数据保存在磁盘中的,并且有两种保存方式 RDB (Redis DataBase) 和 AOF (Append Only File),下面分别介绍。
| 类型 | 在配置文件中对应的位置 |
|---|---|
| RDB | ##### SNAPSHOTTING ##### |
| AOF | ##### APPEND ONLY MOD ###### |
1. SNAPSHOTTING 即 RDB¶
RDB 存储的是二进制格式,按事先定制的策略,周期性地将数据从内存以快照的形式同步至磁盘;数据文件默认为 /var/lib/redis/dump.rdb;
在 客户端 可以显式使用 SAVE 或 BGSAVE 命令来手动启动快照保存机制;
SAVE:同步阻塞式命令,即在主线程中保存快照,此时会阻塞所有客户端请求;BGSAVE:异步非阻塞命令,执行期间还可以接收并处理客户端请求,会 folk 一个子进程创建 RDB 文件。
优点:
- 完全备份,不同时间的数据集备份可以做到多版本恢复;
- 紧凑的单一文件,方便网络传输,适合灾难恢复;
- 快照文件直接恢复,大数据集速度较 AOF 快。
缺点:
- 会丢失最近写入、修改的而未能持久化的数据;
- folk 过程非常耗时,会造成毫秒级不能响应客户端请求。
RDB 备份策略:
创建一个定时任务 cronjob,每小时或者每天将 dump.rdb 复制到指定目录 确保备份文件名称带有日期时间信息,便于管理和还原对应的时间点的快照版本。定时任务删除过期的备份,如果有必要,跨物理主机、跨机架、异地备份。
在配置文件中(RDB)¶
dbfilename dump.rdb:定义快照的文件名;dir /var/lib/redis:定义快照文件的保存路径;rdbchecksum yes:内存中数据和做好的快照是否核对校验码;rdbcompression yes:保存的快照是否进行压缩;stop-writes-on-bgsave-error yes:为了保证数据的一致性,如果快照创建失败了,是否停止客户端的所有请求。save <seconds> <changes>:定义 RDB 多久会将内存的数据以快照的形式同步至磁盘;
1 | 匹配顺序从下至上,例如: |
2. APPEND ONLY MOD 即 AOF¶
在使用 AOF 的方式持久化数据时,每次写操作都将记录保存在指定的磁盘文件实现的持久化,当 Redis 重启时,可通过重新执行文件中的命令在内存中重建出数据库;类似于 MariaDB 的 binlog。
优点:
- 写入机制,默认
fysnc每秒执行,性能很好不阻塞服务,最多丢失一秒的数据; - 重写机制,可以优化 AOF 文件体积;
- 如果误操作了(
FLUSHALL等),只要 AOF 未被重写,停止服务并移除 AOF 文件尾部的FLUSHALL命令,重启 Redis,可以将数据集恢复到 FLUSHALL 执行之前的状态。
缺点:
- 相同数据集,AOF 文件体积较 RDB 大了很多;
- 恢复数据库速度叫 RDB 慢(因为是文本,命令重演);
在配置文件中(AOF)¶
appendonly no:是否启用 AOF 类型的数据持久化,默认关闭;appendfilename "appendonly.aof":定义 AOF 文件的名字,支持使用绝对路径;
AOF 写入机制¶
注意:AOF 方式不能保证绝对不丢失数据!!!
目前常见的操作系统中,执行系统调用 write 函数,将一些内容写入到某个文件里面时,为了提高效率,系统通常不会直接将内容写入硬盘里面。而是每次写操作发给内核后,Linux 内核会先将数据放在内存中的内核空间的缓冲区(buffer)内,至于何时将内存中的数据同步至磁盘,内核有着自己的同步机制,也许等到缓冲区被填满,也许是用户执行 fsync 调用和 fdatasync 调用时才将储存在缓冲区里的内容真正的写入到硬盘里,未写入磁盘之前,数据可能会丢失,这样还是无法保障数据的一致性。
所以,redis 会通过使用 fsync 这个内核级调用来强制将数据从内存同步至磁盘中。
写入磁盘的策略:
appendfsync:定义在什么情况下调用 fsync 内核级参数,以确保的的确确的把内存中的数据同步至磁盘中,可用参数:
always:只要有一个写语句,就马上同步至磁盘,不在内核内存中停留。这种模式下,服务器出现故障,也不会丢失任何已经成功执行的命令数据;everysec(默认):无论有多少写语句,每一秒执行一次同步操作。这种模式下,服务器出现故障,最多只丢失一秒钟内的执行的命令数据,建议此项;no:不自行调用fsync,而是交给内核自动处理。这种模式下,服务器遭遇意外停机时,丢失命令的数量是不确定的。
运行速度:always 的速度慢,everysec 和 no 都很快。
AOF 日志文件重写¶
如果日志中重复的语句太多,写操作越来越多的被记录,AOF 文件会很大。Redis 会进行 AOF 日志重写来合并写操作,以压缩 AOF 文件。
比如一个键执行了 2W 次的 +1,就要记录 2W 个 +1 的写语句,重放时也徒增负担。这时可以把 2W 个 +1 的写语句重写为只加 1 次的值为 20000,重放是只需要执行一句就可以了。
重写过程:不管之前的 AOF 文件,以当前内存中的数据新建一个 AOF 文件,然后替换之前的。
- folk 一个子进程负责重写 AOF 文件;
- 子进程会在内存中创建一个临时文件写入 AOF 信息;
- 父进程会开辟一个内存缓冲区接收新的写命令;
- 子进程重写完成后,父进程会获得一个信号,将父进程接收到的新的写操作由子进程写入到临时文件中
- 新文件替代旧文件。
注:如果写入操作的时候出现故障导致命令写半截,可以使用 redis-check-aof 工具修复。
AOF 配置重写触发:
auto-aof-rewrite-min-size 64mb:当内容量大于等于此值后,才会进行重写,这个选项用于避免对体积过小的 AOF 文件进行重写;auto-aof-rewrite-percentage 100(%):当 AOF 文件的体积大于上一条中指定的体积,并且超过上一次重写之后的 AOF 文件体积的100%时,就会触发 AOF 重写。将这个值设置为0表示关闭自动 AOF 重写。
当以上两个条件同时满足时,与上次 AOF 文件大小相比,其增长量超过 100%,且文件大小不少于 64MB,才会触发重写 AOF;
如果服务器刚刚启动不久,还没有进行过 AOF 重写,那么使用服务器启动时载入的 AOF 文件的体积来作为基准值)
aof-load-truncated yes:如果从 AOF 恢复时,如果有语句因保存不完整等原因执行失败时,是否清理该语句。BGREWRITEAOF:AOF 日志文件重写;不会读取正在使用的 AOF 文件,而是通过将内存中的数据以命令的方式保存至临时文件中,完成之后替换原来的 AOF 日志文件;
在客户端中还可直接使用 BGREWRITEAOF 命令执行 AOF 日志文件重写:
重写配置举例:
1 | auto-aof-rewrite-percentage 100 |
当 RDB 与 AOF 同时启用时¶
BGSAVE 和 BGREWRITEAOF 不会同时进行!
- Redis 服务器启动时用持久化的数据文件恢复数据,会优先使用 AOF;
