天天看点

Redis持久化实现

文章目录

      • 一. Redis
          • 1.1 语法回顾
          • 1.2 事务
          • 1.3 持久化 (重点)
            • 1.3.1 RDB 快照存储
            • 1.3.1 AOF 只追加文件
            • 1.3.3 如何选择
          • 1.4 分布式设计
            • 1.4.1 哨兵 (重点)
            • 1.4.2 集群

一. Redis

1.1 语法回顾
  • String
    • 记录字符串/整数/浮点数
    • 命令
      • set 添加/修改数据
      • get 获取数据
      • mset 添加多个数据
      • mget 获取多个数据
      • incr 计数加1
      • decr 计数减1
      • incrby 计数加n
  • 键命令
    • 适用于所有的类型
    • 命令
      • del 删除数据
      • exists 判断数据是否存在
      • expire 设置过期时间
      • ttl 获取剩余时间
      • keys 查询满足条件的键
  • hash
    • 类似

      字典

      的结构
    • 命令
      • hset 添加字段
      • hget 获取字段
      • hmset 添加多个字段
      • hmget 获取多个字段
      • hdel 删除字段
  • list
    • 是一个

      双向链表

    • 命令
      • lpush 从左侧追加元素
      • lrange 从左侧遍历元素
      • rpush 从右侧追加元素
      • lset 从左侧修改元素
      • lpop 从左侧删除元素
      • rpop 从右侧删除元素
      • ltrim 裁切列表
  • zset
    • 有序

      集合, 按照分数(score)进行排序
    • 命令
      • zadd 添加/修改元素
      • zrange 遍历元素(按分数从小到大)
      • zrevrange 反向遍历元素(从大到小)
      • zrangebyscore 遍历指定分数范围的元素
      • zscore 查询元素的分数
      • zrem 删除元素
      • zincrby 元素的分数计数加n
  • set
    • 无序

      集合 无序+去重
    • 命令
      • sadd 添加元素
      • smembers 遍历元素
      • sismember 判断是否包含
      • srem 删除元素
  • redis-py
    • python中独立的redis包
    • 安装

      pip install redis

Redis持久化实现
1.2 事务
  • 语法
    • MULTI
      • 开启事务, 后续的命令会被加入到同一个事务中
      • 事务中的操作会发给服务端, 但是不会立即执行, 而是放到了该事务的对应的一个队列中, 服务端返回QUEUED
    • EXEC
      • 执行EXEC后, 事务中的命令才会被执行
      • 事务中的命令出现错误时,

        不会回滚也不会停止事务

        , 而是继续执行
    • DISCARD
      • 取消事务, 事务队列会清空, 客户端退出事务状态
  • ACID
    • 原子性
      • 不支持
      • 不会回滚并且继续执行
    • 隔离性
      • 支持
      • 事务中命令顺序执行, 并且不会被其他客户端打断 (先EXEC的先执行)
    • 持久性
      • 不支持, redis数据易丢失
    • 一致性
      • 不支持
      • 强一致性要求 通过乐观锁(watch)来实现
Redis持久化实现
  • WATCH
    • redis实现的乐观锁
    • 机制
      • 事务开启前, 设置对数据的监听, EXEC时, 如果发现数据发生过修改, 事务会自动取消(DISCARD)
      • 事务EXEC后, 无论成败, 监听会被移除
# 解决更新丢失问题
WATCH mykey  # 监视mykey的值
GET mykey  # 读取数据, 假设需要进行*2操作, 再将结果重新赋值 (假设取出5)
MULTI  # 开启事务
SET mykey 10
EXEC  # 如果mykey的值在执行exec之前发生过改变, 则该事务会取消(客户端可以在发生碰撞后不断重试)
           
Redis持久化实现
如果只是加减操作, 建议使用

incr类

的操作, 单一操作具有原子性
  • 流水线
Redis持久化实现
1.3 持久化 (重点)

1.3.1 RDB 快照存储

  • 内存中的所有数据

    完整的保存到硬盘中
  • 机制
    • fork出一个

      子进程

      ,专门进行数据持久化, 将内存中所有数据保存到单个rdb文件中(默认为dump.rdb)
    • redis重启后, 会加载rdb文件中的数据到内存中
  • 触发方式
    • 配置中设置

      自动持久化策略

    • SAVE

      |

      BGSAVE

      |

      SHUTDOWN

      (前提是设置了自动持久化策略)
  • 相关配置
save 60 1000  # 多久执行一次自动快照操作 60秒内如果更新了1000次, 则持久化一次
  stop-writes-on-bgsave-error no  # 创建快照失败后,是否继续执行写命令
  rdbcompression yes  # 是否对快照文件进行压缩
  dbfilename dump.rdb  # 如何命名快照文件
  dir ./ # 快照文件保存的位置

  save   # 关闭RDB机制
           
  • 优缺点
    • 优点
      • 方便数据备份

        : 由于保存到

        单独的文件

        中, 易于数据备份 (可以使用定时任务, 定时将文件发送给数据备份中心)
      • 写时复制

        : 子进程单独完成持久化操作, 父进程不参与IO操作, 最大化redis性能
      • 恢复大量数据时, 速度优于 AOF
    • 缺点
      • 不是实时保存数据

        , 如果redis意外停止工作(如电源断电等), 则可能会丢失一段时间的数据
      • 数据量大时, fork进程会比较慢, 持久化时使redis响应速度变慢

1.3.1 AOF 只追加文件

  • Append-only file 只追加文件
    • 只追加

      而 不是全部重新写入
    • 追加命令

      , 而不是数据
  • 机制
    • 主线程将

      写命令

      追加到aof_buf(缓冲区)中, 根据使用的策略不同,

      子线程

      将缓存区的命令写入到aof文件中 (不使用子进程)
    • 当redis重启时, 会重新执行aof文件中的命令来恢复数据
      • 如果同时开启了 RDB, 则优先使用 AOF
  • 文件修复
    • 如果AOF出错 (磁盘满了/写入中途宕机等), 则redis重启时会拒绝使用该AOF文件
    • 修复步骤
      • 首先备份AOF文件
      • 使用redis-check-aof工具进行修复 (一般会删除末尾无法恢复的命令)
      • 重启redis服务器, 自动载入修复后的AOF文件, 进行数据恢复
    $ redis-check-aof –fix
    # 可选操作: 使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
               
  • 文件重写/压缩
    • AOF 提供了重写/压缩机制(优化命令), 以避免AOF文件过大
    • fork子进程来完成 AOF 重写
  • 相关配置
appendonly no  # 是否开启AOF机制
appendfsync everysec  # 多久将写入的内容同步到硬盘 每秒一次
no-appendfsync-on-rewirete no  # 重写aof文件时是否执行同步操作
auto-aof-rewrite-percentage 100  # 多久执行一次aof重写, 当aof文件的体积比上一次重写后的aof文件大了一倍时
auto-aof-rewrite-min-size 64mb  # 多久执行一次aof重写,当aof文件体积大于64mb时

appendfilename appendonly.aof  # aof文件名
dir ./  # aof文件保存的位置(和rdb文件共享该配置)
           
  • 优缺点
    • 优点

      -

      更可靠

      默认每秒同步一次操作, 最多丢失一秒数据

      - 提供了三种策略, 还可以不同步/每次写同步

      - 可以进行`文件重写`, 以避免AOF文件过大
                 
    • 缺点

      - 相同数据集, AOF文件比RDB

      体积大

      ,

      恢复速度慢

      - 除非是不同步情况, 否则普遍要比RDB

      速度慢

1.3.3 如何选择

  • 对于

    更新频繁, 一致性要求不是非常高的数据

    可以选择使用redis进行持久化存储
  • RDB or AOF
    • 数据安全性要求高, 都打开
    • 可以接受短时间的数据丢失, 只使用 RDB
    • 即使使用 AOF, 最好也开启 RDB, 因为便于备份并且回复速度快, bug更少
  • 项目中的应用
    • 使用redis进行一部分数据的持久化存储
      • 用户的阅读历史
    • 两种持久化机制都开启了
1.4 分布式设计

1.4.1 哨兵 (重点)

  • 作用
    • 监控redis服务器的运行状态, 可以进行

      自动故障转移

      (failover), 实现高可用
    • 数据库主从

      配合使用的机制
  • 特点
    • 独立的进程, 每台redis服务器应该至少配置一个哨兵程序
    • 监控redis主服务器的运行状态
    • 出现故障后可以向管理员/其他程序发出通知
    • 针对故障,可以进行自动转移, 并向客户端提供新的访问地址
  • 流言协议

    - 当某个哨兵程序 发现监视的主服务器下线后(心跳检测), 会向监听该服务器的其他哨兵询问, 是否确认主服务器下线, 当 确认的哨兵数量 达到要求(配置文件中设置)后, 会确认主服务器下线(客观下线), 然后进入投票环节

  • 投票协议

    - 当确认主服务器客观下线后, 哨兵会通过 投票的方式 来授权其中一个哨兵主导故障转移处理

    - 只有在 大多数哨兵都参加投票 的前提下, 才会进行授权, 比如有5个哨兵, 则需要至少3个哨兵投票才可能授权

    - 目的是避免出现错误的故障迁移

  • 建议最低配置

    - 至少在3台服务器上分别启动至少一个哨兵

    - 如果只有一台, 则服务器宕机后, 将无法进行故障迁移

    - 如果只有两台, 一旦一个哨兵挂掉了, 则投票会失败

  • 相关配置 (sentinel.conf)
bind 127.0.0.1  # 哨兵绑定的ip
port 26381  # 哨兵监听的端口号, redis客户端需要访问哨兵的ip和端口号
sentinel monitor mymaster 127.0.0.1 6380 2  # 设置哨兵  (主数据库别名 主数据库ip 主数据库端口 确认下线的最小哨兵数量)

sentinel down-after-milliseconds mymaster 60000  # 服务器断线超时时长
sentinel failover-timeout mymaster 180000  # 故障转移的超时时间
sentinel parallel-syncs mymaster 1  # 执行故障转移时,最多几个从数据库可以同步主数据库数据(数量少会增加完成转移的时长;数量多可能会影响主数据库的数据查询)

min-slaves-to-write 2  # 可执行故障转移的从数据库的最小数量(低于当数量时, 主数据库将禁止写入操作)
min-slaves-max-lag 10  # 从数据库的最大响应时长 
# 以上两条连起来: 当至少有2个从数据库可以进行复制并且响应延迟都在10秒之内时, 主数据库才允许写操作	
           
  • 启动哨兵
redis-sentinel sentinel.conf
           
Redis持久化实现
mysql官方没有提供故障转移方案, 需要用第三方的服务, 如 keepalive等

1.4.2 集群

  • 多个节点共同保存数据
  • 作用
    • 拓展存储空间
    • 提高吞吐量, 提高写能力
  • 和单机的不同点
    • 不再区分数据库, 只有0号库, 单机默认0-15
    • 不支持事务/管道/多值操作
    • 集群默认就集成了哨兵, 至少三主三从, 自动故障转移
  • 安装

    pip install redis-cluster

Redis持久化实现
  • 项目中的使用
    • 主从配置了哨兵和持久化机制, 用于保存用户的阅读历史
    • 集群负责实现缓存设计