一、NoSql

NoSql介绍

  • NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,泛指非关系型的数据库。
  • NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。

NoSql适用的场景

  • 对数据高并发的读写
  • 海量数据的读写
  • 对数据高可扩展性的

常见的NoSQL数据库

Memcached

1 很早出现的NoSql数据库

2 数据都在内存中,一般不持久化

3 支持简单的key-value模式,支持类型单一

4 一般是作为缓存数据库辅助持久化的数据库

Redis

1 几乎覆盖了Memcached的绝大部分功能

2 数据都在内存中,支持持久化,主要用作备份恢复

3 除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。

4 一般是作为缓存数据库辅助持久化的数据库

MongoDB

1 高性能、开源、模式自由(schema free)的文档型数据库

2 数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘

3 虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能

4 支持二进制数据及大型对象

5 可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。

二、Redis介绍

Redis 是C语言开发的一个开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL数据库

介绍

官网: https://redis.io

  • Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。)

  • Redis数据都是缓存在计算机内存中,但是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化。

  • Redis读写速度快,Redis读取的速度是110000次/s,写的速度是81000次/s;

  • Redis的所有操作都是原子性的。

  • Redis支持多种数据结构:string(字符串),list(列表),hash(哈希),set(集合),zset(有序集合)

  • Redis支持集群部署

  • 支持过期时间,支持事务,消息订阅

安装

普通安装

Redis安装步骤:

  1. 在官网中下载源码包

  2. 因Redis官方只提供了源码包, 所以我们要使用gcc编译工具来生成可执行文件

    • 检查gcc环境: gcc -v
    • 若没有gcc环境, 则需要安装gcc编译工具: yum install -y gcc tcl
  3. 确保当前环境存在gcc编译工具后, 我们就可以进行下面的操作了

    • 进入到安装包目录: cd /chinaskills/
    • 将redis解压到/opt/module/ 目录下: tar -xvf redis-6.2.6.tar.gz -C /opt/module/
    • 进入到redis的目录下: cd /opt/module/redis-6.2.6
  4. 使用gcc工具来生成可执行文件

    make PREFIX=/opt/module/redis install

    说明: 此项操作可以生成redis的bin目录, 也就是可执行文件. 此时已经可以启动redis了

  5. Redis现在只能在前台启动, 配置后台启动

    • 在redis目录下拷贝redis的配置文件到/etc下: cp redis.conf /etc/redis.conf

    • 配置/etc/redis.conf

      1
      2
      3
      4
      5
      6
      7
      8
      # 是否在后台启动服务 yes为在后台启动服务 (大概在257行)
      daemonize yes

      # 设置成no(默认是设置成yes的,阻止了远程访问)
      protected-mode no

      # 配置redis的启动地址(注释掉原来的配置)
      bind 172.29.44.81
  6. 配置完成后, 为了其他两台机器也能使用, 我们可以直接进行分发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 分发redis
    xsync /opt/module/kafka/
    # 分发redis的配置
    xsync /etc/redis.conf

    # 分发完成后还需要修改其他两台机器的redis配置!!!! (很重要!)
    # 在/etc/redis.conf中修改slave1 slave2 的配置, 分别为:
    bind 172.29.44.82
    bind 172.29.44.83
  7. 操作redis

    • 启动redis服务: redis-server /etc/redis.conf

    • 查看redis进程: ps -ef | grep redis

    • 用客户端访问: redis-cli

    • Redis 的关闭: redis-cli shutdown

Docker 安装

// todo

docker 中redis 基本操作

基本操作

通过客户端连接redis

语法 功能
keys * 查看当前库所有key (匹配:keys *1)
exists key 判断某个key是否存在
type key 查看你的key是什么类型
del key 删除指定的key数据
unlink key 非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作
expire key 10 10秒钟:为给定的key设置过期时间
ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
select 命令切换数据库
dbsize 查看当前数据库的key的数量
flushdb 清空当前库
flushall 清空全部库

数据库基本操作

  1. 查看当前数据库的key的数量 dbsize
  2. 清空当前库 flushdb
  3. 清空全部库 flushall

三、⭐Redis 原理

Redis单线程+多路复用

多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)

  • 多路:指的是多个网络连接客户端
  • 复用:指的是复用同一个线程
  • I/O 多路复用:其实是使用一个线程来检查多个 Socket 的就绪状态,在单个线程中通过记录跟踪每一个 socket(I/O流)的状态来管理处理多个 I/O 流。

image-20230706100628816

1、一个 socket 客户端与服务端连接时,会生成对应一个套接字描述符(套接字描述符是文件描述符的一种),每一个 socket 网络连接其实都对应一个文件描述符。

  • 文件描述符(file descriptor): Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符。可以理解文件描述符是一个索引,这样,要操作文件的时候,我们直接找到索引就可以对其进行操作了。我们将这个索引叫做文件描述符(file descriptor),简称fd。

2、多个客户端与服务端连接时,Redis 使用 「I/O 多路复用程序」 将客户端 socket 对应的 FD 注册到监听列表(一个队列)中。当客服端执行 read、write 等操作命令时,I/O 多路复用程序会将命令封装成一个事件,并绑定到对应的 FD 上。

3、**「文件事件处理器」**使用 I/O 多路复用模块同时监控多个文件描述符(fd)的读写情况,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器进行处理相关命令操作。

4、文件事件分派器接收到I/O多路复用程序传来的套接字fd后,并根据套接字产生的事件类型,将套接字派发给相应的事件处理器来进行处理相关命令操作。

5、整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,当其中一个 client 端达到写或读的状态,文件事件处理器就马上执行,从而就不会出现 I/O 堵塞的问题,提高了网络通信的性能。

四、⭐Redis常用数据类型

Redis的五大数据类型

常见数据类型操作命令的帮助文档 http://www.redis.cn/commands.html

String

介绍

  1. String是Redis最基本的类型,一个key对应一个value。
  2. String类型是二进制安全的。意味着Redis的string可以包含任何数据。如jpg图片或者序列化的对象。
  3. String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M

常用命令

语法 解释
set <key><value> 添加键值对
NX:当数据库中key不存在时,可以将key-value添加数据库
XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
EX:key的超时秒数
PX:key的超时毫秒数,与EX互斥
get <key> 查询对应键值
setnx <key><value> 只有在 key 不存在时 设置 key 的值
mset <key1><value1><key2><value2> ….. 同时设置一个或多个 key-value对
mget <key1><key2><key3> ….. 同时获取一个或多个 value
setex <key> <过期时间> <value> 设置键值的同时,设置过期时间,单位秒。

redis指令运行的原子性

  • 所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
    • 在单线程中, 能够在单条指令中完成的操作都可以认为是”原子操作”,因为中断只能发生于指令之间。
    • 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
    • Redis单命令的原子性主要得益于的单线程

数据结构

String的数据结构为**简单动态字符串(**Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配.

如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。

List

简介

单键多值, 一个键下的value是一个List.Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。

常用命令

语法 功能
lpush/rpush <key><value1><value2><value3> …. 从左边/右边插入一个或多个值。
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡。
rpop/lpush <key1><key2> 从<key1>列表右边吐出一个值,插到<key2>列表左边
lrange <key><start><stop> 按照索引下标获得元素(从左到右)
0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex <key><index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value><newvalue> 在<value>的前面插入<newvalue>插入值
linsert <key> after <value><newvalue> 在<value>的后面插入<newvalue>插入值
lrem <key><n><value> 从左边删除n个value(从左到右)
lset<key><index><value> 将列表key下标为index的值替换成value

数据结构

List的数据结构为快速链表quickList。首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。

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

Set

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

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变

常用命令

语法 功能
sadd <key><value1><value2> ….. 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
smembers <key> 取出该集合的所有值。
sismember <key><value> 判断集合<key>是否为含有该<value>值,有1,没有0
scard<key> 返回该集合的元素个数。
srem <key><value1><value2> …. 删除集合中的某个元素。
spop <key> 随机从该集合中吐出一个值
spop <key><N> 随机从该集合中吐出N个值。
srandmember <key><n> 随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination><value> 把集合中一个值从一个集合移动到另一个集合
SMOVE set set2 a
sinter <key1><key2> 返回两个集合的交集元素。
sunion <key1><key2> 返回两个集合的并集元素。
sdiff <key1><key2> 返回两个集合的差集元素(key1中的,不包含key2中的)

数据结构

Set数据结构是dict字典,字典是用哈希表实现的。Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它内部也使用hash结构,所有value都指向同一个内部值。

Zset

简介

Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

6.2 常用命令

语法 功能
zadd <key><score1><value1><score2><value2>… 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zrange<key><start><stop> [WITHSCORES] 升序返回有序集 key 中,下标在<start><stop>之间的元素,0代表第一个元素索引,-1代表最后一个元素索引.带WITHSCORES,可以让分数一起和值返回到结果集。
zrevrange <key><start><stop> [WITHSCORES] 降序返回有序集 key 中,下标在<start><stop>之间的元素,0代表第一个元素索引,-1代表最后一个元素索引.带WITHSCORES,可以让分数一起和值返回到结果集
zrangebyscore <key> <min> <max> [withscores] [limit offset count] 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 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开始。

案例:如何利用zset实现一个文章访问量的排行榜?

数据结构

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

Hash

简介

hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息

  1. 单key+序列化 .问题:每次修改用户的某个属性需要,先反序列化改好后再序列化回去。开销较大。

    image-20230706110321231

  2. 多key-value .问题:用户ID数据冗余

image-20230706110341117

  1. 单key + 多(field+value)

image-20230706110256007

  • 通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题

常用命令

语法 功能
hset <key><field><value> 给<key>集合中的 <field>键赋值<value>
hget <key1><field> 从<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。

Redis高级数据结构

  1. bloomfilter(布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
  2. geo(计算地理位置)
  3. hyperloglog(pv / uv)
  4. pub / sub(发布订阅,类似消息队列)
  5. BitMap (1001010101010101010101010101)

五、Springboot整合Reids

Java操作Redis

  • Spring-Data-Redis

    Spring Data

    通用的数据访问框架,定义了一组 增删改查 的接口mysql、redis、jpa

  • Jedis:

    (独立于 Spring 操作 Redis 的 Java 客户端,要配合 Jedis Pool 使用)

  • Lettuce

    • 高阶 的操作 Redis 的 Java 客户端
    • 异步、连接池
  • Redisson

    分布式操作 Redis 的 Java 客户端,让你像在使用本地的集合一样操作 Redis(分布式 Redis 数据网格)

使用场景

  1. 如果你用的是 Spring,并且没有过多的定制化要求,可以用 Spring Data Redis,最方便
  2. 如果你用的不是 SPring,并且追求简单,并且没有过高的性能要求,可以用 Jedis + Jedis Pool
  3. 如果你的项目不是 Spring,并且追求高性能、高定制化,可以用 Lettuce,支持异步、连接池
  4. 如果你的项目是分布式的,需要用到一些分布式的特性(比如分布式锁、分布式集合),推荐用 redisson

Springboot整合Reids

六、Redis配置解析

网络配置

安全配置

密码设置

限制

七、事务和锁

事务

Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。Redis事务的主要作用就是串联多个命令防止别的命令插队。

事务的控制命令

命令 功能
multi 开始组队
exec 执行队列中的命令
discard 取消组队

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard取消组队

错误处理机制

  • 组队报错,提交失败:提交失败组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消

    image-20230706135937567

  • 组队成功,提交时有成功有失败。如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,其他的命令都会执行,不会回滚。

    image-20230706140338715

Redis事务的三个特性

  • 单独的隔离操作
    • 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 没有隔离级别的概念
    • 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
  • 不保证原子性
    • 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。

监视和取消监视key

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

image-20230706141601678

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

Lua脚本

Lua 是一个小巧的脚本语言,Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,Lua并没有提供强大的库,一个完整的Lua解释器不过200k,所以Lua不适合作为开发独立应用程序的语言,而是作为嵌入式脚本语言。很多应用程序、游戏使用LUA作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。

Lua脚本的优势

将复杂或者多步的redis操作, 写为一个脚本, 一次提交给redis执行, 减少反复连接redis的次数。提升性能。

LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作。但是注意redis的lua脚本功能(在Redis 2.6以上可使用)。利用lua脚本淘汰用户,解决超卖问题。

通过lua脚本解决争抢问题,实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题。

Lua脚本实际用途

Redis使用Lua脚本

八、持久化

RDB持久化

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里.

RDB持久化流程

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

  1. Fork子进程

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

优点

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

劣势

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

总结

RDB 配置解析

RDB文件名配置

  • 在redis.conf中配置文件名称,默认为dump.rdb

image-20230706162436439

RDB文件位置配置

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

  • 可以通过修改该配置,将RDB文件存到系统的制定目录下dir “/root/myredis/“

    image-20230706162540821

RDB自动执行快照策略

image-20230706162725781

  • save命令临时这只快照执行策略

    • 格式:save 秒钟 写操作次数 RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件, 默认是1分钟至少1万个key发生变化,或5分钟至少100个key发生变化,或1个小时至少1个key发生变化。

    • 禁用 不设置save指令,或者给save传入空字符串

RDB手动执行快照命令

  • save VS bgsave

    • save :使用主进程进行持久化指令,save时只管保存,其它不管,全部阻塞。手动保存。不建议。
    • bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
      可以通过lastsave 命令获取最后一次成功执行快照的时间
  • flushall命令

    • 执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义
  • shutdown命令

    • shutdown命令在关系服务的时候也会进行自动的持久化

RDB备份异常策略

  • stop-writes-on-bgsave-error 配置
    • 当Redis无法写入磁盘的话,直接关掉Redis的写操作。推荐yes.

image-20230706162910097

RDB 文件压缩配置

  • rdbcompression配置
    • 对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。推荐yes.
      image-20230706163019916

RDB文件检查完整性配置

  • rdbchecksum配置
    • 在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能.推荐yes.

image-20230706163056568

RDB手动备份操作

  • 查询rdb文件的目录 将 *.rdb的文件拷贝到别的地方

  • rdb的恢复

    • 关闭Redis

    • 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb

    • 启动Redis, 备份数据会直接加载

RDB禁用操作

  • 修改配置文件永久禁用

  • 通过指令临时禁用

    redis-cli config set save "" save后给空值,表示禁用保存策略(不建议)

AOF持久化

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

AOF持久化流程

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

AOF的优势

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

AOF的劣势

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

AOF总结

AOF相关配置解析

AOF文件名配置

  • 可以在redis.conf中配置文件名称,默认为 appendonly.aof

image-20230706163701368

AOF文件位置路径

  • Redis6中,AOF文件的保存路径,同RDB的路径一致。

  • Redis7有变化:

    base:基本文件

    incr:增量文件

    manifest:清单文件

image-20230706164947931

AOF开启-修复-恢复操作

1
AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
  • 正常恢复数据

    • 修改默认的appendonly no,改为yes,开启AOP方式

      image-20230706163851658

    • 将有数据的aof文件复制一份保存到对应目录(查看目录:config get dir)

    • 恢复:重启redis然后重新加载

  • 异常修复数据

    • 修改默认的appendonly no,改为yes

    • 如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof –fix appendonly.aof.1.incr.aof进行恢复

    • 备份被写坏的AOF文件

    • 恢复:重启redis,然后重新加载

AOF同步频率设置

image-20230706164003278

  • appendfsync always

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

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

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

AOF压缩配置

  • 什么是文件压缩 rewrite重写?

    AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof

  • 如何实现重写?

    • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),redis4.0版本后的重写,是指上就是把rdb 的快照,以二级制的形式附在新的aof头部,作为已有的历史数据,替换掉原来的流水账操作。
    • no-appendfsync-on-rewrite 设置重写策略
    • 如果 no-appendfsync-on-rewrite=yes ,不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)
    • 如果 no-appendfsync-on-rewrite=no, 还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)
  • 何时触发重写?

    Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发,重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。

    • auto-aof-rewrite-percentage 设置重写基准值
      • 文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
    • auto-aof-rewrite-min-size 设置重写基准值
      • 最小文件64MB。达到这个值开始重写。

    例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB
    系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。

  • 重写的流程是?

    1. bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
    2. 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
    3. 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区,保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
      1. 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
      2. 主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
    4. 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。

持久化方案选择

RDB和AOP用哪个好?

  • 官方推荐两个都启用。
  • 如果对数据不敏感,可以选单独用RDB。
  • 不建议单独用 AOF,因为可能会出现Bug。
  • 如果只是做纯内存缓存,可以都不用。
  • AOF和RDB如果同时开启,系统默认取AOF中的持久化数据

官网建议

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储

  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.

  • Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大

  • 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.

  • 同时开启两种持久化方式

  • 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

  • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?

  • 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份), 快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

  • 性能建议

    因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

    如果使用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。
    代价,一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。
    只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。
    默认超过原大小100%大小时重写可以改到适当的数值。

九、❌主从复制

介绍

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

主从复制的作用

  • 读写分离,性能扩展
  • 容灾快速恢复

主从复制具体实现

实现思路

集群配置: 一台进行写操作, 两台进行写操作

  1. 一个redis服务作为主机,主要负责写操作

  2. 两个redis服务作为从机,主要负责读操作

  3. 从机自动从主机同步数据下来

  4. 从机主动找主机,而主机不会找从机

  5. 正常来说主机和从机应该在不同的IP上开启redis服务,我们为了快速模拟,可以在一台机器上模拟出三个redis服务即可

十、✅Reids 集群

什么是集群

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

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

使用Docker 搭建Reids集群

  1. 拉取镜像

  2. 启动6台redis实例

    参数说明:

    • --net host 容器和宿主机共用一个network namespace ip和端口号使用宿主机所有
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #第一台redis容器实例启动
    docker run -d --name redis-node-1 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-1:/data redis --cluster-enabled yes --appendonly yes --port 6381

    #第二台redis容器实例启动
    docker run -d --name redis-node-2 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-2:/data redis --cluster-enabled yes --appendonly yes --port 6382

    #第三台redis实例
    docker run -d --name redis-node-3 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-3:/data redis --cluster-enabled yes --appendonly yes --port 6383

    #第四台
    docker run -d --name redis-node-4 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-4:/data redis --cluster-enabled yes --appendonly yes --port 6384

    #第五台
    docker run -d --name redis-node-5 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-5:/data redis --cluster-enabled yes --appendonly yes --port 6385

    #第六台
    docker run -d --name redis-node-6 --net host --privileged=true -v /opt/module/redis-cluster/redis-node-6:/data redis --cluster-enabled yes --appendonly yes --port 6386
  3. 进入到第一台节点容器内部

    docker exec -it redis-node-1 /bin/bash

  4. 搭建主从关系

    1
    redis-cli --cluster create 172.29.49.90:6381 172.29.49.90:6382 172.29.49.90:6383 172.29.49.90:6384 172.29.49.90:6385 172.29.49.90:6386 --cluster-replicas 1
  5. 进入集群, 查看状态

    1
    2
    3
    4
    5
    6
    7
    8
    # 以集群状态进入redis
    redis-cli -p 6381 -c

    # 通过cluster info 命令查看 状态
    cluster info

    # 查看集群节点
    cluster nodes
  6. 重启集群

    1
    2
    # 首先启动6台redis节点
    docker start redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5 redis-node-6

集群的slots

image-20230706201426691

  • 一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,

  • 集群使用公式 ==CRC16(key) % 16384== 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

  • 集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:

    • 节点 A 负责处理 0 号至 5460 号插槽。
    • 节点 B 负责处理 5461 号至 10922 号插槽。
    • 节点 C 负责处理 10923 号至 16383 号插槽。

    image-20240527110845054

集群中的存储和查询

集群中存储值

  • 在redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口。
  • redis-cli客户端提供了 –c 参数实现自动重定向。
  • 如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。

image-20230706201647379

  • 不在一个slot下的键值,不能使用mget,mset等多键操作。

  • 可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去。

image-20230706201756159

集群中查找值

  • cluster keyslot key 计算key应该保存在那个插槽
  • cluster countkeysinslot slot的值 计算某个插槽中保存的key的数量
  • CLUSTER GETKEYSINSLOT <slot><count> 返回 count 个 slot 槽中的键。

image-20230706202007674

集群故障恢复

image-20231021091936654

  • Redis集群的选举规则

    • redis中的所有的主机互相监督.当有一台主机宕机时,则剩余的节点(主-从-超半数)投票选举新的主机.
    • 当主机数量缺失时 则集群系统崩溃.
  • 如果主节点下线, 从节点可以自动升为主节点 注意:15秒超时

image-20230706202459470

  • 主节点恢复后,主从关系会如何?主节点恢复后会变成从机。

image-20230706202640635

  • 如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?
    • redis.conf中cluster-require-full-coverage 为yes 那么 ,整个集群都挂掉
    • redis.conf中cluster-require-full-coverage 为no 那么,只有该插槽数据全都不能使用。

集群的优缺点

优点

  • 实现扩容
  • 分摊压力
  • 无中心配置相对简单

缺点

  • 多键操作是不被支持的
  • 多键的Redis事务是不被支持的。lua脚本不被支持
  • 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。