前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis升级

Redis升级

作者头像
晚上没宵夜
发布2020-04-13 12:00:43
9960
发布2020-04-13 12:00:43
举报

1. 数据删除策略

惰性删除+定期删除(默认)

定期删除:默认是每隔 100ms 就轮询各个库随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。每隔100ms就遍历所有的设置过期时间的 key 的话,是个损耗。

惰性删除:定期删除会导致很多过期 key 到了时间并没有被删除掉。除非系统去查询才会删除。如果靠定期删除,和没有走惰性删除的话会导致一大部分过期数据没有删除,这时候就出现了内存淘汰机制

2. 内存淘汰机制

在数据进入内存的时候发现内存不够了,就采用内存淘汰机制,不一定淘汰过期的

其配置有:

  • maxmemory:最大可用内存
  • maxmemory-samples:每次选取删除数据的个数
  • maxmemory-policy:删除策略
    • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)最近最久未使用
    • volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)最近最少使用
    • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)将要过期的数据淘汰
    • volatile-random:从已设置过期时间的数据集(server.db[i].expires)随机淘汰
    • allkeys-lru:在全库数据中(server.db[i].dict),最近最久未使用(这个是最常用的)
    • allkeys-lfu:在全库数据中(server.db[i].dict),最近最少使用
    • allkeys-random:在全库数据中(server.db[i].dict)随机淘汰
    • no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用

3. 限制登录次数功能

  • 判断用户是否被限制登录
    • 有:做相应的提示
    • 没有
      • 登录成功:清除失败错误次数
      • 登录不成功(查询key是否存在,即是否第一次 错误)
        • 第一次错误:设次数为1,user:loginCount:fail:用户名进行赋值,同时设置失效期
        • 不是第一次
          • (判断是否4次,是的话这次加1等于5,限制1小时),user:loginCount:fail:用户名+1
          • 小于第四次,失败次数加1
代码语言:javascript
复制
// 这里笔者用了不规范的返回值,返回数值大于10表示被限制登录
public long login(String username, String password) {

    // 先判断是否被限制,
    if (jedis.exists("user:loginCount:limit:" + username)) {
        return jedis.ttl("user:loginCount:limit:" + username);
    }

    // 然后查询数据库返回结果,这里模拟查询设数据库,密码输入错误
    if (!username.equals(password)) {
        long count = 0;
        // 第一次错误,键不存在,设置过期时间,10秒内可以错误5次
        if (!jedis.exists("user:loginCount:fail:" + username)) {
            jedis.setex("user:loginCount:fail:" + username, 10, "1");
            return 1;
        } else {
            count = jedis.incr("user:loginCount:fail:" + username);
            if (count == 5) {
                jedis.setex("user:loginCount:limit:" + username, 10, ""); // 设置登录限制时间
                jedis.del("user:loginCount:fail:" + username);  // 删除登录次数,因为被登录限制
            }
            return count; // 返回已尝试次数
        }
    }
    // 密码正确,清除错误次数
    jedis.del("user:loginCount:fail:" + username);
    return 0L; // 表示密码正确
}

4. 消息订阅

代码语言:javascript
复制
subscribe channel[channel]  订阅频道
psubscribe pattern[pattern]  订阅匹配的频道

publish channel message  将消息发送到指定频道

unsubscribe [channel | channel]  退订频道
punsubscribe [pattern | pattern]  退订匹配的频道

应用场景:

  • 构建实时消息系统
    • 普通的即时聊天,群聊
    • 粉丝订阅之后,发布新文章的消息推送,公众号模式

5. 缓存雪崩

Redis过期是惰性删除+定期删除,如果缓存数据设置的过期时间相同,那么当这些数据全部过期时,就会在这段时间全部请求走数据库中。简单就是Redis某段时间,或直接挂了,请求全走数据库,那么导致数据库支持不住而宕机。

解决方法:

  • 给过期时间加上一个随机值(数据分类过期),减少大幅度同一时间过期问题
  • 事前:可以用集群或高可用来尽量避免
  • 事发中:使用本地缓存+限流(比如验证码)
  • 事发后:redis的持久化,从硬盘上恢复数据

6. 缓存穿透

大量查询不存在的数据,导致每次返回空,Redis不起作用,相当于直接访问数据库。

解决方法:

  • 请求参数的校验,使之不能进入到redis,更不要说数据库了
  • 查询不存在数据时,也将这个数据放入Redis,下次访问可以从里面获取,当然要设置过期时间
  • 布隆过滤器、限流算法、令牌桶

7. 缓存与数据库的读写一致

读:

  • 如果查询数据缓存里有,直接返回
  • 缓存里没有,去数据库查询,将查询结果放入缓存,并返回给客户端

对于更新时:会导致缓存数据和数据库不一致,可以先修改数据库,再修改缓存。或者先修改缓存,再修改数据库,重点在于我们要是这两个操作突显原子性,这样数据才不会出错

操作缓存:可以选择更新和删除,但一般采取删除操作。因为删除相对比更新更直接简单,如果每次更新数据库都要更新缓存,如果频繁更新的话,会频繁修改一定程度损耗性能,不如直接删除,再次读取时缓存没有就到数据库查找

先更新数据库再删除缓存:也有概率出错但很低,比如缓存失效,线程A查询数据库得到旧值,期间线程B将新值写入数据库,线程B删除缓存,然后线程A才将旧址写入缓存。删除缓存失败策略是,不断重试删除,直到成功。

先删除缓存,再更新数据库:如果原子性被破坏了,第一步成功删除缓存,第二步更新数据库失败,那么数据库数据是一致的,如果第一步删除缓存失败了,可以直接返回错误,数据库数据和缓存还是一致。

但是:线程A删除了缓存,期间线程B查询会走数据库得到旧值,并把旧值写入缓存,然后线程A才将新值写入数据库,导致数据不一致,解决方法:将删除缓存,修改数据库,读取缓存等操作挤压到队列里,实现串行化。

二者对比:

前者:高并发下表现优异,原子性破坏时不好

后者:高并发下串行,原子性破坏时优异

8. 持久化

Redis是基于内存的,万一遇到宕机那么内存中的数据则会丢失,而持久化则是将内存中的数据保存到硬盘防止丢失。Redis支持两种方式的持久化方式:RDB、AOF

1. RDB

创建内存中数据的二进制快照来实现持久化,可对快照备份或把快照复制到其他服务器使之成为服务器副本,还可以将快照留在原地以便重启服务器加载使用,默认持久化文件为dump.rdb

save命令执行一次就保存一次,若数据量过大,加入单线程任务执行会阻塞任务,所以不建议使用

bgsave命令后台运行,fork子进程来进行持久化,成功后记录到日志中

自动执行持久化:需在redis.conf中配置,执行多少次非查询操作就保存

  • save 900 1
  • save 300 10
  • save 60 10000

优点:

  • 紧凑压缩的二进制文件,存储效率高
  • 存储的是某个时间点的快照,适合数据备份,全量复制
  • 恢复数据速度比AOF快很多
  • 应用:每隔一段时间执行bgsave备份,用于灾难恢复

缺点:

  • 不能实时持久化,间隔时间段的数据可能丢失
  • fork子进程,内存额外消耗
  • 数据量大时,持久化速度慢,全部数据持久化

2. AOF

将除查询外的命令追加保存到AOF文件中,重启时重新执行AOF文件中的命令达到恢复数据的目的,是主流的持久化方式,默认没有开启,持久化文件为appendonly.aof

持久化数据的三种策略(写命令刷新到aof命令缓冲区)

  • always 每次
  • everysec 每秒
  • no 系统控制

配置文件

  • appendonly yes|no
  • appendfsync always|everysec|no

AOF重写机制

将Redis进程内的数据转化为写命令同步到新的AOF文件的过程,即将对同一个数据的若干命令的执行结果合并成一条操作指令(忽略超时数据,忽略无效指令删除等,合并重复指令),可降低文件大小,提高持久化与恢复效率,其也有重写缓冲区,下面是重写命令:

  • bgrewriteaof 手动重写
  • auto-aof-rewrite-min-size size 配置自动重写(当aof缓存了多少)
  • auto-aof-rewrite-percentage percentage 配置自动重写(%)

参考黑马教程

优点:

  • AOF持久化的实时性更好
  • 持久化速度快,追加命令

缺点:

  • 因为记录命令,持久化文件大
  • 恢复数据慢,要执行命令

9. 事务

Redis 通过 MULTI、EXEC命令来实现事务(transaction)功能,其事务实质是将多个命令打包后一次性地按顺序执行,期间不会执行其他客户端的命令请求,简单来说是命令串行化执行功能,没有回滚功能。关系型数据库用 ACID 检验事务功能的可靠性和安全性。而 Redis 中,事务总是具有原子性、一致性、隔离性,当持久化时,事务也具有持久性

代码语言:javascript
复制
MULTI:开启事务,创建队列,命令来了加入队列
EXEC:执行事务,队列中执行命令,完后销毁队列
DISCARD:取消事务,销毁队列

流程:

  • 开始事务
  • 命令入队,命令不会立即执行
  • 执行事务,按上面入队顺序执行

举例转账:multi开始事务,exec执行事务

代码语言:javascript
复制
set account:a 100
set account:b 100

multi

get account:a
"QUEUED"
get account:b
"QUEUED"
decrby account:a 10
"QUEUED"
incrby account:b 10
"QUEUED"

exec
 1)  "100"
 2)  "100"
 3)  "90"
 4)  "110"

Redis的事务是没有回滚功能的,在进行事务的时候,只有报错的命令不会执行(例外:语法错误整个队列都不会执行,类型错误会执行),其他命令都会执行。只是单纯的执行事务的时候不会有其他命令加塞

场景:动物园给熊猫投喂竹子,这里有很多个饲养员,只要其中一个投喂了,其他饲养员就不用再投喂,使用watch解决

代码语言:javascript
复制
WATCH:执行事务前,监视Key是否被修改,若有则取消事务,返回nil(针对同时修改用处大)
UNWATCH:取消监视
代码语言:javascript
复制
watch eat
// 中间可以执行其他命令,必须在开启事务前watch
multi
set panda 1
exec

10. 主从复制

代码语言:javascript
复制
repl-backlog-size 设置指令缓冲区
slave-server-stale-data yes|no  slave关闭写功能
  • 建立连接
代码语言:javascript
复制
方式1:
客户端发送 slaveof <masterip> <masterport>
		  auth <password>

方式2:
启动式服务器参数 redis-server -slavveof <masterip> <masterport>

方式3
slave配置文件:slaveof <masterip> <masterport>
masterauth 123456

主从复制低版本不能复制高版本的数据,笔者在这里花了挺久时间才找出问题所在

  • 数据同步
  • 命令传播
  • 心跳机制

进入命令传播阶段时,master和slave的信息交换使用心跳机制维护,实现双方连接保持在线

主从复制的作用

  • master写,slave读,提高读写负载能力
  • 负载均衡,基于主从结构,配合读写分离
  • 故障恢复,当master故障时,由slave提供服务,实现快速恢复
  • 数据冗余,实现数据热备份
  • 高可用基础

11. 哨兵模式Sentinel(主备切换)

哨兵是一个分布式系统,也是一台redis服务器,对于主从结构中的每台服务器进行监控,出现故障时投票机制选择新的master并将所有slave连接到新的master,演示搭建三个哨兵和1主2从

sentinel.conf的配置文件

代码语言:javascript
复制
monitor mymaster 127.0.0.1 6379 2  // 监听主服务器,自定义名字,后面2表示多少个哨兵认为宕机才有效
down-after-millisecoinds mymaster 30000  // 多久才认为宕机
parallel-syncs mymaster 1  // 命令传播
failover-timeout mymaster 180000  // 复制超时时间

先启动1主2从,再启动哨兵

代码语言:javascript
复制
redis-sentinel sentinel-26379.conf
redis-sentinel sentinel-26380.conf
redis-sentinel sentinel-26381.conf

启动哨兵后,每台服务器的配置都会有对应的修改

哨兵模式的流程:

  • 1.监控阶段
    • 获取各sentinel的状态(是否在线)
    • 获取master的状态
      • master属性
        • runId
        • role:master
      • 各个slave的详细信息
    • 获取所有slave的状态(根据master中的slave信息)
      • slave属性
        • runId
        • role:slave
        • master_host、master_port
        • offset
  • 2.通知阶段
    • 不停地用ping去测试
  • 3.故障转移
    • 发现问题
    • 竞选负责人
    • 优选新master
      • 在线的
      • 响应快的
      • 与原master断开时间最短的
      • 优先原则:优先级、offset、runId
    • 新master上线,其他slave切换master,原master作为slave故障恢复后连接

12. 集群

分散单台服务器的访问压力,即负载均衡

其底层存储原理:

  • 将key进行两次算法运算得key应该保存的位置(CRC16(key) % 16384)
  • 将所有Redis服务器的总存储空间计划切割成16384份,每台主机保存一部分
  • 加Redis服务器的话,原本服务器将槽分给新的服务器、删除服务器则相反
  • 集群内部通讯:记录各服务器槽范围,一次命中OK,否则服务器查询通讯录让请求去对应槽服务器(最多2次命中)
  • 内部通讯这样就不用虚拟IP了

配置3主3从(官方自带,每个服务器都要配置)

代码语言:javascript
复制
cluster-enabled yes  // 开启集群节点
cluster-config-file nodes-6379 // 集群配置文件
cluster-node-timeout 10000  // 宕机时间

src下有redis-trib.rb(需要Ruby、Gem支持)

代码语言:javascript
复制
./redis-trib.rb create --replicas 1 // 其中1表示1主拖1从
127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381
127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

客户端启动

代码语言:javascript
复制
redis-cli -c
// 不然key和服务器没有对应上会报错,让你去连对应的服务器。加了配置会帮你重定向

故障处理:

  • 从服务器下线,各个节点能收到通知,对应master节点会标记一下宕机从服务器
  • 主服务器下线,对应从服务器重试,失败就执行上面的主从切换,切换的从顶替了主集群。原主上线变成slave

13. 并发竞争Key问题

所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同

推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)


参考

https://www.bilibili.com/video/BV1CJ411m7Gc?p=101

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 数据删除策略
  • 2. 内存淘汰机制
  • 3. 限制登录次数功能
  • 4. 消息订阅
  • 5. 缓存雪崩
  • 6. 缓存穿透
  • 7. 缓存与数据库的读写一致
  • 8. 持久化
    • 1. RDB
      • 2. AOF
      • 9. 事务
      • 10. 主从复制
      • 11. 哨兵模式Sentinel(主备切换)
      • 12. 集群
      • 13. 并发竞争Key问题
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档