Redis笔记

1. Redis简介

NoSQL即 $Not-Only\ \ SQL$ ,是RDBMS的补充。
Redis是一种NoSQL数据库,是一个使用C语言开发的开源的高性能键值对数据库,内部使用单线程机制进行工作。

  1. $set\ \ [key]\ \ [value]$  添加信息,
  2. $get\ \ [key]$    查询信息,为空返回 $nil$ ,
  3. $del\ \ [key]$  删除信息,$1$ 代表删除成功,
  4. $clear$  清屏,
  5. $help\ \ [op]$  帮助,
  6. $time$  获取时间.

2. 数据类型

Redis采用键值对的方式存储,$key$ 为字符串,$value$ 则允许多种类型。

2.1 string

最简单的数据类型,最大存储量 $512MB$ , 可以作为数字使用,最大值为 $java.lang.Long$ 类型的最大值, 允许以JSON对象的方式存储数据,值中间不能有空白符

  1. $mset/mget$  获取多个数据
  2. $strlen$  获取字符个数
  3. $append$  追加,返回总长度,也可以用于添加信息
  4. $incr/incrby/incrbyfloat$  指定数据增加指定值,允许负数,返回数值
  5. $decr/decrby$  指定数据减少指定的值,允许负数,返回数值
  6. $setex/psetex$  设定数据周期,秒/毫秒

2.2 hash

hash 属于 $value$ ,其中包含多组键值对,键称为 $field$ ,键值对较少时为类数组结构,较多时为 $HashMap$ 结构,$value$ 只能为 string ,且最多只能有 $2^{32} - 1$ 组。

  1. $hset/hmset$ ,
  2. $hget/hmget/hgetall$ ,
  3. $hdel$ ,
  4. $hlen$  字段数量,
  5. $hexists$  字段存在,
  6. $hkeys$  获取所有 $key$ ,
  7. $hvals$  获取所有 $value$ ,
  8. $hincrby/hincrbyfloat$ ,
  9. $hsetnx$  当前 $key$ 的 $field$ 不存在时才设置.

2.3 list

保存多个数据,底层使用双向链表实现
内部数据都是string类型,可以进行索引操作,最多保存 $2^{32} - 1$ 个元素

  1. $lpush/rpush$  添加
  2. $lrange/lindex$  获取,可以通过 $0\ \ -1$ 查询全部数据
  3. $llen$  长度
  4. $lpop/rpop$  删除
  5. $blpop/brpop$  周期内等待获取并删除(阻塞)
  6. $lrem$  移除指定数据,$count$ 可以指定删除个数

2.4 set

用于存储大量数据,拥有高效的查询机制。
sethash结构相同,但仅使用 $field$ ,$value$ 为 $nil$

  1. $sadd$  添加
  2. $smembers$  获取全部
  3. $srem$  删除
  4. $scard$  个数
  5. $sismember$  存在
  6. $srandmember$  随机
  7. $spop$  随机并移除
  8. $sinter/sunion/sdiff$  交并差,差具有方向
  9. $sinterstore/sunionstore/sdiffstore$  交并差并存储
  10. $smove$  移动

2.5 sorted_set/zset

保存可排序的数据,在set的基础上添加可排序字段,称为 $score$。
保存的数据空间是 $64$ 位,可以是一个 $double$ 值。

  1. $zadd$ ,
  2. $zrange/zrevrange$  升序/降序,最后添加 $withscores$ 可以显示 $score$ ,
  3. $zrem$ ,
  4. $zrangebyscore/zrevrangebyscore$  条件查询,
  5. $zremrangebyrank/zremrangebyscore$  条件删除,
  6. $zcard/zcount$ ,
  7. $zinterstore/zunionstore$  可以通过 $aggregate$ 指定 $score$ 值得处理方式,
  8. $zrank/zrevrank$  获取排位,
  9. $zscore/zincrby$ $score$  设置.

3. 通用命令

3.1 key

  1. $del/exists/type$  删除/存在/类型,
  2. $expire/pexpire/expireat/pexpireat$  设置周期,后两者指定 $timestamp$ ,
  3. $ttl/pttl$  获取周期,$-2/-1$ 分别代表不存在和永久,
  4. $persist$  转换为永久,
  5. $keys$  查询,
  6. $rename/renamenx$  重命名,后者只有不存在时才会执行,
  7. $sort$ ,
  8. $help\ \ @generic$  查询所有相关命令,

3.2 db

Redis为每个服务提供了 $16$ 个数据库,相互独立。

  1. $select$  切换,
  2. $ping$  测试服务器连接,
  3. $echo$ ,
  4. $move$  如果指定数据库包含同名 $key$ ,则不会移动,
  5. $flushdb/flushall$  清除,
  6. $dbsize$  当前数据库大小查询.

4. Jedis

Redis中的指令名称与Jedis中的方法名相同。

//  连接
    Jedis jedis = new Jedis("127.0.0.1", 6379);
//  操作
    jedis.set("name", "example");
//  关闭
    jedis.close();

$JedisPool$ 对象

public class JedisUtils {
  private static JedisPool jedisPool = null;
  private static String host;
  private static int port;
  private static int maxTotal;
  private static int maxIdle;

  static {
    ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
    host = resourceBundle.getString("redis.host");
    port = Integer.parseInt(resourceBundle.getString("redis.port"));
    maxTotal = Integer.parseInt(resourceBundle.getString("redis.maxTotal"));
    maxIdle = Integer.parseInt(resourceBundle.getString("redis.maxIdle"));
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(maxTotal);
    jedisPoolConfig.setMaxIdle(maxIdle);
    jedisPool = new JedisPool(jedisPoolConfig, host, port);
  }

  public static Jedis getJedis() {
    return jedisPool.getResource();
  }
}

5. Linux下的Redis操作

  1. $redis-server\ \ –port$  更改端口,
  2. $redis-cli\ \ -h\ \ -p$  设置主机和端口,
  3. $redis.conf$ .
# 端口
port 6379
# 保护模式,开启后只能通过本地访问
protected-mode no
# 以守护进程方式启动, docker 环境下启动需设置为 no
daemonize no
# 日志文件名
logfile "6379.log"
# 日志文件路径
dir /data

6. 持久化

利用永久性存储介质保存数据,用于防止数据意外丢失。

  1. RDB 保存当前数据,速度较快,但是不能保证实时性,且当数据较多时会明显变慢,快照形式,
  2. AOF 保存操作,能够实现实时性,日志形式.

6.1 RDB指令

6.1.1 save

通过RDB模式进行持久化,文件默认保存在 $/data/dump.rdb$ 中。执行过程中会阻塞服务器,直到RDB过程完成,不建议在线上环境中使用。

# 文件名
dbfilename "dump-6379.rdb"
# 文件路径
dir /data
# 是否压缩(LZF压缩)
rdbcompression no
# 是否进行数据校验
rdbchecksum no

6.2.2 bgsave

后台进行持久化,会调用 $fork$ 函数生成一个子进程,可以在日志文件中查询记录。

# 后台保存出错时是否停止操作,默认开启
stop-writes-on-bgsave-error yes

6.2.3 自动

# 如果在10秒内有2个key改变,就执行bgsave
bgsave 10 2

6.2 RDB特殊启动模式

  1. 全量复制
  2. $debug\ \ reload$  服务器运行过程中重启,
  3. $shutdown\ \ save$  关闭服务器时保存.

6.3 AOF

写指令会先被发送到AOF写命令刷新缓冲区,生成 $.aof$ 文件。

# 开启AOF
appendonly yes
# AOF策略,always/everysec/no
appendfsync everysec
# 文件名
appendfilename "appendonly-6379.aof"
# 路径
dir /data

AOF策略:

  1. $always$  每次,通过 $fork$ 生成子进程进行,
  2. $everysec$  每秒,通过 $fork$ 生成子进程,将操作存储在AOF缓存区中,如果是重写,还会有aof重写缓存区,时间到达后写入,
  3. $no$  系统控制.

6.4 AOF重写

  1. $bgrewriteaof$  后台重写,
  2. 自动
    1. $auto-aof-rewrite-min-size$ 最小重写大小,
    2. $auto-aof-rewirte-percentage$ 自动重写百分比.

执行 $info\ \ Persistence$ 指令可以查询持久化信息。

$$ aof_-current_-size > \\ auto-aof-rewrite-min-size \times \\ \frac{aof_-current_-size\ - \ aof_-base_-base_-size}{aof_-base_-size} >= \\ auto-aof-rewrite-percentage $$

时触发自动重写。

6.5 对比

  RDB AOF
占用存储空间 小(压缩) 大(重写)
存储速度
恢复速度
数据安全性 会丢失 依据策略
资源消耗
启动优先级

7. 事务

类似于DBMS中的事务,命令在事务内并不会立即执行。

  1. $multi$  开启事务,
  2. $exec$  执行事务,
  3. $discard$  取消事务.

7.1 流程

  1. 服务器接收到指令后会判断是否处于事务状态,
  2. 接收到 $multi$ 指令后会创建队列,转换为事务状态,
  3. 之后再接收指令时,会把指令加入队列,
  4. 接收到 $exec$ 指令时,依次执行队列中的指令,
  5. 如果接收到 $discard$ 指令,销毁队列.

如果命令中存在语法错误,则整个事务都会被销毁。

7.2 锁

  1. $watch$  添加监视锁,如果在 $exec$ 之前发生了变化则终止事务执行($exec$ 返回 $nil$),监视锁要在开启事务之前设置,
  2. $unwatch$  取消全部监视锁,
  3. $setnx$  添加公共锁,若有返回值则设置失败,
  4. $del$  删除公共锁,
  5. $expire/pexpire$  为锁添加时效,防止长时间被占用.

8. 删除策略

在使用 $expire$ 等操作设置数据周期时,会将数据地址和过期时间以哈希键值对的方式存储在 $expires$ 块中,Redis的每个数据库都有一个 $expires$ 区域。

  1. 定时删除,
    到达指定时间后同时删除存储区和 $expires$ 区中的数据,对CPU负担较大,会抢占当前线程.
  2. 惰性删除,
    到达指定时间后不删除,下次访问时再进行删除。采取此种策略时,每个访问都要调用一次 $expireIfNeeded()$ 函数,用于判断数据是否过期。占用较多内存.
  3. 定期删除,
    1. 使用 $info\ \ Server$ 命令可以得到一个 $hz$ 值,代表每秒执行的 $serverCron()$ 函数的次数,轮询服务器,
    2. 该函数调用 $databaseCron()$ 函数,轮询 $expires$,
    3. 在这些块内调用 $activeExpireCycle()$ 函数,每次执行 $\Large\frac{250}{hz}\normalsize ms$,
    4. 随机挑选 $W$ 个 $key$ 进行检测并删除超时数据,如果删除的数量 $>\Large\frac{W}{4}$ ,循环,
    5. 通过 $active_-expire_-cycle_-lookups_-per_-loop$配置可以设置 $W$ 的值,$current_-db$ 记录当前轮询的$expires$ 块.

8.1 逐出算法

Redis在执行命令前会调用 $freeMemoryIfNeeded()$ 函数,用于检测内存是否充足,如果不足,则会删除一些数据,而清理过程中使用的算法称为逐出算法。当逐出算法失败时,会抛出错误信息。

  1. $maxmemory$  设置Redis的最大可支配内存,默认为全部,
  2. $maxmemory-samples$  随机获取数据时每次获取的个数,
  3. $maxmemory-policy$  挑选删除数据的策略,
    1. 检测易失数据($expires$),
      1. $volatile-lru$  最近最久未使用,
      2. $volatile-lfu$  最近最少次数使用,
      3. $volatile-ttl$  最接近过期,
      4. $volatile-random$  随机,
    2. 全库数据,
      1. $allkeys-lru$,
      2. $allkeys-lfu$,
      3. $allkeys-random$,
    3. 不驱逐 $no-enviction$.

通过 $info$ 命令中的 $keyspace$_$hits$ 和 $keyspace$_$misses$ 可以得到命中率。

9. 服务器配置

  1. $daemonize$  守护进程,Docker中要设为 $no$,
  2. $bind$  绑定主机地址,绑定之后只能通过该地址访问,
  3. $port$  端口号,
  4. $database$  数据库数量,
  5. $loglevel\ \ [debug|verbose|notice|warning]$  日志记录级别,默认为 $verbose$ ,生产环境中可以设为 $notice$,
  6. $logfile$  日志文件名,
  7. $maxclients$  最大客户端连接数,默认为无限,
  8. $timeout$  最大闲置时间,$0$ 为关闭该功能,
  9. $include$  导入其他配置.

10. 高级数据类型

10.1 Bitmaps

Bitmaps相当于对string类型进行二进制操作的一个接口。

  1. $getbit$  获取指定偏移量上的bit值,
  2. $setbit$  设置,
  3. $bitop$  对 $key$ 之间进行 $and/or/not/xor$ 操作并保存到指定 $key$ 中,
  4. $bitcount$  统计指定范围内bit为 $1$ 的数量.

10.2 HyperLogLog

统计不重复的数据数,运用了LogLog算法,是一个估算算法,在大量数据时存在误差,估计结果是一个带有 $0.81%$ 标准错误的近似值。每个 $HyperLogLog key$ 只占用最多 $12K$ 内存用于标基数。

  1. $pfadd$  添加,
  2. $pfcount$  统计,
  3. $pfmerge$  合并.

10.3 GEO

计算坐标点之间的距离

  1. $geoadd$  添加,通过经纬度的方式添加,
  2. $geopos$  获取,
  3. $geodist$  计算,可以指定单位,
  4. $georadius$  获取指定范围内符合条件的坐标,
  5. $georadiusbymember$,
  6. $geohash$  获取对应坐标哈希.

11. 主从复制

主服务器为 $master$ ,收集数据(写数据),提供给多台服务器,称为 $slave$ ,用于提供数据(读数据)。一个 $master$ 可以对应多个 $slave$ ,而一个 $slave$ 只能对应一个 $master$ ,一个服务器可以既是 $slave$ 也是 $master$。主从复制即将 $master$ 的数据即时有效的复制到 $slave$ 中。

11.1 连接

$slave$ 连接 $master$ 。

  1. $slaveof\ \ [ip]\ \ [port]$  可以添加在配置文件中,
  2. 发送指令后 $master$ 响应,
  3. 保存 $masterhost$ 和 $masterport$,
  4. 根据保存的信息创建 $socket$,
  5. 周期性地发送 $ping-pong$ 消息,
  6. $auth\ \ password$ 验证授权,
  7. $replconflistening-port\ \ [port]$ 发送监听端口到 $master$,
  8. $master$ 保存监听端口,
  9. $slaveof\ \ no\ \ one$ 断开连接.

11.1.1 授权访问

  1. $master$ 配置文件中设置密码 $requirepass$ $[password]$,
  2. $master$ 客户端发送命令设置密码 $config$ $set$ $requirepass$ $[password]$ / $config$ $get$ $requirepass$,
  3. $slave$ 配置文件设置密码 $masterauth$ $[password]$,
  4. $slave$ 客户端发送命令设置密码 $auth$ $[password]$,
  5. 启动客户端设置密码 $redis-cli$ $-a$ $[password]$.

11.2 数据同步

  1. $slave$ 发送 $psync2$ 请求同步,
  2. $master$ 执行 $bgsave$ ,在第一个 $slave$ 连接时,创建一个命令缓冲区,用于以AOF形式缓冲RDB过程中添加的新命令,
  3. 生成RDB文件,通过 $socket$ 发送给 $slave$,
  4. $slave$ 接收RDB,清空数据并执行RDB恢复,
  5. 发送消息通知恢复完成,请求部分同步,
  6. $master$ 复制缓冲区信息,以AOF形式发送,
  7. $slave$ 执行 $bgrewriteaof$ 恢复部分同步数据.

$1-4$ 称为全量复制,$5-7$ 称为部分复制,复制完成后 $slave$ 端保有 $master$ 端的全部数据以及复制过程接收的数据,$master$ 保有 $slave$ 当前同步的位置。
在进行全量复制时,如果数据量过大,时间过长导致缓冲区中数据溢出,则必须重新进行全量复制,可以通过在配置文件中设置 $repl-backlog-size$ 改变缓冲区大小。
设置 $repl-backlog-size$ 的流程:

  1. 测算 $master$ 和 $slave$ 的重连时长 $second$,
  2. 获取 $master$ 的 $write_-size_-per_-second$,
  3. $repl-backlog-size$ $=$ $2$ $\times$ $second$ $\times$ $write_-size_-per_-second$.

在复制过程中,通过设置 $slave-serve-stale-data$ 来令 $slave$ 关闭对外服务。如果 $slave$ 过多导致 $master$ 带宽不足,应调整结构,最好为树状结构。

11.3 命令传播

在 $master$ 和 $slave$ 建立连接后,应实时保持数据同步。在该阶段内如果发生短时间网络中断,就要进行部分复制,同步短时间内的改变。

11.3.1 部分复制的核心要素

  1. 服务器运行id
    服务器运行时的身份识别码,在多次运行中可以生成多个,是一个由 $40$ 位字符组成的随机十六进制字符串,用于身份识别。使用 $info\ \ Server$ 内可以查询到运行id $run_-id$ 。
  2. $master$ 的复制缓冲区
    命令发送给 $master$ ,$master$ 在将命令同步到 $slave$ 时,还会把命令发送到复制缓冲区中。复制缓冲区是一个队列,由偏移量和字节值组成,每台服务器启动时,如果开启了AOF或者成为 $master$ ,都要创建复制缓冲区。
  3. $master$ 和 $slave$ 间的复制偏移量
    $master$ 通过 $offset$ 记录不同 $slave$ 间传播数据的量。$offset$ 既记录在 $master$ 中,也记录在 $slave$ 中,在每次发送时记录,用于断线后恢复(部分复制)。

11.3.2 流程

  1. $slave$ 发送 $psync2\ \ [run_-id]\ \ [offset]$ ,在首次连接时通过发送 $psync2\ \ ?\ \ -1$ 获取id和偏移量,
  2. $master$ 执行 $bgsave$ 记录偏移量,
  3. 发送 $+FULLRESYNC\ \ [run_-id]\ \ [offset]$ 进行全量复制,再通过 $socket$ 发送RDB文件,
  4. $slave$ 接收到id和偏移量,进行全量复制,
  5. $slave$ 发送 $psync2\ \ [run_-id]\ \ [offset]$,
  6. $master$ 判断id是否匹配,偏移量是否存在于复制缓冲区中,若不存在,重新进行全量复制,
  7. 如果接收到的偏移量与存储的偏移量相同则忽略,不同则发送 $+CONTINUE\ \ [offset]$ ,再通过 $socket$ 发送复制缓冲区中新增的数据,
  8. $slave$ 接收到部分复制的消息,更新偏移量,执行 $bgrewriteaof$ 恢复数据.

11.4 心跳机制

  1. $master$ 心跳指令 $PING$ ,判断 $slave$ 是否在线,默认周期 $10$ 秒,可以通过设置 $repl-ping-slave-period$ 改变,查询 $info\ \ replication$ 可以获取最后一次连接时间间隔,
  2. $slave$ 心跳指令 $REPLCONF\ \ ACK\ \ [offset]$,默认周期 $1$ 秒,用于汇报偏移量和判断 $master$ 是否在线.

$min-slaves-to-write$ 最小写数量,小于就不再写数据,
$min-slaves-max-lag$ 最长延迟时间,若超过这个时间,不再写数据,
$slave-serve-stale-data$ 如果 $slave$ 延迟过大,是否暂时屏蔽程序对 $slave$ 的数据访问,
$slave$ 的数量和延迟由 $REPLCONF ACK$ 命令得到。

11.4.1 流程

接上,命令传播阶段,在该过程中 $master$ 也会发送 $PING$ 指令。

  1. 发送 $REPLCONF\ \ ACK\ \ [offset]$,
  2. $master$ 判断是否处于缓冲区,
  3. 执行全量复制/部分复制,
  4. $slave$ 执行复制.

11.5 内部优化

当数据过多时,每次重启都要进行一次全量复制,内部优化机制可以缓解问题。

  1. $master$ 创建 $mater_-replid$ ,使用 $run_-id$ 生成,发送给所有 $slave$,
  2. $master$ 关闭时执行 $shutdown\ \ save$ ,将 $run_-id$ 和 $offset$ 保存到RDB文件中,使用 $redis-check-rdb\ \ [*.rdb]$ 可以查看 $repl-id$ 和 $repl-offset$,
  3. $master$ 重启后加载RDB,同时加载 $repl-id$ 和 $repl-offset$ ,通过 $info$ 可以查看 $master_-repl_-id$ 和 $master_-repl_-offset$.

12. 哨兵模式

如果当前 $master$ 宕机,需要一个 $slave$ 作为新的 $master$ ,这时需要通知所有 $slave$ 。哨兵( $sentinel$ ) 是一个分布式系统,也是一台Redis服务器,用于监控主从结构中的每台服务器,并通过投票机制选出新的 $master$ ,通常配置奇数个哨兵。

  1. 监控 $master$ 和 $slave$ 是否正常运行,
  2. 如果服务器出现问题,向其他哨兵和客户端发送通知,
  3. 断开 $mater$ 和 $slave$ 的连接,选择一个 $slave$ 作为新的 $master$ ,将其他 $slave$ 连接到新的 $master$ ,并通知客户端新的服务器地址。

$redis-sentinel\ \ [*.conf]$ 启动哨兵,指定配置文件。哨兵在启动后会在配置文件中添加 $myid$ 以及一些主从配置信息,并且会随着状态更新信息。
设置哨兵后可以通过 $redis-cli\ \ -p\ \ [port]$ 连接哨兵服务器,但是不能进行 $get/set$ 等操作,只能进行哨兵的指令,通过 $info$ 的 $Sentinel$ 下可以获取相关信息。

12.1 配置

# 端口
port 26379
# 守护进程模式
daemonize no
# 日志文件名
logfile "26379.log"
# 日志路径
dir /data
# 监控的 master,2 代表当存在 2 台哨兵认为宕机时即判断该
# master 已经宕机,通常设置为哨兵数的一半加一
sentinel monitor mymaster 127.0.0.1 6379 2
# master 多久未响应即为宕机, ms
sentinel down-after-milliseconds mymaster 30000
# 新的 master 的并行同步数
sentinel parallel-syncs mymaster 1
# 同步的最长时间,ms
sentinel failover-timeout mymaster 180000

12.2 原理

12.2.1 监控

  1. 连接 $master$ ,通过 $INFO$ 获取 $run_-id$ 和 $role$ 以及各个 $slave$ 和 $sentinel$ 的信息,建立一个CMD连接,
  2. 获取 $sentinel$ 的状态,通过 $PING$ ,建立起一个通道,用于相互之间传输信息,在之后也会不断发送 $PING$ 用于确认是否在线,
  3. 获取 $slave$ 的信息,通过 $INFO$ ,获取 $run_-id$ , $role$ , $master_-host$ , $master_-port$ , $offest$ 等,同样建立起CMD连接.

12.2.2 通知

$sentinel$ 周期性的给各个服务器发送信息,确认其是否在线,再将取得的信息发送给其他 $sentinel$。

12.2.3 故障转移

  1. 如果一个 $master$ 没有及时响应,$sentinel$ 会通知其他 $sentinel$ ,将其标志为 $SRI_-S_-DOWN$ ,即主观下线。
  2. 其他 $sentinel$ 也会发送信息给该 $master$ ,如果有达到数量的 $sentinel$ 标记其为 $SRI_-S_-DOWN$ ,那么它就会被标记为 $SRI_-O_-DOWN$ ,即客观下线。
  3. $sentinel$ 发送自己的信息,并通过投票机制(接收到信息的先后顺序),直到获得半数以上的票数后,选出的 $sentinel$ 负责选出新的 $master$ 。
  4. $sentinel$ 根据响应速度和与之前 $master$ 断开时间,再通过优先原则(优先级、偏移量和 $run_-id$ 大小)决出新的 $master$ 。
  5. $sentinel$ 通知新的 $master$ 和其他 $slave\ \ master$ 改变的信息。

13. 集群

Redis服务器将所有的存储空间计划切割成 $16384$ 个槽( $slot$ ),每台主机均保存部分槽,在进行 $key$ 存储时,要先计算出其存储的槽。如果在后续有增加主机,则从现有的主机中每台选出部分槽存储在新主机上。
在集群内部,各个数据库之间相互通信,保存各个库中槽的编号数据。当未命中时,数据库可以通过保存的数据给出要查找的 $key$ 所在的数据库。
在集群模式中,如果 $slave$ 下线,不会影响集群,下线 $slave$ 的 $master$ 会将下线状态发送给其他 $master$ 。当 $slave$ 重新上线后,会清除自己和其他 $master$ 的下线状态。
如果 $master$ 下线,$slave$ 会重复连接( 周期为 $1s$ )直到超时,超时后会选出 $slave$ 成为新的 $master$ 。$master$ 重新连接后,新的 $master$ 清除掉下线状态,将其作为 $slave$ 同步。

13.1 配置

使用集群前要更改配置,添加在 $redis.conf$ 文件内。

# 启动集群
cluster-enabled yes
# 集群配置文件,集群开启后会生成在 dir 指定的路径
cluster-config-file node-6379.conf
# 集群超时时间, ms
cluster-node-timeout 30000
# master 连接的 slave 的最小数量
# cluster-migration-barrier 1

13.2 启动

redis-cli --cluster create --replicas [num] [master_host:ip]... [slave_host:ip]

开启集群,可以通过 $–replicas$ 指定内部结构,$num$ 代表一台 $master$ 有 $num$ 台 $slave$ ,后续输入的主从地址要符合结构,先输入所有的 $master$ ,再按序输入所有的 $slave$ 。
在连接集群服务器时,使用 $redis-cli\ \ -c$ ,会将不属于当前服务器管理的槽的数据自动转到其他服务器或者从其他服务器取出当前服务器管理的槽的数据。

13.3 操作

  1. $cluster\ \ nodes$  查看节点信息,
  2. $cluster\ \ replicate\ \ [master_-run_-id]$  切换一个 $slave$ 的 $master$,
  3. $cluster\ \ meet\ \ [host:ip]$  为新节点添加 $master$,
  4. $cluster\ \ forget\ \ [run_-id]$  忽略一个没有槽的节点,
  5. $cluster\ \ failover$  手动故障转移.

14. 解决方案

14.1 缓存预热

Redis服务器部署后,如果发生请求数高,吞吐量大,同步操作频繁,容易发生宕机。

14.1.1 解决

  1. 前置,
    1. 统计高频数据,
    2. 利用LRU构建数据留存队列( $storm/kafka$ ),
  2. 准备,
    1. Redis优先加载高频数据,
    2. 利用分布式技术加快数据读取,
  3. 实施,
    1. 使用脚本触发数据预热,
    2. 使用CDN.

14.2 缓存雪崩

数据库连接量激增导致 $408$, $500$ 等错误页面的出现,客户反复刷新导致流量居高不下。通常是Redis中一个较短时间内大量 $key$ 过期导致。

14.2.1 解决

  1. 更多静态页面
  2. 多级缓存( $Nginx$, $Redis$, $Ehcache$ 等)
  3. 优化SQL语句
  4. 监控服务器性能指标(CPU占用率,内存占用率,平均响应时间,线程数等)
  5. 限流

也可以针对过期问题解决

  1. 切换删除策略(LRULFU等),
  2. 调整有效期,错峰删除(分类,增加随机时间等),
  3. 对于高频数据使用永久 $key$,
  4. 定期维护,分析数据访问量,
  5. 锁.

14.3 缓存击穿

Redis某个高频数据过期导致数据库访问量激增,从而使数据库崩溃。

14.3.1 解决

  1. 统计高频数据
  2. 定时任务,在高峰期前刷新数据有效期
  3. 实时监控,将高频数据延长周期或设置为永久
  4. 错峰删除

14.4 缓存穿透

Redis命中率下降导致数据库崩溃。通常由于数据库中没有对应数据或者Redis未持久化 $null$ 数据导致,也可能是黑客攻击导致。

14.4.1 解决

  1. 缓存 $null$ ,设置短时限
  2. 拦截异常访问/布隆过滤器
  3. 监控Redis命中率,使用黑名单进行防控
  4. 启动防灾业务 $key$ ,如设置加密 $key$ ,拦截不符合规则的访问

14.5 监控

监控工具:

  1. $Cloud\ \ Insight\ \ Redis$,
  2. $Prometheus$,
  3. $Redis-stat$,
  4. $Redis-faina$,
  5. $RedisLive$,
  6. $zabbix$.

监控命令:

  1. $redis-benchmark$ $-h$ $[host]$ $-p$ $[port]$ $-c$ $[connect]$ $-n[request]$,
    $-c$ 指定连接数,$-n$ 指定请求数.
  2. $redis-cli$,
    1. $monitor$,
      开启监控.
    2. $slowlog\ \ get/len/reset$,
      获取慢日志/获取慢日志条目/重置慢日志, 相关配置:
      1. $slowlog-log-slower-than$ 慢查询时间下限,微秒,
      2. $slowlog-max-len$ 慢查询日志长度.

14.5.1 性能

  1. $latency$  响应延迟,
  2. $instantaneous_-ops_-per_-sec$  每秒处理请求数,
  3. $hit\ \ rate$  命中率.

14.5.2 内存

  1. $used_-memory$  内存占用,
  2. $mem_-fragmentation_-ratio$  碎片率,
  3. $evicted_-keys$  由于内存限制而移除的 $key$ 的数量,
  4. $blocked_-clients$  由于阻塞操作( $blpop$ , $brpop$ 等)而阻塞的客户端数量.

14.5.3 活动

  1. $connected_-clients$  客户端连接数,
  2. $connected_-slaves$  连接的$slave$ 数,
  3. $master_-last_-io_-second_-ago$  最近一次交互距今,
  4. $keyspace$  数据库中 $key$ 的数量.

14.5.4 持久化

  1. $rdb_-last_-save_-time$  最近一次RDB时间
  2. $rdb_-changes_-since_-last_-save$  最近一次RDB至今改变的数据量

14.5.5 错误

  1. $rejected_-connections$  客户端数量达到上限后拒绝的连接数
  2. $keyspace_-misses$  未命中次数
  3. $master_-link_-down_-since_-seconds$  主从断连持续时间

Redis笔记