Redis

参考笔记

Redis的安装和启动

centOS7下使用docker安装

先创建挂载的数据卷,如创建 mydata/redis/conf/redis.conf 文件,Redis配置文件(6.2.1)

下载Redis

docker pull redis:6.0.10

启动Redis

docker run -p 6379:6379 --name redis -v /mydata/redis/conf/redis.conf:/usr/local/redis/redis.conf -v /mydata/redis/data:/data -d redis:6.0.10 redis-server /usr/local/redis/redis.conf
解释: -v /usr/local/redis/data:/data # 将数据目录挂在到本地保证数据安全
-v /root/redis/redis.conf:/usr/local/etc/redis/redis.conf # 将配置文件挂在到本地修改方便
# 进入redis容器
docker exec -it redis redis-cli

find / -name 文件名[*.aop][name.*] # 查找文件, / 是从根目录查找也可以指定目录

配置文件

*** NETWORK***#

网络相关配置。

bind

默认情况 bind=127.0.0.1 只能接受本机的访问请求。

不写的情况下,无限制接受任何 ip 地址的访问。

生产环境肯定要写你应用服务器的地址,服务器是需要远程访问的,所以需要将其注释掉

image-20230809205459845

protected-mode

如果开启了protected-mode,那么在没有设定 bind ip 且没有设密码的情况下,Redis 只允许接受本机的响应。

image-20230809205634474

timeout

一个空闲的客户端维持多少秒会关闭,0 表示关闭该功能。即永不关闭。

image-20230809205719057

*** GENERAL***#

通用。

daemonize

是否为后台进程,设置为 yes

守护进程,后台启动。

image-20230809210139287

pidfile

存放 pid 文件的位置,每个实例会产生一个不同的 pid 文件。

image-20230809210204802

loglevel

指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice

image-20230809210218638

logfile

日志文件名称。

image-20230809210231553

database

设定库的数量 默认16,默认数据库为 0,可以使用 SELECT <dbid> 命令在连接上指定数据库 id

image-20230809210242001

SECURITY#

安全。

访问密码的查看、设置和取消。

在命令中设置密码,只是临时的。重启 redis 服务器,密码就还原了。

永久设置,需要在配置文件中进行设置。

LIMITS#

限制。

maxclients

设置 redis 同时可以与多少个客户端进行连接。

默认情况下为 10000 个客户端。

如果达到了此限制,redis 则会拒绝新的连接请求,并且向这些连接请求方发出 max number of clients reached 以作回应。

maxmemory

建议必须设置,否则,将内存占满,造成服务器宕机。

设置 redis 可以使用的内存量。一旦到达内存使用上限,redis 将会试图移除内部数据,移除规则可以通过 maxmemory-policy 来指定。

如果 redis 无法根据移除规则来移除内存中的数据,或者设置了不允许移除,那么 redis 则会针对那些需要申请内存的指令返回错误信息,比如 SET、LPUSH 等。

但是对于无内存申请的指令,仍然会正常响应,比如 GET 等。如果你的 redis 是主 redis( 说明你的 redis 有从 redis ),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。

maxmemory-policy

volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键(最近最少使用)。

allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key

volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键。

allkeys-random:在所有集合 key 中,移除随机的 key

volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key

noeviction:不进行移除。针对写操作,只是返回错误信息。

maxmemory-samples

设置样本数量,LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis 默认会检查这么多个 key 并选择其中 LRU 的那个。

一般设置 3 到 7 的数字,数值越小样本越不准确,但性能消耗越小。

常用操作&&基本数据类型

key操作#

keys *:查看当前库所有 key

exists key:判断某个 key 是否存在

type key:查看你的 key 是什么类型

del key :删除指定的 key 数据

unlink key:根据 value 选择非阻塞删除,仅将 keyskeyspace 元数据中删除,真正的删除会在后续异步操作

expire key 10 :为给定的 key 设置过期时间

ttl key:查看还有多少秒过期,-1表示永不过期,-2表示已过期

select:命令切换数据库

dbsize:查看当前数据库的 key 的数量

flushdb:清空当前库

flushall:通杀全部库

字符串(String)#

String 类型是二进制安全的。意味着 Redisstring 可以包含任何数据。比如 jpg 图片或者序列化的对象。

String 类型是 Redis 最基本的数据类型,一个 Redis 中字符串 value 最多可以是 512M。

set <key><value>:添加键值对

get <key>:查询对应键值

append <key><value>:将给定的 追加到原值的末尾

strlen <key>:获得值的长度

setnx <key><value>:只有在 key 不存在时,设置 key 的值

incr <key>:将 key 中储存的数字值增 1,只能对数字值操作,如果为空,新增值为 1(具有原子性

decr <key>:将 key 中储存的数字值减 1,只能对数字值操作,如果为空,新增值为 -1

incrby/decrby <key><步长>:将 key 中储存的数字值增减。自定义步长

mset <key1><value1><key2><value2> :同时设置一个或多个 key-value

mget <key1><key2><key3>...:同时获取一个或多个 value

msetnx <key1><value1><key2><value2>... :同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在

getrange <key><起始位置><结束位置>:获得值的范围

setrange <key><起始位置><value>:用 覆写 所储存的字符串值

setex <key><过期时间><value>:设置键值的同时,设置过期时间,单位秒。

getset <key><value>:以新换旧,设置了新值同时获得旧值。

列表(List)#

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。

img

基本命令#

lpush/rpush <key><value1><value2><value3> ....: 从左边/右边插入一个或多个值。

lpush k1 v1 v2 v3
lrange k1 0 -1
输出:v3 v2 v1
rpush k1 v1 v2 v3
rrange k1 0 -1
输出:v1 v2 v3

lpop/rpop <key>:从左边/右边吐出一个值。值在键在,值光键亡。

rpoplpush <key1><key2>:从 列表右边吐出一个值,插到 列表左边。

lrange <key><start><stop>:按照索引下标获得元素(从左到右)

lrange mylist 0 -1 0:左边第一个,-1右边第一个,(0 -1表示获取所有)

lindex <key><index>:按照索引下标获得元素(从左到右)

llen <key>:获得列表长度

linsert <key> before/after <value><newvalue>:在 的前面/后面插入 插入值

lrem <key><n><value>:从左边删除 nvalue(从左到右)

lset<key><index><value>:将列表 key 下标为 index 的值替换成 value

数据结构#

List 的数据结构为快速链表 quickList

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表。

它将所有的元素紧挨着一起存储,分配的是一块连续的内存。

当数据量比较多的时候才会改成 quicklist

因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针 prevnext

Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

img

Set(集合)#

Set 对外提供的功能与 List 类似列表的功能,特殊之处在于 Set 是可以 自动排重 的,当需要存储一个列表数据,又不希望出现重复数据时,Set** 是一个很好的选择,并且 Set 提供了判断某个成员是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的。

RedisSetString 类型的无序集合。它底层其实是一个 valuenullhash 表,所以添加,删除,查找的复杂度都是 O(1)

一个算法,随着数据的增加,执行时间的长短,如果是 O(1),数据增加,查找数据的时间不变。

基本命令#

sadd <key><value1><value2> ..... :将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略

smembers <key>:取出该集合的所有值。

sismember <key><value>:判断集合 是否为含有该 值,有返回 1,没有返回 0

scard<key>:返回该集合的元素个数。

srem <key><value1><value2> ....:删除集合中的某个元素

spop <key>:随机从该集合中吐出一个值

srandmember <key><n>:随机从该集合中取出 n 个值,不会从集合中删除

smove <source><destination>value:把集合中一个值从一个集合移动到另一个集合

sinter <key1><key2>:返回两个集合的交集元素

sunion <key1><key2>:返回两个集合的并集元素

sdiff <key1><key2>:返回两个集合的差集元素(key1 中的,不包含 key2 中的)

数据结构#

Set 数据结构是字典,字典是用哈希表实现的。

Hash(哈希)#

Redis hash 是一个键值对集合。

Redis hash 是一个 String 类型的 fieldvalue 的映射表,hash 特别适合用于存储对象。

基本命令#

hset <key><field><value>:给 集合中的 键赋值

hget <key1><field>:从 集合 取出 value

hmset <key1><field1><value1><field2><value2>...: 批量设置 hash 的值

hexists <key1><field>:查看哈希表 key 中,给定域 field 是否存在

hkeys <key>:列出该 hash 集合的所有 field

hvals <key>:列出该 hash 集合的所有 value

hincrby <key><field><increment>:为哈希表 key 中的域 field 的值加上增量 1 -1

hsetnx <key><field><value>:将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在

数据结构#

Hash 类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。

field-value 长度较短且个数较少时,使用 ziplist,否则使用 hashtable

Zset(有序集合)#

Redis 有序集合 zset 与普通集合 set 非常相似,是一个没有重复元素的字符串集合。

不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的。

因为元素是有序的,所以可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。

访问有序集合的中间元素也是非常快的,因此能够使用有序集合作为一个没有重复成员的智能列表。

基本命令#

zadd <key><score1><value1><score2><value2>…:将一个或多个 member 元素及其 score 值加入到有序集 key 当中

zrange <key><start><stop> [WITHSCORES] :返回有序集 key 中,下标在 之间的元素

当带 WITHSCORES,可以让分数一起和值返回到结果集

zrangebyscore key min max [withscores] [limit offset count]:返回有序集 key 中,所有 score 值介于 minmax 之间(包括等于 minmax )的成员。有序集成员按 score 值递增(从小到大)次序排列。

zrevrangebyscore key max min [withscores] [limit offset count] :同上,改为从大到小排列

zincrby <key><increment><value>:为元素的 score 加上增量

zrem <key><value>:删除该集合下,指定值的元素

zcount <key><min><max>:统计该集合,分数区间内的元素个数

zrank <key><value>:返回该值在集合中的排名,从 0 开始。

数据结构#

SortedSet(zset)Redis 提供的一个非常特别的数据结构,一方面它等价于 Java 的数据结构 Map<String, Double>,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过 score 的范围来获取元素的列表。

zset 底层使用了两个数据结构

  • hashhash 的作用就是关联元素 value 和权重 score,保障元素 value 的唯一性,可以通过元素 value 找到相应的 score
  • 跳跃表,跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表

Redis新数据类型#

  • bitmaps
  • ** HyperLogLog(基数统计)**

Redis的订阅与发布

Redis 发布订阅( pub/sub )是一种消息通信模式:发送者( pub )发送消息,订阅者( sub )接收消息。

Redis 客户端可以订阅任意数量的频道。

订阅:
subscribe channel [channel]
发布:
publish channel message
  1. 客户端可以订阅频道
    img
  2. 当给这个频道发布消息后,消息就会发送给订阅的客户端
    img

Redis 事务和锁机制

Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序执行。事务在执行的过程中,不会备其他的客户端发送的请求打断

Redis 事务的主要作用就是串联多个命令防止别的命令插队

  • multi 开启事务
    image-20230724223818381
  • exec # 事务执行 执行组队阶段
  • ** discard # 放弃组队,事务不执行**
127.0.0.1:6379> multi #开启事务,组队阶段
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED # 进入事务队列
127.0.0.1:6379(TX)> exec # 事务执行 执行组队阶段
1) OK
127.0.0.1:6379>
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> discard # 放弃组队,事务不执行
OK
127.0.0.1:6379>

组队阶段,任何一个执行失败,执行时所有的队列都会取消,不会回滚

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.

组队阶段的命令全部执行成功,执行整个队列时某个命令失败,只有这个错误命令会失败

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> incr k1 # k1不是数字类型,所以自增失败
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK

悲观锁#

每次读取数据都会认为别人会对数据进行修改,所以在每次拿数据的时候都会上锁,只有拿到锁的人才能访问/操作数据。传统数据库中这种机制很多,如行锁、表锁、读锁、写锁等,都是在操作之前先上锁,释放锁之后别人才能操作

缺点:效率低,只能一个一个操作

乐观锁#

每次读取数据都认为别人不会修改数据,所以不会上锁,但是在进行数据更新的时候会判断在此期间有没有别人更新数据。

在表中添加一个控制版本号的字段,所有人都可以得到这个版本的数据,一旦有人修改版本号就会迭代,下次操作的人要对版本号进行比对,版本号相同可以操作,不同则不能进行

优点:适用于多读应用类型,可以提高吞吐量

场景:抢票

Watch、unwatch#

在执行 multi 之前,先执行 watch key1 [key2],可以监视一个(或多个 )key 。如果在事务执行之前这个 key 被其他命令所改动,那么事务将被打断。

取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后,EXEC 命令或 DISCARD 命令先被执行,那么就不需要再执行 UNWATCH

Redis 事务的三个特性#

1、单独的隔离操作

事务中的所有命令都会序列化、按顺序的执行。事务在执行的过程中,不会被其他的客户端发送来的命令请求打断

2、没有隔离级别的概念

事务队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被执行

3、不保证原子性

事务中如果有一条命令执行失败,其后的命令依然会被执行,没有回滚

持久化

RDB#

在指定的时间间隔内将内存中的数据集快照写入磁盘, 即 Snapshot 快照,恢复时是将快照文件直接读到内存里。默认文件名 dump.rdb

image-20230809235911402

Redis 会单独创建一个子进程(fork)来进行持久化。

先将数据写入到一个临时文件中,待持久化过程完成后,再将这个临时文件内容覆盖到 dump.rdb

整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效。

RDB 的缺点是最后一次持久化后的数据可能丢失

Fork#

  • 作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
  • Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,出于效率考虑,Linux 中引入了 写时复制技术
  • 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程

配置#

RDB 默认配置文件名

image-20230810000258062

rdb 文件的保存路径可以修改。默认为 Redis 启动时命令行所在的目录下。

image-20230810000407120

stop-writes-on-bgsave-error

即当 redis 无法写入磁盘,关闭 redis 的写入操作。

image-20230810000530021

rdbcompression 持久化的文件是否进行压缩存储。

image-20230810000627478

rdbchecksum 完整性的检查,即数据是否完整性、准确性。

image-20230810000703055

save 表示写操作的次数。

格式:save 秒 写操作次数
当每秒 key 的变化达到指定的量开始持久化

image-20230810000830263

总的来说就是,rdb 的过程就是 通过创建一个 子进程(fork)来创建一个 临时文件,把临时文件替换rdb文件,使用的是写时复制技术

优点#

  • 适合大规模的数据恢复;
  • 对数据完整性和一致性要求不高更适合使用;
  • 节省磁盘空间;
  • 恢复速度快。

缺点#

  • Fork 的时候,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑;
  • 虽然 Redisfork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能;
  • 在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改

备份恢复#

  • 关闭Redis
  • 把备份的文件直接拷贝到Redis的工作目录
  • 启动Redis,备份数据会直接加载

AOF(Append only File)#

以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件Redis 启动之初会读取该文件重新构建数据,换言之,如果 Redis 重启就会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF 持久化流程#

  • 客户端的请求写命令会被 append 追加到 AOF 缓冲区内;
  • AOF 缓冲区根据 AOF 持久化策略 [always,everysec,no] 将操作 sync 同步到磁盘的 AOF 文件中;
  • AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 Rewrite 重写,压缩 AOF 文件容量;
  • Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的。

配置#

AOF 默认不开启 AOF 和 RDB 同时开启以AOF为准

image-20230810003520158

AOF 同步频率设置

  • appendfsync always

始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好。

  • appendfsync everysec

每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。

  • appendfsync no

Redis 不主动进行同步,把同步时机交给操作系统。

image-20230810004227118

Rewrite 压缩

AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令 bgrewriteaof

image-20230810013953118

优点#

  • 备份机制更稳健,丢失数据概率更低;
  • 可读的日志文本,通过操作 AOF 稳健,可以处理误操作。

缺点#

  • 比起 RDB 占用更多的磁盘空间;
  • 恢复备份速度要慢;
  • 每次读写都同步的话,有一定的性能压力;
  • 存在个别 Bug,造成不能恢复。

异常恢复#

在Redis的data文件加下:redis-check-aof --fix appendonly.aof

问题:

使用docker安装的 Redis镜像挂载后 没有办法在容器外部使用,容器内部在 data文件下可以使用

但是aof文件出错后容器无法启动。

选择#

官方推荐两个都启用。

如果对数据不敏感,可以选单独用 RDB

不建议单独用 AOF,因为可能会出现 Bug

如果只是做纯内存缓存,可以都不用。

主从复制

主机数据更新后根据配置和策略, 自动同步到备机的 master/slaver 机制,Master 以写为主,Slaver 以读为主。

  1. 读写分离,性能扩展
  2. 容灾快速恢复
  3. 一主多从(主服务器宕机,从服务器选举出新的主服务器)

image-20230810015357305

搭建一主两从#

使用docker-compose

**docker-compose.yml **文件

version: "3.1"
services:
redis1:
image: redis:6.2.1
# 开机自启动
restart: always
# 容器名
container_name: redis1
environment:
- TZ=Asia/Shanghai
ports:
- 7001:6379
# 容器卷挂载,配置文件以及数据,需要创建对应的 文件夹以及文件
volumes:
- /mydata/redis_cluster/redis1/conf/redis.conf:/usr/local/redis/redis.conf
- /mydata/redis_cluster/redis1/data:/data
command: [ "redis-server", "/usr/local/redis/redis.conf" ]
redis2:
image: redis:6.2.1
restart: always
container_name: redis2
environment:
- TZ=Asia/Shanghai
ports:
- 7002:6379
volumes:
- /mydata/redis_cluster/redis2/conf/redis.conf:/usr/local/redis/redis.conf
- /mydata/redis_cluster/redis2/data:/data
links:
- redis1:master
command: [ "redis-server", "/usr/local/redis/redis.conf" ]
redis3:
image: redis:6.2.1
restart: always
container_name: redis3
environment:
- TZ=Asia/Shanghai
ports:
- 7003:6379
volumes:
- /mydata/redis_cluster/redis3/conf/redis.conf:/usr/local/redis/redis.conf
- /mydata/redis_cluster/redis3/data:/data
links:
- redis1:master
command: [ "redis-server", "/usr/local/redis/redis.conf" ]

后台启动 docker-compose up -d开放服务器/虚拟机对应的端口

查看主机运行情况

info replication
image-20230810032324322

数据同步

slaveof <ip><port>
# 成为某个实例的从服务器
# 主节点可以进行读写操作,从节点只能进行读操作
# 使用docker 时这里的ip 地址 要使用容器内部的ip地址 查看 容器元信息
docker inspect 容器id

搭建成功

一主二仆#

1、当从服务器连接上主服务器之后,从服务器 向主服务器发送数据同步的消息

2、主服务器接收到从服务器发送的同步消息,将主服务器的数据进行持久化(RDB),把RDB文件发送给从服务器,从服务器从RDB文件读取数据

3、之后当主服务器进行写操作后,主动同步从服务器

薪火相传#

上一个 slave 可以是下一个 slavemasterslave 同样可以接收其他 slave的连接和同步请求,那么该 slave 作为了链条中下一个的 master,可以有效减轻 master 的写压力,去中心化降低风险。

slaveof <ip><port>

中途变更转向:会清除之前的数据,重新建立拷贝最新的。

当某个 slave 宕机,后面的 slave 都没法备份。

即当主机挂掉,从机还是从机,但是无法继续写数据。

反客为主#

当一个 master 宕机后,后面的 slave 可以立刻升为 master,其后面的 slave 不用做任何修改。

slaveof no one

哨兵模式#

反客为主的自动版,即能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

  1. 创建 sentinel.conf 文件
/opt/etc/sentinel.conf
  1. 配置哨兵
sentinel monitor mymaster 172.16.88.168 6379 1
# mymaster:监控对象起的服务器名称
# 1:至少有多少个哨兵同意迁移的数量。
  1. 启动哨兵(需要进入容器启动)
redis-sentinel /opt/etc/sentinel.conf

image-20230810172405672

主机挂掉,会从机选举中产生新的主机。选举的规则。

选举规则#

  • 根据优先级别,slave-priority/replica-priority,优先选择优先级靠前的。
    image-20230810172602580
  • 根据偏移量,优先选择偏移量大的。
  • 根据 runid,优先选择最小的服务。

复制延时#

由于所有的写操作都是先在 master 上操作,然后同步更新到 slave 上,所以从 master 同步到 slave 从机有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,slave 机器数量的增加也会使这个问题更加严重。

复制原理#

  • slave 启动成功连接到 master 后会发送一个 sync 命令(同步命令)。
  • master 接到命令启动后台的存盘进程,对数据进行持久化操作,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master 将传送整个数据文件(rdb)到 slave,以完成一次完全同步。
  • 当主服务进行写操作后,和从服务器进行数据同步。
  • 全量复制:而 slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制:master 继续将新的所有收集到的修改命令依次传给 slave,完成同步。
  • 只要是重新连接 master,一次完全同步(全量复制)将被自动执行。

集群

容量不够,redis 如何进行扩容?

并发写操作, redis 如何分摊?

主从模式,薪火相传模式,主机宕机,导致 ip 地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。

解决方法:

  • 代理主机( 之前
    img
  • 无中心化集群配置(redis3.0)
    img

Redis 集群实现了对 Redis 的水平扩容,即启动 NRedis 节点,将整个数据库分布存储在这 N 个节点中,每个节点存储总数据的 1/N

Redis 集群通过分区(partition)来提供一定程度的可用性(availability),即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

应用问题解决

缓存穿透#

img

现象#

key 对应的数据在数据源并不存在,每次针对此 key 的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。

比如用一个不存在的用户 id 获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

造成:

  1. 应用服务器压力变大。
  2. redis 命中率下降 ⟶⟶ 查询数据库 。

如何解决#

  • 对空值缓存

    如果一个查询返回的数据为空(不管是数据是否不存在),仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟。

  • 设置可访问的名单(白名单):

    使用 bitmaps 类型定义一个可以访问的名单,名单 id 作为 bitmaps 的偏移量,每次访问和 bitmap 里面的 id 进行比较,如果访问 id 不在 bitmaps 里面,进行拦截,则不允许访问。

  • 采用布隆过滤器

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

    布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

    将所有可能存在的数据哈希到一个足够大的 bitmaps 中,一个一定不存在的数据会被这个 bitmaps 拦截掉,从而避免了对底层存储系统的查询压力。

  • 进行实时监控

    当发现 Redis 的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

缓存击穿#

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

  1. 数据库访问压力瞬间增大。
  2. redis 中没有出现大量 key 过期,redis 正常运行。
  3. (即某个经常访问的 key 过期,突然有大量访问这个数据)

img

如何解决#

  • 预先设置热门数据

    redis 高峰访问之前,把一些热门数据提前存入到 redis 里面,加大这些热门数据 key 的时长。

  • 实时调整

    现场监控哪些数据热门,实时调整 key 的过期时长。

  • 使用锁

缓存雪崩#

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

缓存雪崩与缓存击穿的区别在于这里针对很多 key 缓存,前者则是某一个 key

  1. 数据库压力变大。
  2. 即极少的时间段,查询大量 key 的集中过期情况。

如何解决#

  • 构建多级缓存架构

    nginx 缓存 + redis 缓存 + 其他缓存(ehcache等)

  • 使用锁或队列:

    用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况。

  • 设置过期标志更新缓存:

    记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际 key 的缓存。

  • 将缓存失效时间分散开:

    比如我们可以在原有的失效时间基础上增加一个随机值,比如 1~5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

作者:Bikakaso

出处:https://www.cnblogs.com/Bikakaso/p/17621787.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Bikakaso  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示