Redis的学习路线,Redis的应用场景,以及Redis关于缓存的一系列问题。---个人总结笔记

第一章 入门概述

1. 主流功能和应用

  1. 分布式缓存, 挡在mysql数据库之前的带刀护卫

  2. 内存存储和持久化(RDB + AOF) redis支持异步将内存中的数据写到硬盘上, 同时不影响继续服务

  3. 高可用架构搭配

  4. 缓存穿透, 击穿, 雪崩

  5. 分布式锁

  6. 队列

  7. 排行榜 + 点赞

2. Redis安装步骤 ----> 推荐使用Docker容器进行部署安装

下载安装
  1. yum -y install gcc-c++

    • 安装redis之前需要具备C++库环境

  2. wegt https://download.redis.io/releases/redis-7.0.0.tar.gz

  3. tar -zxvf redis压缩文件

    • 解压文件

  4. make && make install

    • 安装

  • 默认安装路径/usr/local/bin目录下

    • 在/usr/local/bin目录下默认是配置了环境变量

配置文件
  1. 修改成 daemonize yes

  • 后台启动

  1. protected-mode no

  • 后面需要别的机器连接的时候 需要把这个按键关掉

  1. 将 bind 127.0.0.1 注释掉

  • 默认的是只能本机连接, 后续影响远程IP连接

  1. 将requirepass 开启设置自己的密码 [在1036行]

  • 设置访问redis的密码

3. 开启服务

  • redis-server 需要扫描的配置文件

4. 关闭服务

  • shutdown

    • 前提要在redis客户端里面执行这行代码

  • redis-cli -a 密码 shutdown

    • 直接关闭redis服务

5. 进入客户端

  • 第一种方式进入的时候输入密码

    • redis-cli -a 密码 -p 端口号

  • 第二种方式进入之后输入密码

    • redis-cli

    • 进入之后

      • auth 密码

      • 输入之后才能对redis有使用权

6. 退出客户端

  • quit

    • 只退出redis客户端

    • 没有停止redis服务

7. 卸载Redis

  • ls -l /usr/local/bin/redis-*

  • rm -rf /usr/local/bin/redis-*

第二章 Redis的十种数据类型

  • Key一般都是字符串, 所谓的十种类型是指Value

  • 命令不区分大小写, 而key是区分大小写的

  • 永远的帮助命令 , help @类型

    • help @string

    • help @list

    • help @hash

    • … …


1. String --- 字符串

理解:
  • 支持序列化

  • String 是 redis最基本的类型, 一个Key对应一个Value

  • String类型是二进制安全的, 意思是redsi的String可以包含任何数据, 比如jpg图片或者序列化对象

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

命令:
  • set key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds | [KEEPTTL]

    • EX seconds

      • 以秒为单位

    • PX milliseconds

      • 以毫秒为单位

    • EXAT timestamp

      • 设置以秒单位的UNIX时间戳所对应的时间为过期时间

    • PXAT milliseconds-timestamp

      • 设置以毫秒为单位的UNIX时间戳所对应的时间为过期时间

    • NX

      • 键不存在的时候设置键值

    • XX

      • 键存在的时候设置键值

    • KEPTTL

      • 保留设置前指定键的生存时间。

    • GET

      • 返回指定键原本的值, 若键不存在时返回nil。

  • append key value 将给定的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 起始位置 结束位置 获得值的范围,类似java中的substring,前包,后包。

  • setrange key 起始位置 value 用 value 覆写key所储存的字符串值,从起始位置开始(索引从0开始)。

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

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

2. List --- 列表

理解:
  • 是简单的字符串列表, 按照插入顺序排序。

  • 可以添加一个元素到列表的头部 (左边) 或者 尾部 (右边)。

  • 它的底层实际是个双端链表。

  • 每个列表超过40亿个元素。

  • 可以左进也可以右进左出。

说明:
  • 一个双端链表的结构, 一般用在栈, 队列, 消息队列等场景。

  • left, right都可以插入添加。

  • 如果键不存在, 创建新的链表。

  • 如果键已存在, 新增内容。

  • 如果值全移除, 对应的键也就消失了。

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

命令:
  • lpush key value 将元素加入列表左边。

  • rpush key value 将元素加入列表右边。

  • lpop key 删除列表最左边的元素,并将元素返回。

  • rpop key 删除列表最右边的元素,并将元素返回。

  • lrang key start end(0 -1) 按照索引下标获得元素(从左到右)。

    • -1右边第一个,(0-1表示获取所有)。

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

  • lindex key index 按照索引下标获得元素(从左到右)。

  • llen key 获得列表长度。

场景:
  • 公众号关注了一个作者, 只要他们发布文章就会装进我关注作者这个List里面

  • 查看文章的时候根据文章的下标来查询详细的内容

3. Hash --- 哈希表

理解:
  • 是一个String类型的 field 和 value 的映射表, hash特别适合用于存储对象

  • 每个hash可以存储2^32 - 1 键值对 (40多亿)

命令:
  • HSET key field value 一次设置一个字段值

  • HGET key field 一次获取一个字段值

  • hgetall key 获取所有字段值

  • hdel 删除一个key

  • HMSET key field value [field value …] 一次设置多个字段值

  • HMGET key field [field …] 一次获取多个字段值

  • hlen 获取某个key内的全部数量

  • hkeys key 列出该hash集合的所有field

  • hvals key 列出该hash集合的所有value

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

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

场景:
  • 购物车 加购商品 取消商品 结算所有商品

4. Set --- 集合

理解:
  • 是String类型的无序集合, 集合成员是唯一的, 这就意味着集合中不能出现重复数据

  • 集合对象的编码可以是 intset 或者 hashtable

  • 通过哈希表实现的, 所以添加, 删除, 查找的复杂度都是 O

  • 最大成员数为 2^32 - 1 (40 亿)

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

  • smembers key 取出该集合的所有值

  • srem key value1 value2 … 删除集合中的某个元素

  • sismember keyvalue 判断集合key是否为含有该value值,有1,没有0

  • scard key 返回该集合的元素个数

  • spop key 随机从该集合中吐出一个值

  • srandmember key n 随机从该集合中取出n个值。不会从集合中删除

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

  • sinter key1 key2 返回两个集合的交集元素

  • sunion key1 key2 返回两个集合的并集元素

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

场景:
  • 随机指令可以用来抽奖

  • 查询两个set的交集 可以在交友的标签匹配上面用到

  • 微信朋友圈点赞查看同赞的朋友

  • dy里面可能认识的人通过两个人的交集很多, 将自己的推荐给朋友可能认识的朋友

5. ZSet (sorted set) --- 有序集合

理解:
  • 和set一样, 也是不允许出现重复的成员

  • 不同的是每个元素都会关联一个double类型的分数, redis正式通过分数来为集合中的成员进行从小到大的排序

  • 成员是唯一的, 但分数( score )却可以重复

  • 通过哈希表实现的, 所以添加, 删除, 查找的复杂度都是 O(1)

  • 集合中最大的成员数为 2^32 - 1

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

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

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

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

  • zrangebyscore key min maxwithscores 返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score值递增(从小到大)次序排列

  • zrevrangebyscore key max minwithscores 同上,改为从大到小排列

  • zincrby key increment value 为元素的score加上增量

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

  • zrank key value 返回该值在集合中的排名,从0开始

场景:
  • 做排行榜

  • 根据商品的销售对商品进行排序显示

  • 每天微信步数的排行榜

6. GEO --- 地理空间

理解:
  • 记录地理位置

  • 底层也是String类型实现的

命令:
  • GEOADD 添加经纬度坐标

  • GEOPOS 返回经纬度

  • GEOHASH 返回坐标的geohash表示

  • GEODIST 两个位置之间距离

  • GEORADIUS 以半径为中心, 查找附近的 xxx

  • GEORADIUSBYMEMBER

平替MySQL的好处:
  • 查询性能问题, 如果并发高, 数据量大这种查询量大这种查询是要搞垮数据库的

  • 这个查询的是一个矩形访问, 而不是以我为中心r公里为半径的圆形访问

  • 精准度的问题, 我们知道地球不是平面坐标系, 而是一个圆球, 这种矩形计算在长距离计算时会又很大的误差

场景:
  • 美团地图位置附近的酒店推送

  • 高德地图附近的核算检查点

7. HyperLogLog --- 基数统计

理解:
  • 用来做基数统计的算法

  • 优点:

    • 在输入元素数量或者题键非常非常大时, 计算基数所需的空间总是固定且很小的

  • 缺点:

    • 去重之后的数量有0.81%的误差

  • meigeHyperLogLog键只需要花费12KB, 就可以计算接近2^64个不同元素的基数, 这和计算基数时, 元素越多耗费内存就越多的集合形成鲜明对比

  • 但是, HyperLogLog只会根据输入元素来计算基数, 而不会存储输入元素本身, 所以HyperLogLog不能像集合那样, 返回输入的各个元素

  • 只要数量, 自带去重功能, 占用内存是定好的, 不会因为多, 而增加内存

  • 去重脱水后的真实数据

命令:
  • PFADD key element [element]

  • PFCOUNT key [key …]

  • PFMERGE destkey sourcekey [sourcekey …]

场景:
  • 天猫网站首页亿级的redis统计方案

名词:
  • UV (Unique Visitor)

    • 独立访客, 一般理解为客户端IP

  • PV (Page View)

    • 页面浏览量

  • DAU (Daily Active User)

    • 日活跃用户量

  • MAU (Monthly Active User)

    • 月活跃用户量

8. bitmap --- 位图

理解:
  • 由0 和 1状态表现的二进制位的bit数组

  • 底层是String类型, 一种统计二值状态的数据类型

  • 位图的本质就是数组

命令:
  • setbit key offset val 给指定key的值的第offset赋值val [偏移量是从零开始计算的]

  • getbit key offset 获取指定key的第offset位

  • bitcount key start end 返回指定key中[start, end]中为1的数量

  • bitop operation destkey key 对不同的二进制存储数据进行位运算(AND, OR , NOT , XOR)

场景:
  • 用户是否登录过

  • 钉钉的每日打卡

    • 状态的统计 , 占用的内存极少

  • 电影, 广告是否被点击播放过

两种计算方式:

  1. 把bit看做当天是否登录, 用户个每一个位置对应 , 1为登录0为未等

  2. 每个用户都有一个bit, 大小为365, 每个位置对应一年的每一天

9. bitfield --- 位域

理解:
  • 通过bitfield命令可以一次性错做多个比特位域( 指的是连续的多个比特位 )

  • 他会执行一系列操作并返回一个响应数组, 这个数组中的元素对应参数列表中的响应操作的执行结果

  • 通过bitfield可以一次性对多个比特位域进行操作

10. Stream --- 流

理解:
  • 主要用于消息队列 (MQ) , Redis本身是有一个Redis发布订阅(pub / sub) 来实现消息队列的功能

  • 但它有个缺点就是消息无法持久化, 如果出现网络断开, 宕机, 消息就会被丢弃

  • 发布订阅可以分发消息, 但是无法记录历史消息

  • 提供了持久化和主备复制功能, 可以让任何客户端访问任何时刻的数据, 并且能记住每一个客户端的访问位置, 还能保证消息不丢失

  • 就是redis版本的MQ, 消息中间件

第三章 Redis常用指令

命令说明
keys *查看当前库所有的key
exists key判断某个key是否存在
type key查看你的key是什么类型
del key删除指定的key数据
unlink key非阻塞删除, 仅仅将keys从keyspace元数据中删除, 真正的删除会在后续异步中操作
ttl key查看还有多少秒过期, -1表示永不过期, -2表示已过期
expire key 秒钟为给定的key设置过期时间
move key dbindex [0-15]将当前数据库的key移动到给定的数据db当中
select dbindex [0- 15]切换数据库[0-15], 默认为0
dbsize查看当前数据库key的数量
flushdb清空当前库
flushall通杀全部库

第四章 Redis的缓存机制和持久化


缓存机制:

  • 数据存储在内存中

  • 数据过期管理

    • 设置Key的过期时间

    • 删除策略 定期清除过期的键,同时采用惰性删除机制(访问时发现键已过期即删除)

    • 内存淘汰策略

      • noeviction:默认策略,不淘汰数据,写操作失败。

      • allkeys-lru:基于 LRU(最近最少使用)算法淘汰任意键。

      • volatile-lru:基于 LRU 算法淘汰设置了过期时间的键。

      • allkeys-random:随机淘汰任意键。

      • volatile-random:随机淘汰设置了过期时间的键。

      • volatile-ttl:淘汰最近要过期的键。

RDB (Redis DataBase)

理解:
  • 类似于快照, 打包成一组数据保存到磁盘, 恢复的时候, 再将硬盘快照文件直接读回到内存中

  • 在指定的时间间隔, 执行数据集的时间点快照

  • 把某一时刻的数据和状态以文件(dump.rdb)的形式写道磁盘上, 这样就算服务宕机了, 快照文件也不会丢失

关于版本之间快照的时间变化:
  • redis6

    • 每隔900s 如果有超过1个key变化, 就重写一份新的RDB文件

    • 每隔300s 如果有超过10个key变化, 就重写一份新的RDB文件

    • 每隔60s 如果有超过10000个key变化, 就重写一份新的RDB文件

  • redis7

    • 每隔3600s 如果有超过1个key变化, 就重写一份新的RDB文件

    • 每隔300s 如果有超过100个key变化, 就重写一份新的RDB文件

    • 每隔60s 如果有超过10000个key变化, 就重写一份新的RDB文件

官网说明 RDB
优势:
  • RDB是Redis数据的一个非常紧凑的单文件时间点表示, RDB文件非常适合备份

  • RDB最大限度的提高了Redis的性能, 因为Redis父进程为了持久化而需要做的唯一工作就是派生一个将完成所有其余工作的子进程, 父进程永远不会执行磁盘IO或类似操作

  • 与AOF相比, RDB允许使用大数据集更快的重启

  • 在副本上 , RDB支持重启和故障转移后的部分重新同步

劣势:
  • 在一定间隔时间做一次备份, 所以如果redis意外down掉的话, 就会丢失从当前至最近一次快照期间的数据, 快照之间的数据会丢失

  • 内存数据的全量同步, 如果数据量太大导致IO严重影响服务器性能

  • RDB依赖主进程fork, 在更大的数据集中, 这可能会导致服务请求的瞬间延迟, fork的时候内存中的数据被克隆了一份, 大致2倍的膨胀性, 需要考虑

触发两种方式:
  • 自动触发

    • 每次操作都会备份, 如果flushdb删除了库, 这时候的redis为了数据统一也会是空的文件

    • 如果自动恢复

      • 先关闭redis服务

      • 在关闭redis服务时会产生一个dump.rdb文件, 这时候先将这个文件删除

      • 在将没有没有删除备份的文件移动到本路径下同名的文件, 这样redis启动的时候会直接读取, 文件会直接恢复

      • 注意:

        • 不可以把备份文件dump.rdb和生产redis服务器放在同一台机器, 必须分开各自存储, 以防生产物理损坏后备份文件也挂了

  • 手动触发

    • 两种命令

      • save

        • 在主程序中执行会阻塞当前redis服务器, 直到持久化工作完成执行save命令期间, redis不能处理其他命令, 线上禁止使用

      • bgsave

        • Redis会在后台异步进行快照操作, 不阻塞快照同时还可以响应客户端请求, 该触发方式会fork一个子进程由子进程复制持久化过程

        • 相当于又开了一个黑窗口来执行手动备份

      • lastsave

        • 获取最后一次成功执行快照的时间

哪些情况会触发RDB快照:
  • 配置文件中默认的快照配置

  • 手动save/bgsave命令

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

  • 执行shutdown且没有设置开启AOF持久化

  • 主从复制时, 主节点自动触发

如果禁用RDB:
  • 动态所有停止RDB保存规则的方法: redsi-cli config set save " "

  • 在配置文件中禁用

    • 在SNAPSHOTTING下的 save “ ” 将这个注释打开即可

RDB优化:
  • 配置文件中:

    • stop-writes-on-bgsave-error

      • 默认yes

      • 如果配置成no, 表示你不在乎数据不一致或者有其他的手段发现和控制这种不一致, 那么在快照写入失败时, 也能确保redis继续接受新的写请求

    • rdbcompressioon

      • 默认yes

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

    • rdbchecksum

      • 默认yes

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

    • rdb-del-sync-files

      • 默认no

      • 在没有持久性的情况下删除复制中使用的RDB文件启用

修复RDB文件:
  • redis-check-rdb [需要修复的rdb文件位置]

AOF (Append Only File)

理解:
  • 将执行的指令记录下来, 恢复的时候重新执行一边所有记录的指令

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

开启操作:
  • 开启需要在配置文件中 appendonly yes

  • 设置默认写回策略 appendfsync everysec 默认

  • aof文件-保存路径

    • redis 6

      • 都保存在统一个文件夹下面

    • redis 7

      • 在原有dir文件路径地址下的appendonlydir文件里

  • aof文件-保存名称

    • redis 6

      • 只有一个存储文件

    • redis 7

      • MP-AOF实现

        • BASE(基本文件) : 表示基础AOF, 它一般由子进程通过重写产生, 该文件最多只有一个

        • INCR(增量文件): 表示增量AOF, 它一般回在AOFRW开始执行时被创建, 该文件可能存在多个

        • manifest(清单文件):

AOF持久化流程
  • 找到请求的命令

  • 在到达Redis Server 以后并不是直接写入AOF文件, 会将这些命令先放入AOF缓存中进行保存, 这里的AOF缓冲区实际上是内存中的一片区域, 存在的目的是当这些命令达到一定量以后再写入磁盘, 避免频繁的磁盘IO操作

  • AOF缓冲会根据AOF缓冲区同步文件三种写回策略将命令写入磁盘上的AOF文件

  • 随着写入AOF内容的增加为避免文件膨胀, 会根据规则进行命令的合并(又称AOF重写), 从而起到AOF文件压缩的目的

三种写回策略(appendfsync):
  • Always

    • 同步写回, 每个写命令执行完立刻同步地将日志写回磁盘

  • everysec

    • 每秒写回, 每个写命令执行完, 只是先把日志写道AOF文件的内存缓冲区, 每隔1秒把缓冲区中的内容写入磁盘

  • no

    • 操作系统控制的写回, 每个写命令执行完, 只是先把日志写到AOF文件的内存缓冲区, 由操作系统决定何时将缓冲区内容写回磁盘

配置项写回时机优点缺点
Always同步写回可靠性高, 数据基本不丢失每个写命令都要落盘, 性能影响较大
Everrysec每秒写回性能适中宕机时丢失1秒内的数据
No操作系统控制的写回性能好宕机时丢失数据较多

异常恢复AOF文件:
  • 在/usr/local/bin 中的指令

  • 进入AOF存储目录里 执行以下命令进行修复

  • redis-check-aof --fix appendonly.aof.1.incr.aof

AOF优势:
  • 更好的保护数据不丢失, 性能高, 可做紧急恢复

AOF劣势:
  • 相同数据集的数据而言aof文件要远大于rdb文件, 恢复速度慢于rdb

  • aof运行效率要慢于rdb, 每秒同步策略效率较好, 不同步效率和rdb相同

AOF重写机制:
  • 启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集

  • 也就是说AOF文件重写并不是源文件进行重新整理, 而是直接读取服务器现有的键值对, 然后一条命令去代替之前记录这个键值对的多条命令, 生成一个新的文件后去替换原来的AOF文件

触发机制:
  • 自动触发

    • auto-aof-rewrite-min-size 来进行配置文件阈值到多少开始重写

    • 满足配置文件中的选项后, Redis会记录上次重写时的AOF大小, 默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时

  • 手动触发

    • 客户端向服务端发送bgrewriteaof命令

原理:
  • 重写aof文件的操作, 并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写一个新的aof文件, 这点和快照有点类似

配置文件:
配置指令配置含义配置示例
appendonly是否开启aofappendonly yes
appendfilename文件名称appendfilename "appendonly.aof"
appendfsync同步方式everysec/always/no
no-appendfsync-no-rewriteaof重写期间是否同步no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage auto-aof-rewrite-min-size重写触发配置, 文件重写策略auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb

AOF + RDB 混合持久化

  • 如果同时开启 AOF 优先级最高

  • 重启时只会加载AOF文件, 不会加载RDB文件

开启方式:
  • 设置 aof-use-rdb-preamble 的值为 yes

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

  • RDB的数据不实在, 同时使用两者时服务器重启也只会找AOF文件, 那要不要只用AOF?作者建议不要, 因为RDB更上泳于碑文数据库(AOF在不断变化不好备份),留着rdb作为一个万一的手段

结论:
  • RDB镜像做全量持久化

  • AOF做增量持久化

  • 先使用RDB进行快照存储, 然后使用AOF持久化记录所有的写操作, 当重写策略满足或手动触发重写的时候, 将最新的数据存储为新的RDB记录, 这样的话, 重启服务的时候会从RDB和AOF两部分恢复数据, 既保证了数据的完整性, 又提高了恢复数据的性能

纯缓存模式

配置文件中, 不是指令

  • save “ ”

    • 禁用rdb

    • 禁用rdb持久化模式下, 我们仍然可以是哟个命令save,bgsave生成tdb文件

  • appendonly no

    • 禁用aof

    • 禁用aof持久化模式下, 我们仍然可以使用命令bgrewriteaof生成aof文件

第五章 Redis事务

概述:

是什么
  • 可以一次执行多个命令, 本质是一组命令的集合, 一个事务中的所有命令都会序列化, 按顺序的串行化执行而不会被其他命令插入, 不许加塞

能干嘛
  • 一个队列中, 一次性, 顺序性, 排他性的执行一系列命令

Redis事务 VS 数据库事务
  • 单独的隔离操作

    • Redis是事务仅仅是保证事务里的操作会被连续独占的执行, redis命令是单线程架构, 在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的

  • 没有隔离级别的概念

    • 因为事务提交前任何指令都不会被实际执行, 也就不存在"事务内的查询要看到事务里的更新, 在事务外查询不能看到"这种问题

  • 不保证原子性

    • Reids的事务不保证原子性, 也就是不保证所有指令同时成功或同时失败, 只有决定是否开始执行全部指令的能力, 没有执行到一半进行回滚的能力

  • 排他性

    • Redis会保证一个事务内的命令依次执行, 而不会被其他命令插入

使用操作:

事务命令:
命令说明
DISCARD取消事务, 放弃执行事务块内的所有命令
EXEC执行所有事务块内的命令
MULTI标记一个事务块的开始
UNWATCH取消WATCH命令对所有key的监视
WATCH key [key…]监视一个或多个key, 如果在事务执行之气那这个key被其他命令所改动, 那么事务将被打断
  • redis 没有回滚操作

  1. 编译时出错, redis会让用户提交错误

  2. 运行时异常, radis会将成功的成功, 失败的失败, 没有原子性

  3. 如果在事务中修改一个数据的同时有其他线程提前修改了, 这也会导致redis事务里的修改指令失败

第六章 Redis管道

问题:

  • 客户端向服务端发送命令分四步, 并监听Socket返回, 通常以阻塞模式等待服务端响应

    1. 发送命令

    2. 命令排队

    3. 命令执行

    4. 返回结果

  • 服务端处理命令, 并将结果返回给客户端

上述两部称为: Roud Trip Time (简称RTT, 数据包往返于两端的时间) 往返时间

使用好处:

  • 管道(pipeline)可以一次性发送多条命令给服务端, 服务端依次处理完完毕后, 通过一条响应一次性将结果返回, 通过减少客户端于redis的通信次数来实现降低往返延时时间, pipeline实现的原理是队列, 先进先出特性就保证数据的顺序性.

定义:

  • 为了解决RTT往返回时, 仅仅时将命令打包依次性发送, 对整个Redis的执行不造成其他任何影响

  • 批处理命令的变种优化措施, 类似Redis的原生批名令(mget和mset)

操作方法:

  • 第一种以文件格式存储命令, 统一执行, 使用了linux的管道命令

    • cat reids.txt | redis-cli -a damao --pipe

  • 第二种使用redis原生命令

    • mset k1 v1 k2 v2

区别:

  • 命令对比

原生批量命令pipeline(使用redis管道批量执行)
带有原子性非原子性
一次只能执行一种命令支持批量执行不同命令
是服务端实现的需要客户端和服务端共同完成
  • 事务对比

    • 事务具有原子性, 管道不具有原子性

    • 管道一次性将多条命令发送到服务器, 事务时一条一条的发, 事务只有在接收到exec命令后才会执行, 管道不会

    • 执行事务时会阻塞其他命令的执行, 而执行管道中的命令不会

  • pipeline 缓冲的指令是会依次执行, 不保证原子性, 如果执行中指令发生异常, 将会继续执行后续的指令

  • 使用pipeline组装的命令个数不能太多, 不然数据量过大客户端阻塞的时间可能更久, 同时服务端此时也被迫回复一个队列答复, 占用很多内存

第七章 Redis复制(replica)

理解:

  • 就是主从复制, master以写为主, Slave以读为主

  • 当master数据变化的时候, 自动将心的数据异常同步到其他slave数据库

作用:

  • 读写分离

  • 容灾恢复

  • 数据备份

  • 水平扩容支撑高并发

怎么玩:

  • 配从不配主

  • 权限配置

    • master如果配置了requirepass参数, 需要密码登录

    • 那么slave就要配置masterauth来设置校验密码, 否则的话master会拒绝slave的访问请求

  • 命令:

    • info replication

      • 可以查看复制节点的主从关系和配置信息

    • replicaof 主库IP 主库端口

      • 一般写入进redis.conf配置文件内

    • slaveof 主库IP 主库端口

      • 每次与master断开之后, 都需要重新连接, 除非你配置进redis.conf文件

      • 在运行期间修改slave节点的信息, 如果该数据库已经时某个主数据库的从数据库, 那么会停止和原主数据库的同步关系转而和新的主数据库同步, 重新拜码头

    • slaveof no one

      • 使当前数据库停止与其他数据库的同步, 转成主数据库, 自立为王

工作流程:

  • slave启动, 同步初请

    • slave启动成功后连接到master后会发送一个sync命令

    • 首次连接, slave会被清楚数据, 同步master的数据, 直接覆盖原有数据

  • 首次连接, 全量复制

    • master节点收到sync命令后会开始在后台保存快照 ( 即RDB持久化, 主从复制时会触发RDB ) , master节点执行RDB持久化之后, master会将rdb快照文件和所有缓存的命令发送到所有slave, 以完成依次完全同步

    • slave收到文件后, 保存到磁盘中, 加载到内存, 从而完成初始化

  • 心跳持续, 保持通信

    • 每10秒钟发送对接消息, 查看都是否存在

  • 计入平稳, 增量复制

    • Master继续将心的所有收集到的修改命令自动依次传给slave, 完成同步

  • 从机下线, 重连续传

    • Master只会把已经复制的oppset后面的数据复制给slave

缺点:

  • 默认情况下, 主机宕机后, 不会在slave节点中自动重选一个master

第八章 Redis哨兵

原理:

  • 监控后台master主机是否故障, 如果故障了根据投票数自动将某有一个从库转换为新主库, 继续对外服务

  • 多台哨兵在一起投票, 避免一台哨兵因为网络而误判

作用:

  • 主从监控

    • 监控主从redis库运行是否正常

  • 消息通知

    • 哨兵可以将故障转移的结果发送给客户端

  • 故障转移

    • 如果Master异常, 则会进行主从切换, 将其中一个Slave作为新的Master

  • 配置中心

    • 客户端通过连接哨兵来获得当前Redis服务的主节点地址

运行流程和选举原理

  • 当一个主从配置中的master失效后, sentinel可以选举出一个新的master用于自动接替原master的工作, 主从配置中的其他redis服务器自动指向新的master同步数据, 一般建议sentinel采用奇数台, 防止某一台sentinel无法连接到master导致误切换

主观下线:
  • 单个sentinel认为某个服务下线, (当然有可能是接收不到订阅, 之间的网络不通等等原因)

  • 如果服务器在sentinel 给定的毫秒数之内没有回复,ping命令或者返回一个错误, 那么这个sentinel会主观的认为这个master不可用了

客观下线:
  • 多个哨兵达成一致的意见才能认为一个master客观上已经宕掉了

选举master流程:

哨兵选举:

  • 哨兵进行协商选举一个领导者, 由领导者进行选择下一任master是谁

  • 选举是Raft算法实现

master选举:

  1. 新主登基

    1. 优先级

    2. 复制偏移量

    3. 最后的Run ID的节点

  2. 群臣俯首:

    1. 哨兵会给新的master执行slaveof no one操作, 将其提升为master节点

    2. 哨兵向其他slave发送命令, 让剩余的slave成为新的master节点的slave

  3. 旧主拜服

    1. 老master回来之后,

    2. sentinel leader会让原来的master降级为slave并恢复正常工作

哨兵建议

  • 哨兵节点数量应为多个, 哨兵本身应该集群, 保证高可用

  • 哨兵节点的数量应该是奇数

  • 各个哨兵节点的配置应一致

  • 如果哨兵节点部署在Docker等容器里面, 尤其要注意端口的正确映射

  • 哨兵集群+ 主从复制, 并不能保证数据零丢失

  • 集群弥补以下的缺陷

第九章 Redis集群

作用:

  • Redis集群支持多个Master, 每个Master又可以挂载多个Slave

    • 读写分离

    • 支持数据的高可用

    • 支持海量数据的读写存储操作

  • Cluster自带Sentinel的故障转移机制, 内置了高可用的支持, 无需在去使用哨兵功能

  • 客户端与Redis的节点连接, 不在需要连接集群中所有的节点, 只需要任意连接集群中的一个可用节点即可

  • 槽位slot负责分配到各个物理服务节点, 由对应的集群来负责维护节点, 插槽和数据之间的关系

槽位slot:
  • Redis集群由 16384 个哈希槽

  • 将槽位均匀分布到master集群上

分片:
  • Redis集群会将存储的数据分散到多台redis机器上, 具体分布到机器上的称为分片

  • 如何找到给定的分片:

    • 对key进行CRC16算法处理并通过对总分片数量取模, 然后, 使用确定性哈希函数, 这意味着给定的key将多次始终映射到同一个分片, 我们可以推断将来读取特定key的位置

槽位和分片两个的优势:
  • 这种结构很容易添加或者删除节点

  • 由于从一个节点将哈希槽移动到另一个节点并不会停止服务

  • 所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群的不可用的状态

slot槽位映射,三种解决方案:
  • 哈希取余分区

    • 优点

      • 简单粗暴, 直接有效

    • 缺点

      • 缺点一台redis宕机了就要重新洗牌

  • 一致性哈希算法分区

  • 哈希槽分区

第十章 布隆过滤器

定义:

  • 由一个初值都为零的bit数组和多个哈希函数构成, 用来快速判断集合中是否存在某个元素

设计思想:
  • 本质就是判断具体数据是否在于一个大的集合中

特点:

  • 高效的插入和查询, 占用空间少, 返回的结果是不确定性 + 不够完美

  • 目的: 减少内存占用, 不保存数据信息, 只是在内存中做一个是否存在的标记flag

  • 重点: 一个元素如果判断结果: 存在时, 元素不一定存在, 但是判断结果为不存在时, 则一定不存在

  • 布隆过滤器可以添加元素, 但是不能删除元素, 由于设计hashcode判断依据, 删掉元素会导致误判率增加

  • 如果布隆过滤器判断一个元素不在一个集合中, 那这个元素不一定不会在集合中

  • 因为映射函数本身就是散列函数, 散列函数是会有碰撞的

布隆过滤器原理:

  • 是一个专门用来解决去重问题的高级数据结构

  • 实质就是一个大型位数组和几个不同的无偏hash函数(无偏表示分布均匀), 由一个初值都为零的bit数组和多个哈希函数构成, 用来快速判断某个数据是否存在, 但是跟HyperLogLog一样, 它也一样有一点点不精确, 也存在一定的误判概率

什么是缓存穿透:

  • 一般情况下, 先查询缓存redis是否有该条数据, 缓存中没有, 在查询数据库. 当数据库也不存在该条数据时, 每次查询都要访问数据库, 这就是缓存穿透

  • 缓存穿透带来的问题是:

    • 当有大量请求查询数据库不存在的数据时, 就会给数据库带来压力, 甚至会拖垮数据库

布隆过滤器的使用场景:

  • 解决缓存穿透问题, 和redis结合bitmap使用

  • 黑名单校验, 识别垃圾邮件

  • 安全连接网址, 全球上10亿的网址判断

第十一章 缓存预热+缓存雪崩+缓存击穿+缓存穿透

1. 缓存预热

  • @PostConstruct 初始化白名单数据

2. 缓存雪崩

发生:
  • redis主机挂了, redis全盘崩溃, 偏硬件运维

  • redis中有大量key同时过期大面积失效, 偏软件开发

预防+解决:
  • redis中key设置永不过期或者过期时间错开

  • redis缓存集群实现高可用

    • 主从 + 哨兵

    • Redis Cluster集群

    • 开启Redis持久化机制aof/rdb, 尽快恢复缓存集群

  • 多缓存结合预防雪崩

    • ehcache本地缓存 + redis缓存

  • 服务降级

    • Hystrix 或者sentinel限流且降级

  • 设置随机过期时间 TTL 防止缓存雪崩问题.

  • 人民币玩家

3. 缓存穿透

是什么
  • 查询一条记录, 先查redsi无, 后查mysql无, 都查询不到该记录, 但是请求每次都会打到数据库上面区, 导致后台数据库压力暴增, 这种现象我们称为缓存穿透

  • 两库都没有的数据, 一直访问, 数据库在被多次暴击风险

解决:

第一种:

  • 布隆过滤器记录是否存在这个, 如果不存在, 则就直接返回为空

  • 直接隔离在redis, mysql之前了

缺点:

  • 布隆过滤器会有误判

第二种:

  • 第一次打到mysql, 空对象缓存后第二次就返回defaultNull, 避免mysql被攻击

  • 记得要写null值的redis值设置过期时间

缺点:

  • 如果故意访问空值, 内存占用会越来越大

4. 缓存击穿

是什么
  • 大量的请求同时查询一个key时, 此时这个key正好失效了, 就会导致大量的请求都打到数据库面去

  • 热点的key突然失效了, 暴打mysql

理解:

  • 有这个东西, 是在redis失效的一瞬间, 大量的请求直接打到数据库上面

解决
  • 双检双锁 , 单机就用jvm锁, 分布式系统就用分布式锁

  • 热点key直接不设置过期时间

缓存扩展知识

单机QPS能抗住多少?

  • 有报告提到,Redis单机在默认配置下可以达到115074.80 requests per second(即约11万5千次请求/秒)。

乐观锁 ABA 问题

乐观锁中的ABA问题可以通过以下几种方法来解决:

  1. 引入版本号:每次变量更新时,版本号加一。这样,即使值从A变为B再变回A,版本号也会增加,从而避免了CAS操作中的误判。

  2. 时间戳版本号:为数据添加一个时间戳版本号。每次更新数据时,时间戳也会递增。CAS操作时,除了比较值之外,还需要比较时间戳,以确保数据自上次更新以来没有被修改。

  3. 双向链表:使用双向链表将数据连接起来。每个节点包含数据值和指向前后节点的指针。更新数据时,需要同时修改链表中的多个节点,这使得ABA问题不太可能发生。

  4. 锁保护:如果并发冲突频繁发生,可以使用锁来保护共享数据。虽然这会降低并发性,但可以有效避免ABA问题。

这些方法通过增加额外的检查或使用不同的数据结构来确保在并发环境下数据的一致性和正确性。通过这些策略,可以有效地解决或避免ABA问题,从而提高程序的可靠性和稳定性。

什么是缓存延迟双删策略?

缓存延迟双删策略,顾名思义,就是在更新数据库时,通过两次删除缓存来保证数据的一致性。

它的基本思想是先删除缓存,再更新数据库,最后再次删除缓存。之所以要这样做,是因为在并发场景下,可能会存在更新后的缓存再次被旧数据污染的情况,因此需要通过两次删除缓存来防止这种情况发生。

具体步骤: 假设你有一个涉及缓存和数据库的数据更新操作,那么缓存延迟双删策略的步骤如下:

  • 第一次删除缓存:在执行数据库更新操作之前,先删除缓存中与该数据对应的条目。这是为了防止在接下来的数据库操作过程中,有其他线程请求旧的缓存数据。

  • 更新数据库:执行数据更新的操作,将数据写入数据库。

  • 延迟一段时间后再删除缓存:由于在并发环境下,可能在数据库更新后有其他线程写入了旧数据到缓存中,所以我们需要等待数据库事务提交,并延迟一段时间后再次删除缓存。这个延迟时间的设定需要小心平衡,因为时间过短可能无法有效防止旧数据写入缓存,时间过长则可能影响性能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值