当前位置: 首页 > article >正文

python全栈-Redis从入门到开发

Redis从入门到开发

文章目录

  • NoSQL四大类
  • Redis
  • 数据类型
    • key键
    • string字符串
    • list 列表
    • set 字典
    • hash 哈希
    • zset 有序字典
    • bitmaps
    • geospatia(GEO)
    • hyperloglog
  • Redis可视化工具redis desktop manager
  • redis的配置文件
  • redis功能
    • 发布与订阅
    • 慢查询
    • 流水线pipeline
  • redis数据安全
    • RDB 按时间间隔,存储一次内存快照
    • AOF
  • redis事务
  • 集群
    • 主从复制环境搭建
    • 主从复制原理剖析
    • 哨兵机制
    • 哨兵监控环境搭建
    • 哨兵工作原理详解
    • 故障转移
    • Cluster模式
    • Cluster模式搭建
  • redis问题
    • redis脑裂
    • 缓存预热
    • 缓存穿透
    • 缓存击穿
    • 缓存雪崩
  • Redis开发规范
  • 数据一致性
    • 先更新数据库,再更新缓存
    • 先删缓存,再更新数据库
    • 先更新数据库,再延时删缓存

NoSQL四大类

  • KV型—Redis
    • 类似键值对,key与value
    • 数据基于内存
    • 读写快
    • 性能高
    • 10万级别
  • 列式----HBase
    • 按列读取数据库
    • 超大数据库
  • 文档型—MongoDB
    • 博客,字多
    • json字符串格式存储
  • 搜索型—ElasticSearch
    • 淘宝
    • 重点在搜索,存目录

NoSQL数据库,不支持SQL,没有事务处理

Redis

支持分布式,理论可以无限扩展

  • Redis-server 服务器启动
  • redis-cli 客户端

去usr/local/redis-6.2.6/src目录下

使用命令./redis-server,打开客户端

看port默认端口号6379,pid是1568

目前来说,直接打开server就是前端。我们要让它后台运行

  • 需要修改redis.conf文件
  • daemonize yes # 从no改成yes
  • 启动服务./redis-server …/redis.conf
  • 检查是否运行lsof -i:6379
  • 连接redis,./redis-cli

通过docker的方式安装redis

  1. 安装docker,yum install -y docker
  2. 运行docker,service docker start
  3. 下载redis镜像,docker pull redis,默认下载最新版redis
  4. 检查redis的镜像文件,docker images,如果有docker.io/redis就成功了
  5. 启动docker容器,docker run 容器名
  6. 后台运行,docker run -d --name myredis -p 6379:6379 redis 映射端口号到容器,然后返回容器的id
  7. 检查容器,docker ps,有up就是运行
  8. 连接容器,docker exec -it myredis /bin/bash 进入容器内部
  9. 在容器里面连接redis,使用命令redis-cli,显示127.0.0.1:6379 就成功了,并且redis是联机状态了,刚刚上面那种方式是单机redis,就跟MySQL一样
  10. 退出redis,exit
  11. 退出容器,exit
  • 限制redis读取速度的是内存操作io接口,与cpu无关

  • 因此是单线程程序

redis采用网络io多路复用技术保证多连接的时候,系统的吞吐量

  • redis默认有16个库,也就是一个MySQL有16个数据库,下标从0开始

  • 往redis里面添加数据,使用set key value的方式,比如set k1 20

  • 获取数据的值,get 键名,比如get k1,得到20

  • 切换数据库,从默认的0库,到1库,就是select 1,后面的就是数据库的下标

  • 数据库的数据不是通用的。

  • 我们直接创建的键值对,存放在0库里面,当我们使用select切换到1库之后,就无法get了

  • 清空数据库,flushdb,清空当前数据库的所有键

  • 清空所有数据库,flushall

数据类型

key键

  • keys *
    • 查看数据库中所有的键
    • 可以匹配通配符
      • *任意多个字符
      • ?单个字符
      • []匹配括号内的任意字符
    • 因为是单线程,大量的查询会占用线程,不建议使用
    • 替换方案:使用./redis-cli --scan “*” 在引号里面写匹配条件,不会占用线程,不会阻塞
  • exists key
    • 判断键是否存在
    • 存在返回1,否则是0
    • 可以一次性判断多个键是否存在,返回值是累加和,比如exists k1 k2 k3,如果都存在,返回3,有任意一个不存在就是1+1+0=2
  • type key
    • 查看键所储存的值的类型
  • del key
    • 删除key,可以一次性删除多个键,返回1,也是累加,失败是0
  • expire key time
    • 给key设置过期时间,以秒为单位
  • ttl key
    • 检查key剩余的过期时间
    • key过期,就返回-2
    • key没有设置过期时间,返回-1
  • persist key
    • 给有过期时间的key,改成永不过期,成功返回1,否则返回0
    • 永不过期,ttl就是-1

string字符串

最多放512m的数据,是一个二进制文件数据,可以放任何数据。

  • set key value

    • 设置值
  • get key

    • 获取值
    • 如果key不存在,返回nil
  • append key value

    • 在值的后面追加数据,因为数据是字符串,可以追加
  • strlen key

    • 获取值的长度
  • setex key time value

    • 重新设计键的过期时间,一般用于验证码的设计
  • setnx key value

    • 只有键不存在,才可以设置键的值
  • gettange key start end

    • 获取指定区间范围内的值,类似MySQL的between…and
    • 比如,set k1 abcd12346666
    • getrange k1 4 8,获取键k1的值的第4到8位的字符,即12346
  • setrange key offset value

    • 替换指定位置的字符
    • 第三个参数是开始替换的下标
    • setrange k1 3 999 ,k1=abc999346666
  • incr key

    • 给键的值加一
    • 前提是这个键的值是数值,不能是非数字的字符串
    • 统计网站的访问次数
  • decr

    • 给键的值减一,和incr的用法相反
    • 类似博客,你删除了一篇文章,你的文章数减一
  • incrby/decrby key step

    • 自定义键的增减数量
  • met key1 value1 key2 value2

    • 同时设置多个键值对
  • mget key1 key2

    • 一次性获取多个键的值
  • getset key value

    • 先拿到旧的值,再给这个键设置新的值。 能返回旧值

使用场景:

  1. 计数器
  2. 粉丝数
  3. 缓存商品信息
  4. 分布式锁

list 列表

字符串列表,底层是双向列表

  • lpush/rpush key1 value1 value2…
    • 头插,尾插,插入一个或多个值
  • lrange key start end
    • 拿列表的数据
    • 拿列表的从开始到结束的元素
    • -1是最后一个元素,搭配lrang key 0 -1,获取列表所有元素
  • lpop/rpop key
    • 移除列表第一个元素或最后一个元素
    • 头删,尾删
  • lindex key index
    • 获取下标值位置
    • lindex k1 0 获取k1列表第0个元素的值
    • 下标从0开始
  • llen key
    • 获取键的长度
  • lrem key count value
    • 从左边开始,删除count个值是value的元素
  • linsert key before/after value newvalue
    • 从左边开始,在值是value的前面或后面,插入一个新元素
  • lset key index value
    • 通过列表的下标,重新设置值

使用场景:

  1. 排行榜
  2. 消息队列
  3. 最新列表

set 字典

与list类似,都是列表,但是set会自动去重

是string类型的无序集合,底层是一个value值都是null的hash表,也就是字典,增删查改的时间是o(1)

  • sadd key value1 value2 value3…
    • 将一个或多个元素添加到列表里面,已经存在的元素被忽略
    • 返回值是列表成功保存元素的个数
  • smembers key
    • 获取集合里面所有的值
  • sismember key value
    • 判断集合key中,是否有value值的元素
    • 有返回1,没用返回0
  • scard key
    • 返回集合元素的个数
  • srem key value1 value2。。。
    • 删除集合key的值,没有的元素就忽略
  • spop key count
    • 随机删除count个元素,并返回该元素
  • srandmember key count
    • 随机取出集合count个元素,但不会删除
  • smove key1 key2 value
    • 把value元素从key1集合拿出来,拿到key2里面去
  • sinter key1 key2
    • 返回两个集合的交集,就是俩集合都有的元素值
  • sunion key1 key2
    • 返回两个集合的并集
  • sdiff key1 key2
    • 在key1里面,但是不在key2的元素,就是key1与key2的差集
    • 就是把key1集合中,移除key2 的元素

使用场景:

  1. 黑白名单
  2. 随机展示
  3. 好友
  4. 粉丝
  5. 感兴趣的人

hash 哈希

本质是键值对的集合,也就是字典

  • hset 对象 键 值
    • hset person key1 value1 给对象,设置键值对key1:value1
    • hset person key2 value2 键值对类似对象的属性,就是一个对象有多个属性
  • hget 对象 键
    • hget person key1 获取对象的键的值
  • hmset 对象 键1 值1 键2 值2 。。。
    • 一次性对一个对象,设置多个键值对
  • hexists 对象 键
    • hexists person key1 查看对象里面有没有key1这个键,有返回1,没有返回0
  • hkeys 对象
    • 获取对象里面的所有的键
  • hvals 对象
    • 获取对象里面所有的值
  • hincrby 对象 键 count
    • 给对象的某个键的值,增加或者减少count
    • 返回值修改后的结果
  • hdel 对象 键1 键2 。。。
    • 删除对象里面的键1,键2。。。
  • hsetnx 对象 键 值
    • 只能给对象,不存在的键赋值,也就是给对象增加键值对,这个键不能存在,如果存在,就不能增加新的键值对,并且也不能修改原来的键值对

使用场景:

  1. 购物车
  2. 存储对象

zset 有序字典

与set类型,但是多了一个score分数功能,是专门用来排序的属性

也就是比set多了一个排序

通俗来说,就是每一个zset对象,都有一个score属性,我们通过这个属性,可以给所有对象排序

  • zadd 对象 分数1 值1 分数2 值2 。。。
    • 把一个或多个键值对添加到对象里面
  • zrange 对象 开始 结束 [withscores]
    • 返回对象下标从开始到结束的元素的值,包含开始和结束的元素
    • 类似list里面的lrange,如果开始是0,结束是-1,就是取出所有元素的值
    • 如果把后面的withsocres参数写上,就可以显示分数了。值1,分数1,值2,分数2
  • zrangebyscore 对象 开始 结束 [withscores]
    • 显示某个区间内的值,刚刚是按照下标取值
    • 现在是按照分数的区间取值
  • zincrby 对象 count 值
    • 给对象的某个值,增加count
  • zrem 对象 value
    • 删除对象下面的某个值,联通的分数也会删除
  • zcount 对象 最小值 最大值
    • 统计在对象中,分数在最小值和最大值之间的键值对的个数
  • zrank 对象 value
    • 返回对象中值的排名,从0开始

使用场景:

  1. 延时队列
  2. 排行榜
  3. 限流

bitmaps

对二进制位的操作

本质是一个数组,数组的元素就是二进制的位

  • setbit key offset value

    • 设置某个偏移量的值

    • 统计用户信息:活跃天数,打卡天数,登录天数

    • 因为是二进制位,只有0和1两个状态,通过统计01完成数据的统计,因为只占一个比特位,节省空间

    • 下标从0开始

    • setbit zhangsan:3 0 1
      setbit zhangsan:3 1 1
      setbit zhangsan:3 2 0
      setbit zhangsan:3 3 0
      setbit zhangsan:3 4 1
      setbit zhangsan:3 5 1    我们设置的对象是zhangsan:3,自定义的意思是张三三月份打卡次数。后面的第一个数值是占的位,第二个数值是哪个位赋的值。然后我们统计了张三三月份的打卡情况,就是110011,然后统计10的个数,计算打卡次数
      
  • getbit key offset

    • 获取key对象里面的某个位的值
    • getbit zhangsan:3 3 获取张三三月份第4位的值,是0.
  • bitcount key [start end]

    • 统计key中,位是1的数量,后面也可以指定范围,统计这个范围内位的数量情况
  • bitop and/or 新数组 旧数组1 旧数组2

    • 前面是and,就是把两个旧数组的交集,放到新数组里面,有1则1
    • 前面是or,就是我两个旧数组的并集放到新的数组里面,有0则0

使用场景:

  1. 活跃天数
  2. 打卡天数
  3. 登录天数
  4. 用户签到
  5. 统计活跃用户
  6. 统计活跃用户是否在线
  7. 布隆过滤器

geospatia(GEO)

就是定位信息,提供经纬度

应用场景:定位查询,范围查询,距离查询

  • geoadd 对象 经度1 纬度1 坐标名称1 。。。
    • 设置一个或多个经纬度坐标
  • geopos 对象 坐标名称1 。。。
    • 取值
    • 取坐标名称的经纬度
  • geodist 对象 坐标名称1 坐标名称2 [单位参数]
    • 计算两点之间的距离,参数可以是m米,km千米,mi英里,ft英尺
  • georadius 对象 经度 纬度 半径 单位参数
    • 以给定的经纬度为中心,返回不超过最大半径的所有位置元素
    • 返回刚刚我们创建的坐标名称
    • 就是范围查询,比如微信之前的功能:附近人。美团的附近商店等等

实现场景:

  1. 附近电影院
  2. 附近好友
  3. 最近的火锅店

hyperloglog

统计数据,PV网站统计浏览量,UV游客数量

我们在统计每天的用户时候,都是把访问网站的用户,用户名存放到一个列表里面,这个列表就是数据集。

我们把这个数据集里面相同的元素进行去重操作,得到的就是基数集。这个基数集就是统计用户数的

应用场景是超大数据统计,快速计算

  • pfadd 对象 元素1 元素2 。。。
    • 添加元素到对象里面
    • 比如,把一篇文章的所有访问者添加到对象里面。然后再对这个对象统计
    • 如果有元素进入了对象,就返回1,否则返回0
  • pfcount 对象1 对象2 。。。
    • 统计对象的游客数量,返回的就是游客数量,如果同时查看两个对象的游客数量,就是返回他们的游客数量之和
  • pfmerge 新对象 旧对象1 旧对象2 。 。。
    • 把一个或多个对象,合并到一个新对象里面

基数不大的时候,尽量不用,因为是大材小用。只能统计,不能查询

使用场景:

  1. 网站游客统计
  2. 网络浏览量统计
  3. ip数
  4. 在线用户
  5. 搜索不同词条的个数
  6. 文章的真实阅读次数

Redis可视化工具redis desktop manager

就像MySQL的navicat

先关防火墙

  1. 检查防火墙 firewall-cmd --state running就是运行
  2. 关闭防火墙 systemctl stop firewalld.service
  3. 因为redis默认关闭远程访问,还要修改redis
  4. 打开redis配置文件redis.conf
  5. 注释里面的一行命令bind 127.0.0.1 -::1
  6. 然后把里面的保护模式protected-mode 设置成 no。保存退出
  7. 重启redis服务,kill掉之前的redis服务

然后就可以了

在redis的桌面端,就可以看到16个数据库了

使用右键,单击任意一个库,就可以添加键值对了

redis的配置文件

在Linux里面的redis有一个配置文件redis.conf

参数:

  • timeout 0 设置客户端连接的超时时间,单位是秒,默认是0,表示不关闭,一直开启
  • tcp-keepalive 300 周期性检查客户端是否正常连接
  • daemonize yes 守护进程,yes是后台运行,no是前台开启
  • pidfile /var/run/redis_6379.pid 放置文件进程pid的文件路径
  • loglevel notice日志级别,默认是notice
  • logfile “” 配置log文件输出的地址,默认是输出到终端
  • databases 16 设置数据库的数目

还有很多配置文件,后面学到什么再说什么

redis功能

发布与订阅

  • 订阅
    • subscribe 主题名字
    • 当有发布者发布新消息的时候,所有的订阅者都可以接收到消息
  • 发布
    • publish 主题名称 内容
    • 这个是发布者使用的命令,发布者对主题名称,发布内容,然后所有的订阅者可以接收

慢查询

redis执行命令的过程:

  1. 客户端发送命令
  2. 在redis内部排队,等待
  3. redis执行命令
  4. redis发送命令到客户端

慢查询在第三布发生

客户端超时不一定是慢查询,但是慢查询是客户端超时的一个可能因素

慢查询日志存放在redis内存列表中

  • 慢查询日志

    是redis服务端在执行命令前后,计算该条命令的执行时间,一般是设定一个阈值,超过阈值,就记录下来这条命令

  • slowlog get [count] 获取慢查询的日志,后面可以指定慢查询命令的数量

    • 127.0.0.1:6379> SLOWLOG get 3   
      1) 	1) (integer) 0     慢查询的唯一标识
          2) (integer) 1640056567  时间戳
          3) (integer) 11780  执行时间,微秒为单位
          4) 1) "FLUSHALL"  实际执行的命令
          5) "127.0.0.1:43406"   ip地址
          6) ""
      
  • slowlog len 获取慢查询的长度 返回的就是慢查询的数量

  • config get slow*

    • 查看慢查询的参数,
      • slowlog-max-len 日志保存命令的数量
      • slowlog-log-slower-than 超时阈值,微秒为单位
  • 修改慢查询的参数

    • 去redis的配置文件redis.conf里面修改下面的两个命令,然后重启redis服务
      • slowlog-max-len number 直接在后面跟数值就行
      • slowlog-log-slower-than number
    • config set 命令,使用这种方式可以动态修改配置文件的内容

流水线pipeline

就是缓存命令,然后打包给服务器,让服务器一次性处理好返回来。

没有pipeline,就只能一次给服务器一条命令

课中,用的是java测试了jedis和pipeline的效率

redis数据安全

有一个持久化机制,用于故障恢复。因为redis缓存在内存中,每次开关机,都会清空内存,有的内容不想清空,只能写在硬盘里面

redis提供了两种持久化的方式:

  1. RDB
  2. AOF

RDB 按时间间隔,存储一次内存快照

是每隔多长时间,就对内存中的所有内容进行一次存储。

存储的文件是一个二进制压缩文件,RDB默认保存的文件名称是dump.rdb

这个默认名称在redisconf里面,可以修改

在文件里面的440行,叫dbfilename dump.rdb

在这个模块中,有rdb文件的保存路径,是dir ./ , 可以自己修改保存路径

这个内存快照是新的替换旧的,不会一直增长

  • RDB的触发条件,有三种

    • save 3600 1 意思是3600秒内,有一个key键发生改变,就执行一次rdb
    • save 300 100 是300秒内有100个键发生改变,触发一次rdb
    • save 60 10000
    • 在文件中,这些都是注释内容,需要在源文件里面修改内容,也就是增加命令save 60 5 ,60秒内,有5个key发生改变就记录rdb
  • 执行flushall 执行清空命令的时候,也会触发一次rdb

  • 手动触发rdb,有两种save和bgsave

    • save,会占用redis的单线程,就是会阻塞redis执行
    • bgsave 是不占用redis线程,异步执行,一般建议使用bgsave
  • 高级设置

    • stop-writes-on-bgsave-error
      默认值是yes。当Redis无法写入磁盘的话,直接关闭Redis的写操作。
    • rdbcompression
      默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
    • rdbchecksum
      默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
    • 恢复数据
      只需要将rdb文件放在Redis的启动目录,Redis启动时会自动加载dump.rdb并恢复数据。 只要有了rdb文件,就会自动加载并恢复。
  • 优势

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

    • 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
    • 因为是基于时间内键的修改次数,这个是不确定的。很不稳定

AOF

以日志的形式,记录所有往服务端写的操作

默认AOF不开启,RDB开启

开启AOF也要去修改配置文件

  • 设置Yes:修改默认的appendonly no,改为yes。。。修改完需要重启redis服务。

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

有一个好处,就是,如果执行的误删的操作,可以去aof文件里面,删除刚刚的删除操作,就可以恢复到误删之前的状态了

  • AOF文件的保存路径,同RDB的路径一致,如果AOF和RDB同时启动,Redis默认读取AOF的数据。

  • AOF同步频率设置,就是我们往AOF写的时候,也不是一次写一条,而且集中处理

    • 参数:
      1. appendfsync always
        始终同步,每次Redis的写入都会立刻记入日志,性能较差但数据完整性比较好。
      2. appendfsync everysec
        每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
      3. appendfsync no
        redis不主动进行同步,把同步时机交给操作系统。
    • 基本上都是使用第二个
  • 优势

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

    • 比起RDB占用更多的磁盘空间。
    • 恢复备份速度要慢。
    • 每次读写都同步的话,有一定的性能压力。
  • 不要仅仅使用RDB
    RDB数据快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候就得接受一旦redis进程宕机,那么会丢失最近5分钟的数据。

  • 也不要仅仅使用AOF

    1. 你通过AOF做冷备,没有RDB做冷备,来的恢复速度更快。
    2. RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug。
  • 综合使用AOF和RDB两种持久化机制

    • 用AOF来保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。
  • 修复损坏的AOF或者RDB

    • 使用redis-check-aof 可以修复AOF文件
    • 使用redis-check-rdb 可以修复RDB文件

redis事务

  • 数据库层面事务

    • 在数据库层面,事务是指一组操作,这些操作要么全都被成功执行,要么全都不执行。
  • 数据库事务的四大特性

    • A:Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
    • C:Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
    • I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
    • D:Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。
  • Redis事务

    • Redis事务是一组命令的集合,一个事务中的所有命令都将被序列化,按照一次性、顺序性、排他性的执行一系列的命令。

Redis事务三大特性

  1. 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;
  2. 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”。
  3. 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚;
  • Redis事务执行的三个阶段
    • 开启:以 MULTI 开始一个事务,就是创建一个事务队列;
    • 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面;
    • 执行:由 EXEC 命令触发事务;
  • Redis事务基本操作
    • Multi、Exec、discard
      事务从输入Multi命令开始,输入的命令都会依次压入命令缓冲队列中,并不会执行,直到输入Exec后,Redis会将之前的命令缓冲队列中的命令依次执行。组队过程中,可以通过discard来放弃组队。
  • 如果在事务队列里面有语法错误,整个事务直接失败
  • 如果没有语法错误,只是逻辑错误,运行时错误,即非语法错误,正确命令都会执行,错误命令返回错误。

集群

Redis 为了解决这个单一节点的问题,也会把数据复制多个副本部署到其他节点上进行复制,实现Redis的高可用,实现对数据的冗余备份从而保证数据和服务的高可用。

  • 什么是主从复制

    主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

  • 主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制环境搭建

  • 编写配置文件

新建redis6379.conf

include /usr/local/redis/redis.conf   # 复制redis的配置文件
pidfile /var/run/redis_6379.pid    # 新建pid号
port 6379    # 新建端口号
dbfilename dump6379.rdb   # 新建rdb文件

新建redis6380.conf

新建redis6381.conf

下面这两个把里面的数值替换一下就可以了

  • 启动三台redis服务器
./redis-server ../redis6379.conf
./redis-server ../redis6380.conf
./redis-server ../redis6381.conf

需要分别去三个服务器窗口去执行命令,也可以是一台服务器多开窗口,分别执行命令,模拟多台服务器

  • 查看三台主机运行情况 info replication
#分别在三个窗口里面运行命令
./redis-cli -p 6379    #  在新的窗口启动
127.0.0.1:6379> info replication  # 查看该窗口的redis配置信息
./redis-cli -p 6380
127.0.0.1:6380> info replication
./redis-cli -p 6381
127.0.0.1:6381> info replication
  • 给从库匹配主库 slaveof <ip> <port>

示例:给6380匹配6379主库。

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379

主从复制原理剖析

  • 主从复制可以分为3个阶段

    • 连接建立阶段(即准备阶段)
    • 数据同步阶段
    • 命令传播阶段
  • 主从同步策略
    主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

    就是同步的时候,有一个预处理阶段,也就是一个新的库接受正在使用的库的内容,需要先把这个库之前的命令全部移植过来,这个叫全量同步。然后随着我们使用主库,所使用的写命令,也会被从库拷贝过去

  • offset就是偏移量,我们在主从服务器里面查看信息的时候,都可以看到服务器的偏移量

  • 命令持续复制。

    当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

哨兵机制

  • Redis主从复制缺点

    当主机 Master 宕机以后,我们需要人工解决切换。一旦主节点宕机,写服务无法使用,就需要手动去切换,重新选取主节点,手动设置主从关系。

  • 主从切换技术
    当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。

  • 哨兵概述
    哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

  • 哨兵作用

    • 集群监控:负责监控redis master和slave进程是否正常工作
    • 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
    • 故障转移:如果master node挂掉了,会自动转移到slave node上
    • 配置中心:如果故障转移发生了,通知client客户端新的master地址

哨兵监控环境搭建

  • 新建sentinel-26379.conf文件
#端口
port 26379
#守护进程运行
daemonize yes
#日志文件
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 6379 2

参数:
sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是:该哨兵节点监控192.168.92.128:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。

然后再新建两个哨兵文件,也就是三个哨兵监督三个服务器

可以把上面的79,改成80和81

  • 哨兵节点的启动两种方式

    • redis-sentinel sentinel-26379.conf
    • redis-server sentinel-26379.conf–sentinel
  • 查看哨兵节点状态

​ 使用命令./redis-cli -p 26379 进入哨兵模式

​ 127.0.0.1:26379> info sentinel 输入命令,可以查看哨兵的信息

​ master0:name=mymaster,status状态=ok,address主节点=192.168.66.100:6379,slaves从节点数量=2,sentinels=3

哨兵工作原理详解

因为哨兵通过不断的向redis数据库发消息,拿返回值判断是否正常运行,如果不正常的时候,可能是哨兵自己出问题,也可能是服务器出问题,这时候就需要别的哨兵进行判断,如果哨兵判断的时候,有两种结果,就需要哨兵之间进行表决,比如半数哨兵都不能访问服务器,就直接切换服务器了。防止有哨兵投票两边队伍相等,因为哨兵的数量必须是奇数,防止两边相等

  • 选新数据库的方式
    • 方式:
      自己最先接到哪个服务器的sentinel的竞选通知就会把票投给它。
    • 剔除一些情况:
      1. 不在线的
      2. 响应慢的
      3. 与原来master断开时间久的
      4. 优先级原则
  • 一般是看从数据库里面,谁的偏移量最多,也就是谁的数据最多,就选谁

故障转移

我们之前配置好了三个哨兵和三个服务器数据库。

我们通过kill命令杀死主节点6379.然后再进入哨兵模式,查看信息。会发现master切换了。

会发现主节点还没有切换过来,因为哨兵发现主节点故障并转移,需要一段时间。

重启主节点,这个主节点的服务器也会变成从库

进入主节点的redis,查看信息,在第一行有一个role是角色,会变成slave,从节点

都是自动转移的

配置文件都会被改写,故障转移阶段,哨兵和主从节点的配置文件都会被改写

  • 结论
    哨兵系统中的主从节点,与普通的主从节点并没有什么区别,故障发现和转移是由哨兵来控制和完成的。
    哨兵节点本质上是redis节点。
    每个哨兵节点,只需要配置监控主节点,便可以自动发现其他的哨兵节点和从节点。
    在哨兵节点启动和故障转移阶段,各个节点的配置文件会被重写(config rewrite)。

Cluster模式

就是之前,为了解决主从库的切换,设计出了哨兵。因为哨兵投票的时候会有一段时间不能使用redis,所以又把哨兵也分成一个个独立的组织,也就是集群。哨兵投票的时候,就把主节点换成其他的哨兵集群。

Redis有三种集群模式

  • 主从模式
  • Sentinel模式
  • Cluster模式

哨兵模式的缺点

  1. 当master挂掉的时候,sentinel 会选举出来一个 master,选举的时候是没有办法去访问Redis的,会存在访问瞬断的情况;
  2. 哨兵模式,对外只有master节点可以写,slave节点只能用于读。尽管Redis单节点最多支持10W的QPS,但是在电商大促的时候,写数据的压力全部在master上。
  3. Redis的单节点内存不能设置过大,若数据过大在主从同步将会很慢;在节点启动的时候,时间特别长;

Redis集群是一个由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。

  • Redis集群的优点
    • Redis集群有多个master,可以减小访问瞬断问题的影响
    • Redis集群有多个master,可以提供更高的并发量
    • Redis集群可以分片存储,这样就可以存储更多的数据

Cluster模式搭建

Redis的集群搭建最少需要3个master节点,我们这里搭建3个master,每个下面挂一个slave节点,总共6个Redis节点;

redis问题

只针对超大型企业,拥有海量数据读写操作的问题

redis脑裂

由于网络卡顿,导致一个正常的数据库无法被访问,被redis集群判定为无效节点,投票出新节点之后,网络恢复,就会同时出现两个主节点

注意:
此时存在两个不同的master节点,就像一个大脑分裂成了两个。集群脑裂问题中,如果客户端还在基于原来的master节点继续写入数据,那么新的Master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的Master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。

  • 解决方案
    • redis.conf配置参数:
      • min-replicas-to-write 1
      • min-replicas-max-lag 5
    • 参数:
      • 第一个参数表示最少的slave节点为1个
      • 第二个参数表示数据复制和同步的延迟不能超过5秒

缓存预热

就是启动服务器的时候,MySQL是加载在硬盘的,MySQL会先加载出来,服务器会直接跟MySQL建立连接,redis就没法使用,MySQL大概率会直接挂掉。所以要让redis比MySQL先启动,并且存放一定的数据,通过数据分析,提前缓存一些信息。

  • 解决思路
  • 提前给redis中灌入部分数据,再提供服务
  • 如果数据量非常大,就不可能将所有数据都写入redis,因为数据量太大了,第一是因为耗费的时间太长了,第二根本redis容纳不下所有的数据需要根据当天的具体访问情况,实时统计出访问频率较高的热数据
  • 然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热

缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

就是对手恶意攻击数据库,大量查询访问未知或违法元素,造成服务器过载。为了避免这种清空,要么单独判断,要么使用布隆过滤器

解决方案

  1. 对空值缓存:如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果缓存,
    设置空结果的过期时间会很短,最长不超过5分钟。
  2. 布隆过滤器:如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起
    来,然后通过比较确定
  • 布隆过滤器
    • 什么是布隆过滤器
      布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
  • 本质是一个二进制数组,通过hash算法给每一个变量提前写一个地址,比如西瓜的偏移量是3,9,11。在布隆数组里面对应的位置就是1,没有的位置是0.然后允许不同的商品偏移量交错,也可能相同。
  • 在判断商品的时候,如果这个商品的偏移量,在布隆数组里面有值,那么这个商品有概率存在。如果这个商品的hash偏移量不存在,这个商品一定不存在 ,一定程度上避免了恶意攻击
  • 结论:布隆说不存在一定不存在,布隆说存在你要小心了,它有可能不存在。

缓存击穿

某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

因为写操作,可能直接作用在数据库上,由于key过期需要新建key,大量访问这个key,就可能大量新建key。给服务器增加负担

解决方案

  1. 互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,其他线程直接查询缓存。
  2. 热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。

缓存雪崩

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

  • 解决方案
    • 过期时间打散:既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。
    • 热点数据不过期:该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。
    • 加互斥锁: 该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。

Redis开发规范

  • key设计技巧
    1、把表名转换为key前缀,如 tag:
    2、把第二段放置用于区分key的字段,对应msyql中主键的列名,如 user_id
    3、第三段放置主键值,如 2,3,4
    4、第四段写存储的列名

在redis里面,设计键的名称时候,使用:冒号,可以自动生成多级目录。使用下划线就没用该功能

  • 示例
# 表名 主键 主键值 存储列名字
set user:user_id:1:name baizhan     这里面的每一个冒号,都是对应之前的下划线
set user:user_id:1:age 20
#查询这个用户
keys user:user_id:9*
  • value设计
    拒绝bigkey,防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000

  • 命令使用
    1、禁用命令
    禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。
    2、合理使用select
    redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。
    3、使用批量操作提高效率
    原生命令:例如mget、mset。
    非原生命令:可以使用pipeline提高效率。
    但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
    4、不建议过多使用Redis事务功能
    Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上。

  • 尽可能避免一些全局查询修改操作

数据一致性

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。

  • 三种更新策略
  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

先更新数据库,再更新缓存

这套方案,大家是普遍反对的。为什么呢?
线程安全角度,同时有请求A和请求B进行更新操作,那么会出现
(1)线程A更新了数据库
(2)线程B更新了数据库
(3)线程B更新了缓存
(4)线程A更新了缓存
这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

先删缓存,再更新数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:
(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
注意:该数据永远都是脏数据。

先更新数据库,再延时删缓存

这种情况存在并发问题吗?
(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存
发生这种情况的概率又有多少?
发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的,因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。


http://www.kler.cn/a/593785.html

相关文章:

  • 蓝桥杯备考----小贪心+分类讨论问题---Popsicle
  • 版本控制器Git ,Gitee如何连接Linux Gitee和Github区别
  • 电路基础【3】:三极管基础:三极管开关电路与三极管放大电路(一篇讲明白!建议收藏!)
  • 基于springboot的教务系统(源码+lw+部署文档+讲解),源码可白嫖!
  • 美国丹佛启动“DenverTrack“计划:首批发放450个AirTag追踪器应对汽车盗窃潮
  • 解释 一下什么是 React 的 useRef Hook
  • Pytorch使用手册—自定义函数的双重反向传播与自定义函数融合卷积和批归一化(专题五十二)
  • 【鸿蒙开发】Hi3861学习笔记- OLED示例
  • 【“缘起”:万物依条件而生】
  • 【原创】python授权加密
  • 12、STL中的multiset使用方法
  • Excel 小黑第18套
  • C++11新增内容详解(三)
  • 软件测试--黑马程序员
  • ABC395题解
  • 浅谈canal实例 在docker里面安装canal镜像 Canal监听MySQL数据库变更并同步更新Redis和Elasticsearch 示例
  • 共筑智慧城市新生态!YashanDB与荣科科技完成兼容互认证
  • Hive高频SQL及典型应用场景总结
  • 【Pandas】pandas Series plot
  • STC89C52单片机学习——第28节: [12-2] AT24C02数据存储秒表(定时器扫描按键数码管)