第三章 Redis相关知识

一、Redis的优点

  1. 数据类型丰富。
  2. 支持数据磁盘持久化存储。
  3. 支持主从。
  4. 支持分片。

二、为什么Redis能这么快?

  1. Redis的使用能达到100000+QPS(QPS即Query Per Second,每秒内查询次数)。
  2. 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。
  3. 数据结构简单,对数据操作也简单。
  4. 采用单线程,单线程也能处理高并发请求,可以通过启动多实例实现多核操作,Redis使用单线程的原因是,省去了很多上下文切换线程的时间(多线程切换上下文的时间远远超过了读取数据所使用的时间)。
  5. 使用多路I/O服用模型,非阻塞I/O 。

三、常用的Redis存储结构

1. 字符串(String)

常用语法:

set key value [expiration EX seconds|PX milliseconds] [NX|XX]

get key
127.0.0.1:6379>SET name "zhangsan"
OK
	
127.0.0.1:6379>GET "name"
"zhangsan"

2. 哈希(Hash)

  1. Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
  2. Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。

常用语法:

hmset key field value [field value ...]

hgetall key

举个栗子:

127.0.0.1:6379> HMSET student name "zhangsan" age 11 sex 1
OK
127.0.0.1:6379> HGETALL student
1) "name"
2) "zhangsan"
3) "age"
4) "11"
5) "sex"
6) "1"

3. 列表(List)

  1. Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
  2. 一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

常用命令:

lpush key value [value ...]

lrange key start stop

举个栗子:

127.0.0.1:6379> LPUSH mate zhangsan
(integer) 1
127.0.0.1:6379> LPUSH mate lisi
(integer) 2
127.0.0.1:6379> LPUSH mate wangwu
(integer) 3
127.0.0.1:6379> LRANGE mate 0 2
1) "wangwu"
2) "lisi"
3) "zhangsan"

4. 集合(Set)

  1. Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据
  2. Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
  3. 集合中最大的成员数为 2^32 - 1(4294967295, 每个集合可存储40多亿个成员)。

常用命令:

sadd key member [member ...]

smembers key

举个栗子:

127.0.0.1:6379> sadd color red
(integer) 1
127.0.0.1:6379> sadd color blue
(integer) 1
127.0.0.1:6379> sadd color white
(integer) 1
127.0.0.1:6379> sadd color yellow grew black
(integer) 3
127.0.0.1:6379> smembers color
1) "blue"
2) "white"
3) "red"
4) "grew"
5) "black"
6) "yellow"

4. 有序集合(Sorted Set)

  1. Redis 有序集合和集合一样也是 String 类型元素的集合,且不允许重复的成员
  2. 不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
  3. 有序集合的成员是唯一的,但分数(score)却可以重复。
  4. 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1(4294967295, 每个集合可存储40多亿个成员)。

常用命令:

zadd key [NX|XX] [CH] [INCR] score member [score member ...]

zrange key start stop [WITHSCORES]

举个栗子:

127.0.0.1:6379> zadd food 1 apple
(integer) 1
127.0.0.1:6379> zadd food 2 apple
(integer) 0 #未添加新成员,apple对应的分数改为2
127.0.0.1:6379> zadd food 1 banana
(integer) 1
127.0.0.1:6379> zadd food 2 banana
(integer) 0 #添未添加新成员,banana对应的分数改为2
127.0.0.1:6379> zadd food 3 banana
(integer) 0 #添未添加新成员,banana对应的分数改为3
127.0.0.1:6379> zadd food 3 peach
(integer) 1
127.0.0.1:6379> zadd food 2 fish
(integer) 1
127.0.0.1:6379> zrange food 0 3
1) "apple"
2) "fish"
3) "banana"
4) "peach"
127.0.0.1:6379> zrange food 0 3 withscores
1) "apple"
2) "2"
3) "fish"
4) "2"
5) "banana"
6) "3"
7) "peach"
8) "3"

四、从海量的KEY中查询出某一固定前缀的KEY

  • 需要先确定数据规模的范围,根据数据范围的边界确认解决思路。

五、如何通过Redis实现分布式锁

1. 分布式锁需要解决的问题

  1. 互斥性:即任意时刻,只能有一个客户端获取锁,不能有多个客户端获取锁;
  2. 安全性:锁只能由持有该客户端所删除,不能被其他客户端删除;
  3. 死锁:客户端因宕机等原因无法正常释放锁而产生的死锁,需要对应的设定机制避免死锁的产生;
  4. 容错:当部分节点宕机时,客户端仍能获取锁。

2. 分布式锁的实现方法

  1. 通过使用SETNX实现分布式锁,(SETNX设置成功,返回 1 。 设置失败,返回 0)SETNX具有原子性,使用SETNX对某个KEY设值,如果设值成功,则没有其他线程使用该KEY;反之,设值失败,则说明此时有其他线程或程序正在占用该KEY

常用命令:

setnx key value

get key

举个栗子:

127.0.0.1:6379> exists cat
(integer) 0
127.0.0.1:6379> setnx cat tom
(integer) 1
127.0.0.1:6379> setnx cat tommy
(integer) 0
127.0.0.1:6379> get cat
"tom"
  • 为解决SETNX长期有效的问题,引入了EXPIRE,来为SETNX设置生存时间。
    常用命令:
expire key seconds

举个栗子:

127.0.0.1:6379> get cat
"tom" 
127.0.0.1:6379> expire cat 10
(integer) 1
127.0.0.1:6379> setnx cat tommy
(integer) 0
127.0.0.1:6379> setnx cat tommy
(integer) 1
127.0.0.1:6379> get cat
"tommy"
  • 使用EXPIRE,但同时也存在着问题,如果在执行EXPIRE前,SETNX就已经完成了相应的操作,EXPIRE就会一直占用资源,导致无法释放。
  1. 通过使用SETNX实现分布式锁(set key value [expiration EX seconds|PX milliseconds] [NX|XX]
    (1)EX second :设置键的过期时间second秒;
    (2)PX millisecond:设置键的过期时间喂millisecond毫秒;
    (3)NX:只在键不存在是,才对键进行设置操作;
    (4)XX:只在键已经存在时,才对键进行设置操作;
    (5)SET操作成功时,返回OK,否则返回nil。

举个栗子:

127.0.0.1:6379> set rat Jerry ex 10 nx
OK
127.0.0.1:6379> set rat Mickey ex 10 nx
(nil)
127.0.0.1:6379> get rat
"Jerry"
127.0.0.1:6379> get rat
(nil)

3. 当大量的KEY同时过期时,应注意的问题

  1. 当存在集中过期的情况时,由于消除大量的KEY会耗时很长,导致出现卡顿的情况发生
  2. 解决方案:在设置KEY的过期时间时,使用随机值,使得过期时间分散,从而解决同时消除大量KEY所产生的的卡顿现象。

六、如何使用Redis实现异步队列

1. 使用List作为队列,RPUSH生产消息,LPOP消费消息

  1. 缺点:LPOP不会去等待队列中的值。而是直接消费;
  2. 改进:可以在应用层中引入SLEEP机制去重新调用LPOP重试。

如果不使用SLEEP,怎么实现异步队列?

2. 使用BLPOP,实现异步队列

  1. 缺点只能供一个消费者使用

常用命令:

blpop key [key ...] timeout

举个栗子:

客户端1

127.0.0.1:6379> blpop bear 30

客户端2:

127.0.0.1:6379> lpush bear panda
(integer) 1

客户端2完成操作后,客户端1的变化:

127.0.0.1:6379> blpop bear 30
1) "bear"
2) "panda"
(27.55s)

3. 使用pub/sub,主题订阅者模式

  1. 消息的发布是无状态的,无法保证正常到达(客户端下线期间无法正常获取消息)

举个栗子:

客户端1:(创建主题topic1)

127.0.0.1:6379> SUBSCRIBE topic1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "topic1"
3) (integer) 1

客户端2:

127.0.0.1:6379> SUBSCRIBE topic1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "topic1"
3) (integer) 1

客户端3:(发送内容)

127.0.0.1:6379> PUBLISH topic1 "hello"
(integer) 2

此时,客户端1 、2显示新的内容:

1) "message"
2) "topic1"
3) "hello"

六、Redis如何做到持久化?

1. RDB(快照)持久化:保存某个时间点的全量数据快照

1.1 RDB持久化的相关配置

由于我使用的redis是安装在windows环境,所以打开:

D:\Software\Redis-x64-5.0.10下的redis.windows.conf文件

找到对应的配置:

save 900 1 # 在900秒以内,产生1次写入,就进行一次快照
save 300 10 # 在300秒以内,产生10次写入,就进行一次快照
save 60 10000 # 在60秒以内,产生10000次写入,就进行一次快照

save "" # 禁用rdb的配置

# 设置为yes时,表示当备份进程出错的时候,主进程就不再进行写入的操作了
stop-writes-on-bgsave-error yes 

rdbcompression yes

1.2 SAVE:(使用较少)阻塞Redis的服务器进程,知道RDB文件被创建完毕

127.0.0.1:6379> save  # 重新生成dump.rdb文件
OK
127.0.0.1:6379> lastsave
(integer) 1618413018

1.3 BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器的进程

127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> lastsave
(integer) 1618413131

1.4 自动化出发RDB持久化的方式

  1. 根据redis.conf配置里的SAVE m n定时出发(使用的是BGSAVE)
  2. 主从复制时,主节点自动触发
  3. 执行Debug Reload命令
  4. 执行Shutdown且没有开启AOF持久化时

1.5 RDS持久化的缺点

  1. 内存数据的全量同步,数据量大会由于I/O而严重影响性能
  2. 可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据

2. AOF(Append-Only-File)持久化:通过保存写命令

2.1 AOF持久化的方式

  1. 记录下除了查询意外的所有变更数据状态的指令
  2. 以append的形式追加保存到AOF文件中(增量)

2.2 AOF持久化的相关配置

打开:

D:\Software\Redis-x64-5.0.10下的redis.windows.conf文件

找到对应的配置:

appendonly no # 默认状态为 关闭状态

appendfilename "appendonly.aof" # 生成AOF文件的文件名

# appendfsync always # 缓存发生变化,就执行持久化操作
appendfsync everysec # 默认状态,每隔一秒
# appendfsync no # 交由操作系统决定(缓存区填满)

2.3 AOF持久化的流程

  1. 调用Fork(),创建一个子进程
  2. 子进程吧新的AOF写入一个临时文件中,不依赖原来的AOF文件
  3. 主进程持续将新的变动同步写到内存和原来的AOF中
  4. 主进程获取子进程重写AOF完成的信号,将辛的AOF同步增量变动
  5. 使用新的AOF文件代替旧的AOF文件

3. RDB和AOF文件共存的情况下的恢复流程

Redis的恢复流程

4. RDB和AOF的优缺点

优缺点RDBAOF
优点全量数据快照,文件小,恢复快可读性高,适合保存增量数据,数据不易丢失
缺点无法保存最近一次快照之后的数据文件体积大,恢复时间长

5. RDB-AOF混合持久化方式(较为推荐)

  • BGSAVE做镜像全量持久化(BGASVE会花费较长时间,停机时会导致大量数据丢失),AOF做增量持久化(配合使用,存放近期的操作指令)

七、Redis的集群原理

1. 如何从海量的数据中快速找到所需的数据

  1. 分片:按照某种规则去划分数据,分散存储在多个节点上
  2. 常规的按照哈希划分无法实现节点的动态增减

2. 一致性哈希算法

对2^32取模,将哈希值空间组织成虚拟的圆环

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值