本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

Redis

1) 常用 NoSQL 应用特点

1.1) Ehcache

  • 优点
    • 基于 java
    • 基于 JVM 缓存
    • 简单, 轻巧, 方便
  • 缺点:
    • 官方不支集群分布式

1.2) Memcache

  • 优点
    • 简单 Key - Value 存储
    • 内存使用率高
    • 多线程, CPU 利用效率高
  • 缺点
    • 不支持持久化

1.3) Redis

  • 优点
    • 数据结构丰富
    • 持久化
  • 缺点
    • 单线程

2) Redis conf 常用配置

  • port
  • bind 0.0.0.0
  • daemonize no/yes : 后台启动, Systemd 不需要
  • requirepass password
  • dir : Redis 工作(数据)目录
  • pidfile
  • logfile
  • databases 16

3) Redis 基本数据类型

更全的 Command 参考官方文档: https://redis.io/commands

展开查看

* common
* SELECT index : 切换数据库, config 默认 16 个
* FLUSHDB
* FLUSHALL
* key
* KEYS *xoxo*
* TYPE key
* string
* SET key value [EX second]
* SETNX
* GET key
* DEL key
* TTL key
* EXPIRE key second
* APPEND key value
* STRLEN key
* INCR key
* DECR key
* INCRBY key value
* DECRBY key value
* GETRANGE key start end
* SETRANGE key index value
* MSET k1 v1 k2 v2
* MSETNX
* MGET k1 k2
* hash
* HSET name key value
* HGET name key
* HMSET name k1 v1 k2 v2
* HMGET name k1 k2
* HGETALL name
* HKEYS name
* HVALS name
* HLEN name
* HINCRBY name key value
* HINCRBYFLOAT name key value
* HDECRBY name key value
* HDECRBYFLOAT name key value
* HEXISTS name key
* HDEL name key
* list
* LPUSH/RPUSH name v1 v2...
* LRANGE name start end
* LPOP/RPOP name
* LINDEX name index
* LLEN name
* LSET name index value
* LGET name index
* LINSERT name BEFORE/AFTER value target_value
* LREM name count value
* LTRIM name start end
* DEL name
* set
* SADD name v1 v2...
* SMEMBERS name
* SCARD name
* SISMEMBER name value
* SREM name value
* SPOP name [num]
* SRANDMEMBERS name [num]
* SMOVE name name2 value
* SDIFF name name2 : 差集(name - name2)
* SINTER name name2 : 交集
* SUNION name name2 : 并集
* zset(score/sorted set)
* ZADD name score v1 score2 v2...
* ZRANGE name start end [withscores]
* ZRANK name value : 排名(start of 0)
* ZCARD name

4) RDB VS AOF

  • RDB
    1. 快照式存储
    2. 每隔一个特定时间备份
    3. 效率高
    4. 因为非同步备份, 容易在宕机时丢数据
  • AOF
    1. 单文件存储
    2. 有每秒备份/同步备份两种模式
    3. 每隔一段时间会整理碎片以及压缩
    4. 效率相比 RDB 较低
    5. 数据丢失率更低

在 Redis 老版本中 Bug 会引起 AOF 备份内容和实际内容有出入问题;
可根据需求将 RDB 和 AOF 结合使用解决持久化风险问题.

5) Redis 主从/读写分离

Redis 的主从复制即读写分离, 分为 Master(写) 和 Slave(读) 两种角色, 原理图大致如下:

  • Master 在启动时将 RDB 持久化文件同步到 Slave 上然后做批量恢复节约资源
  • 启动后 Master 上产生的写入将实时同步给 Slave

请开启 Master 的持久化! 否则当 Master 宕机重启时, 因无持久化文件会同步将 Slave 上数据清空!

5.1) Redis 主从复制扩展方案

一般一个 Master 只用两个 Slave 节点, Slave 节点过多会导致主节点产生网络 IO 过大.

5.2) Redis 无磁盘复制方案

在主从复制时, 如果 Redis 实例所在机器磁盘 IO 吞吐量较低时, 可以采用此方案, 将磁盘 IO 压力转移至 RAM.

#repl-diskless-sync no # 开启
repl-diskless-sync yes

#repl-diskless-sync-delay 5 # 同步前等待时间(S), 等待客户端连接
repl-diskless-sync-delay 5

6) Redis 缓存过期机制

  • 主动删除: 根据配置中的 hz 参数(1S 扫描次数), 扫描 Redis 中的过期 Key, 主动删除
    • 相对于占用 CPU 资源
  • 惰性删除: 当客户端访问到过期 Key 时才删除
    • 相对于占用内存资源
  • 内存淘汰: 根据配置中设置的内存淘汰机制删除 Key

7) Redis 哨兵

Redis 的主从复制时, 为了保证高可用, 引入了 redis-sentinel 哨兵进程; 使可以在 Master 节点宕机时, 将 Slave 升级为 Master.
注意事项:

  • 若 Slave 升级为新 Master, 原 Master 即使上线也会变成 Slave 只提供读取操作.
  • 哨兵一般集群部署至少 3 个节点

7.1) SpringBoot 整合哨兵模式

spring:
  redis:
    database: 0
    password: passwd
    sentinel:
      master: master
      nodes: 10.0.96.200:26379,10.0.96.201:26379,10.0.96.202:26379
      #password:

8) Redis Cluster 集群

Redis 集群至少要有 3 个 Master 节点, 常用架构如下:

config 基本配置如下:

# Auth password
requirepass passwd
# Cluster
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 5000
# AOF
appendonly yes

启动各个实例的 Redis-Server 后使用 redis-cli 创建集群

# redis-cli -a 密码 --cluster create 主节点 1 主节点 2... 从节点 1 从节点 2... --cluster-replicas 每个节点中从节点数量
redis-cli -a passwd --cluster create 10.0.96.150:6379 10.0.96.151:6379 10.0.96.154:6379 10.0.96.152:6379 10.0.96.153:6379 10.0.96.155:6379 --cluster-replicas 1

运行后得到如下确认提示

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.0.96.152:6379 to 10.0.96.150:6379
Adding replica 10.0.96.153:6379 to 10.0.96.151:6379
Adding replica 10.0.96.155:6379 to 10.0.96.154:6379
M: cf946565d99c6a9231ab94e771d4970088853f58 10.0.96.150:6379
   slots:[0-5460] (5461 slots) master
M: 6ad9624f0901ddc91546420844557633c3ba2d77 10.0.96.151:6379
   slots:[5461-10922] (5462 slots) master
M: 457564f62eff029fc38ccbac72fafdce817de100 10.0.96.154:6379
   slots:[10923-16383] (5461 slots) master
S: c3cc750a9706850ed14fab9fcca7ba3b3de98ed6 10.0.96.152:6379
   replicates cf946565d99c6a9231ab94e771d4970088853f58
S: aad4d73437964fab0c9a950c48fff133cb2b37b1 10.0.96.153:6379
   replicates 6ad9624f0901ddc91546420844557633c3ba2d77
S: 9d6f3ec40c372f86a8f13e7f4e65effa2bc19def 10.0.96.155:6379
   replicates 457564f62eff029fc38ccbac72fafdce817de100
Can I set the above configuration? (type 'yes' to accept): 

yes 即可开始创建, 接着我们随便 check 一个节点是否成功:

redis-cli -a passwd --cluster check 10.0.96.150:6379
>>> Performing Cluster Check (using node 10.0.96.150:6379)
M: cf946565d99c6a9231ab94e771d4970088853f58 10.0.96.150:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 9d6f3ec40c372f86a8f13e7f4e65effa2bc19def 10.0.96.155:6379
   slots: (0 slots) slave
   replicates 457564f62eff029fc38ccbac72fafdce817de100
M: 457564f62eff029fc38ccbac72fafdce817de100 10.0.96.154:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: aad4d73437964fab0c9a950c48fff133cb2b37b1 10.0.96.153:6379
   slots: (0 slots) slave
   replicates 6ad9624f0901ddc91546420844557633c3ba2d77
M: 6ad9624f0901ddc91546420844557633c3ba2d77 10.0.96.151:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: c3cc750a9706850ed14fab9fcca7ba3b3de98ed6 10.0.96.152:6379
   slots: (0 slots) slave
   replicates cf946565d99c6a9231ab94e771d4970088853f58
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

完成

8.1) Redis Cluster Slots 槽

在创建集群的时候会有一些 Slots 相关的输出, 如:

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383

Redis 默认会有 16384 个槽, 均匀的分配给集群中的每个主节点.
当往集群中插入 Key 时, 会拿 Key 做 Hash, 接着做取模 16384 的操作, 取模后会得到 Slot 值, 然后插入该条数据至拥有此 Slot 的节点上.

9) RedisTemplate 序列化问题

存入 Redis 中的数据含有多余字符问题解决方案:

9.1) 修改 RedisTemplate 默认序列化器

  @Bean
  public RedisTemplate redisTemplate(RedisTemplate redisTemplate) {
    RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringSerializer);
    redisTemplate.setValueSerializer(stringSerializer);
    redisTemplate.setHashKeySerializer(stringSerializer);
    redisTemplate.setHashValueSerializer(stringSerializer);
    return redisTemplate;
  }

9.2) 改用 StringRedisTemplate

可以将 RedisTemplate 改用继承了它的 StringRedisTemplate, 具体可以康康源码哈和上一种解决方案差差不多.