Redis 从入门到高级

1. Redis

底层原理:单线程+多路IO复用

2. 基本数据类型

2.1 String

2.2 list

2.3 set

2.4 zset

2.5 hash

3. 特殊数据类型

3.1 Geospatial

地理信息,添加经纬度之后可以计算两个地点之间的距离

3.2 Bitmaps

签到、记录用户每天是否活跃,比如记录一周考勤 value 可以是 0000000,如果周一到周五都打卡了就设置为1 ,value 变成 1111100

在这里插入图片描述

3.3 HyperLogLog

解决基数计算问题:求集合中不重复元素个数的问题称为基数问题
相当于可以实现去重,但是和 set 不同,HyperLogLog 无法返回集合中的元素有哪些,只能告诉你基数统计的结果。如果添加的元素已经存在就返回 0 否则返回 1,优势是需要花费的内存极小就可以完成计算

4. 发布订阅

# 一个客户端订阅 channel1
subscribe channel1
# 另一个客户端向 channel1 发生 helloworld
publish channel1 helloworld

5. redis 事务

redis 事务本质就是串行多个指令,开始执行就不受其他命令打断

# 开启事务 相当于 mysql 的 begin
multi

# 执行事务 
exec

# 取消本次事务组队
discard 
  • 要执行 exec 命令事务中包含的所有指令才开始执行
  • 组队阶段报错,所有操作会被取消 (比如一个指令是 set k1 没有指定value值)
  • 执行阶段报错,就会出现部分成功,部分失败 (比如一个指令是 incr k1,但是 k1 的value 不是数值类型,就会出现执行阶段报错,组队阶段不报错)
  • 执行不保证原子性,会出现部分指令执行成功,部分失败的情况。而且失败之后也不会回滚

redis 乐观锁实现

原理就是检查版本号

# 再开启事务之前执行 watch,监视某个key的值是否发生变化,如果发生了变化接下来事务的执行exec就会失败
watch key名称

redis 不能实现悲观锁

6. 持久化

6.1 RDB(Redis Database)

再一定的时间间隔后,将内存中的数据快照写入到磁盘中
通过再配置文件中设置 save 的规则,比如10秒内有3个key发生了变化就进行持久化操作。

开启和关闭 RDB
Redis默认开启的就是RDB模式

通过在配置文件 redis.conf 配置 save 例如 save 10 2 表示10秒内有 2个key 发生修改就进行持久化

如果不开启就将 save 注释,或者配置空字符串 save ""

备份指令

# 备份命令
# 会阻塞主进程,期间redis不能正常使用
save
# 不会阻塞主进程,开启fork 子进程
bgsave

bgsave 备份过程
开启一个 fork 进程,将当前 redis 的数据快照写入到一个临时文件中,然后再去替换原有的 dump.rdb 持久化文件,这个过程叫做“写时复制”。过程是再后台异步执行的,不阻塞redis的正常请求

备份恢复
只需要将备份生成的 dump.rdb 文件放到redis启动目录下,然后重启redis就会自动读取

优点:

  • 适合大规模数据复制
  • 速度较快

缺点:

  • 因为需要写入一份临时文件中,所以对磁盘空间有一定的要求
  • 如果没有达到设置的规则,比如 10 秒内只有一个key 发生了变化,此时不会进行持久化操作,如果发生宕机这一部分修改就丢失了

6.2 AOF (Append Only File)

AOF 和 RDB 只能开启一个
如果同时开启了 AOF 和 RDB ,就不会去读取 dump.rdb 中的数据了。因为 appendonly.aof 的数据认为是更完整的
将写入的操作追加保存到 appendonly.aof 文件中

开启方式
配置文件中设置 appendonly yes

刷盘频率
当进行写入操作时,会先记录到 aof buffer 中,然后再以一定的频率刷新到磁盘文件中,可以配置以下三种级别

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

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

appendfsync no:redis不主动进行同步,把同步时机交给操作系统。

备份恢复
只需要将备份生成的 appendonly.aof 文件放到redis启动目录下,然后重启redis就会自动读取

优点:

  • 数据完整性更好,不容易丢失数据

缺点:

  • 备份恢复速度慢
  • 如果是每次写入都刷盘到文件中,会有一定的性能压力
  • 文件占用空间更大

建议

RDB 和 AOF 两者都开启

7. 主从复制

从服务器第一次和主服务器建立连接时会发送 sync 同步指令,此时主服务器会进行一次 rdb 持久化操作。然后将 dump.rdb 文件传给从服务器,实现数据同步。之后就是主服务发生了写入操作再主动同步给从服务器

7.1 一主二仆

一个主节点两个从节点。

在这里插入图片描述

  • 从节点只能读不能写
  • 主机宕机,从机还是从机
  • 主机重启还是主机

7.2 薪火相传

一个主节下以一个从节点,从节点下还有从节点
在这里插入图片描述

  • 减轻主服务器的压力
  • 从机可以作为其他从机的主机
  • 如果 从1 宕机会导致 从2 无法同步数据

7.3 反客为主

主机宕机之后,从机可以成为主机

  • 需要再从机上手动执行 slaveof no one

7.4 哨兵模式

启动一个哨兵监视主服务器,当主服务器宕机之后,选择一个从服务器成为主服务器,之前的主服务器重启之后自动成为从服务器
如果不使用哨兵模式,是无法自动产生新的主节点的。需要再从机上手动执行命令将从节点升级为主节点

最少需要3个哨兵,因为只有1个哨兵宕机了就完犊子了。一主二从起步就是 6 个节点

在这里插入图片描述

选举新的主服务器规则
依照如下步骤,如果一样就进行下一步

  1. 配置文件的 replica-priority
  2. 选择偏移量最小的,也就是同步主服务器数据最多的
  3. 选择 runid 最小的,redis启动时会生成

8. Redis 集群

无中心化集群配置
集群中的每一台服务器都可以作为集群的入门

例如我们按照业务部署了两台 Redis ,一台负责用户相关的数据记录,另一台负责商品相关数据的记录。我们想要存储的商品信息,可以是先发送到用户的 Redis 服务,然后再由用户 Redis 转发到商品 Redis。这就是无中心化

这样也实现了 Redis 的水平扩容。将不同业务的数据分散到不同的 Redis 服务器上。

8.1 slot

一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽

每一个节点负责处理一部分插槽,例如下图
在这里插入图片描述
集群中的每一个节点都可以作为集群的入口。比如在节点A上,添加一个key,但是经过计算
这个 key 存储的插槽属于节点 B 的范围。这时会自动重定向到对应的节点上。

注意:
如果一条指令同时添加多个 key 需要保证这些key经过计算是属于同一个节点的插槽区域的,否则无法成功。当然也有解决办法就是在key后加上组id,比如 key1{group1}

9. 常见问题

9.1 缓存穿透

大量请求无法命中 Redis ,直接访问数据库

解决方案

  1. 对空值进行缓存,缺点就是存储大量空值的 key 浪费空间,也可能造成数据不一致,数据库中有值了但是缓存中确是空
  2. 使用布隆过滤器
  3. 设置可访问名单 bitmap

9.2 缓存击穿

某一个热点key过期的瞬间,大量请求进来,直接访问数据库

解决方案

  1. 临时调整过期时间
  2. 增加分布式锁
  3. 预先将热点数据写入缓存中,比如双11预先将一些热门秒杀商品加载到缓存中

9.3 缓存雪崩

Redis 中大量的key 同时失效,此时大量请求进来,直接访问数据库
也可能是 redis 宕机了

解决方案

  1. 设置过期时间的时候,增加一个随机的值,让key的过期时间产生差异
  2. 记录缓存过期的时间,提前进行一个提醒,再开启一个线程去增加过期时间
  3. 使用分布式锁或者队列
  4. 构建多级缓存

10. 分布式锁

假设我们部署了多个java服务,如果在java中加锁,那就是只有访问到这一台服务上的请求会被这个锁限制,其他服务是不知道已经上锁的。这时就需要分布式锁,Redis就可以实现分布式锁

# 加锁(最好设置过期时间,否则如果忘记释放锁就会造成问题)
setnx 

# 释放锁(就是删除key)
del 

一般流程就是先加锁,如果加锁失败就再一定时间之后再重试

10.1 误删锁问题

情况一:
由于设置了过期时间,就可能出现线程A拿到锁之后,中途可能出现了卡顿导致在锁的超时时间内没有执行完操作,锁自动过期了。这时线程B拿到了锁,开始进行操作。但是在线程B操作的过程中,线程A又恢复了正常,然后A执行完释放了锁。注意问题来了,A的锁早就过期了,他现在释放的是B的锁,就造成了问题

解决
设置key时,value可以设置一个 uuid。释放锁时,比较uuid是否一致,一致才能删除,保障了自己只能释放自己的锁

情况二
经过情况一的优化之后依然存在问题,在线程A比较完uuid一致之后,因为比较uuid和释放锁并不是原子操作,所以可能出现,比较完uuid之后,还没删除锁。过期时间就到了,自动释放,这时线程B拿到了锁准备执行,然后A删除了锁就把线程B的锁给误删了

解决
使用 lua 脚本,使得比较uuid和删除的操作变成原子操作,执行lua脚本时Redis不会执行其他的命令。这也就保证了即便比较完 uuid 之后,锁过期自动释放,也不会误删他人的锁,因为此时 lua 脚本没有执行完,其他人是无法进行加锁操作的

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值